Terrarium User Wiki



/////////////////////////////////////////////////////////////////////////////// // // // GreenWolf_07d by Tommy Ringstad (ringstad@msn.com) // // // // This code, with the exception of this paragraph, is exactly as it was // // released. It still has several issues, most of which are documented. // // Any feedback, constructive criticism, or ideas will be greatly // // appreciated and well recieved. // // // // And for the record, I am not a programmer, so take it easy on me. // // // ///////////////////////////////////////////////////////////////////////////////

using System; using System.Drawing; using System.Collections; using System.IO;

// Every organism assembly must have all the attributes below defined on it. [assembly: AuthorInformation("Tommy Ringstad", "ringstad@msn.com")] // Author's credentials. [assembly: OrganismClass("GreenWolf_07d")] // This organism's name.

[CarnivoreAttribute(true)] // I'm a cold-blooded killing (and eating) machine. [MatureSize(32)] // 32 is the optimal size for larger creatures / cellradius.

// Point Based Attributes... 100 points to distribute [MaximumEnergyPoints(10)] // Need to survive between meals, but keep low to grow and reproduce fast. [EatingSpeedPoints(7)] // Bigger numbers means bigger bites, to get our energy over 80%. [AttackDamagePoints(28)] // In case it doesn't want to be eaten. [DefendDamagePoints(0)] // In case it wants to eat us. [MaximumSpeedPoints(15)] // Have to be fast if you want to eat. [CamouflagePoints(0)] // Hide? Nevah! (besides, it's easily defeated) [EyesightPoints(40)] // Need to see dinner before dinner sees us, or it won't be dinner.

// The following attributes are optional. [AnimalSkin(AnimalSkinFamilyEnum.Spider, "Wolf")] // Hey, somebody make me a cool Wolf skin. [MarkingColor(KnownColor.Green)] // To set apart from same-skinned organisms.

// Organism Classes must be serializable public class GreenWolf_07d : Animal { ArrayList creatures = new ArrayList(); // A list of all creatures within view. ArrayList animals = new ArrayList(); // A list of animals within view. ArrayList others = new ArrayList(); // A list of everything not an animal. ArrayList live = new ArrayList(); // A list of live animals. ArrayList dead = new ArrayList(); // A list of dead animals. ArrayList threats = new ArrayList(); // A list of stronger animals that might attack us. AnimalState predator = null; // Current potential enemy. AnimalState prey = null; // Current potential dinner. Point destination = new Point(); // Where we ultimately want to get to. ArrayList wayPoints = new ArrayList(); // Array of waypoints to destination. int afraid = 0; // Gotta know when to hold em, know when to fold em.

// Unused hooks are left commented so that I can refer to // the proper order of events in the game. protected override void Initialize() { Load += new LoadEventHandler(LoadEvent); MoveCompleted += new MoveCompletedEventHandler(MoveCompletedEvent); // AttackCompleted += new AttackCompletedEventHandler(AttackCompletedEvent); // EatCompleted += new EatCompletedEventHandler(EatCompletedEvent); Teleported += new TeleportedEventHandler(TeleportedEvent); // ReproduceCompleted += new ReproduceCompletedEventHandler(ReproduceCompletedEvent); // Born += new BornEventHandler(BornEvent); // DefendCompleted += new DefendCompletedEventHandler(DefendCompletedEvent); // Attacked += new AttackedEventHandler(AttackedEvent); Idle += new IdleEventHandler(IdleEvent); }

// First event for an organism. Called every turn. void LoadEvent(object sender, LoadEventArgs e) { // Comedy Gold - written backwards (bottom to top) this.WriteTrace("----------------------------------"); this.WriteTrace("- "); this.WriteTrace("- Contact : ringstad@msn.com "); this.WriteTrace("- "); this.WriteTrace("- This Space for Rent! (Cheap!) "); this.WriteTrace("- "); this.WriteTrace("----------------------------------");

return; }

// Fires whenever we arrived at our destination or get blocked. void MoveCompletedEvent(object sender, MoveCompletedEventArgs e) {

// Determine why we stopped. if(e.Reason == ReasonForStop.Blocked) {

// Determine what exactly is in our way. if(e.BlockingOrganism is AnimalState) {

// It's a living animal, let's see if it's edible AnimalState blockingAnimal = (AnimalState) e.BlockingOrganism; if(blockingAnimal.IsAlive) { if(IsMySpecies(e.BlockingOrganism)) {

// If we get blocked by one of our own, we'll try to get around him. if (FindPath(this.Position, destination, 3)) { BeginMoving(new MovementVector((Point) wayPoints[0], BestSpeed())); } else { RandomizeDestination(); BeginMoving(new MovementVector(destination, BestSpeed())); } } else {

// This should only happen if we have targeted a corpse // on the other side of a living creature, which we either // did not see, or did not consider to be an obstacle or a // threat. So we'll just have to deal with it. BeginDefending(blockingAnimal); BeginAttacking(blockingAnimal); } }

// Theoretically, this shouldn't happen, but we have to account for it. // We ran into a plant (or something else neither animal nor plant). } else { // Seems to be some sort of problem here, occasionally throws Exception // errors for destination being null, and speed being greater than 0, which // should not be possible.. needs more research. I hate using try/catch, // but it's the only interim solution try { if (FindPath(this.Position, destination, 3)) { BeginMoving(new MovementVector((Point) wayPoints[0], BestSpeed())); } else { RandomizeDestination(); BeginMoving(new MovementVector(destination, BestSpeed())); } } catch (Exception ex) { // Dunno what to do really. Hope it figures something out in Idle. this.WriteTrace(ex); } }

// If we've stopped of our own accord, we shouldn't need to do anything at all // since we'll just re-evaluate anyhow, but there are a few rare cases that we // need to deal with this, so we do it anyhow. } else {

// See if we've reached our final destination, or just a waypoint. if (wayPoints.Count > 1) {

// Update our list of waypoints, and begin moving to the next on the list. wayPoints.RemoveAt(0);

// Seems to be some sort of problem here, occasionally throws Exception // errors for destination being null, and speed being greater than 0, which // should not be possible.. needs more research. I hate using try/catch, // but it's the only interim solution try { BeginMoving(new MovementVector((Point) wayPoints[0], BestSpeed())); } catch (Exception ex) { // Dunno what to do really. Hope it figures something out in Idle. this.WriteTrace(ex); }

// We've reached our final destination. } else { wayPoints.Clear(); } } return; }

// Fires whenever we get teleported. void TeleportedEvent(object sender, TeleportedEventArgs e) {

// We need to reset some variables to prevent confusion. wayPoints.Clear(); return; }

// Last event for an organism. Called every turn. void IdleEvent(object sender, IdleEventArgs e) {

// Reproduce as often as possible. if (CanReproduce) { // We don't pass any information to children... yet. BeginReproduction(null); }

// See what's around us. ScanArea(); // Survey the area for potential threats before we do anything more. if (CheckForPredators()) {

// Turn on the adrenaline. afraid = 2; // Fight like a trapped rat if we're not getting away (hell, even if we are). BeginDefending(predator); BeginAttacking(predator);

// What I do if threats are present. FindPath(escapeRoute) basically // The CheckForPredators function should select the largest threat and assign // it's AnimalState to predator, create an array of potential threats, and set // our destination to the best escape point... in theory.

// Temporary instructions. Vector fleeVector = Vector.Subtract(predator.Position, Position); fleeVector = fleeVector.GetUnitVector(); fleeVector = fleeVector.Scale(100); int fleeSpeed = (int) (BestSpeed() * 2) < Species.MaximumSpeed ? (int) (BestSpeed() * 2) : Species.MaximumSpeed; destination = Vector.Add(Position, fleeVector); // Seems there may be some sort of problem with attempting to run // out of the world bounds here, but if that were the case, FindPath // should be returning false. Needs more examination. if (FindPath(this.Position, destination, 3)) { BeginMoving(new MovementVector((Point) wayPoints[0], BestSpeed())); } else { RandomizeDestination(); BeginMoving(new MovementVector(destination, BestSpeed())); }

// If we were running away the last turn, and we no longer see any threats, // we will continue to run this turn, just to be safe. } else if (afraid > 0) { afraid -= 1;

// We'll see if we can get some dinner } else if (CheckForPrey()) { if (!prey.IsAlive) { if (CanEat) { if (WithinEatingRange(prey)) { if (IsMoving) { StopMoving(); } BeginEating(prey); } else { destination = prey.Position; if (FindPath(this.Position, destination, 3)) { BeginMoving(new MovementVector((Point) wayPoints[0], BestSpeed())); } else { RandomizeDestination(); BeginMoving(new MovementVector(destination, BestSpeed())); } } } else { // We're full and have targeted a corpse. Need to think of something // more productive to do.

} } else { BeginDefending(prey); BeginAttacking(prey); if (!WithinAttackingRange(prey)) { destination = prey.Position; if (FindPath(this.Position, destination, 3)) { BeginMoving(new MovementVector((Point) wayPoints[0], BestSpeed())); } else { RandomizeDestination(); BeginMoving(new MovementVector(destination, BestSpeed())); } } } } else { // There is nothing around here, so we'll go look for something to eat. if (!IsMoving) { RandomizeDestination(); if (FindPath(this.Position, destination, 3)) { BeginMoving(new MovementVector((Point) wayPoints[0], BestSpeed())); } else { RandomizeDestination(); BeginMoving(new MovementVector(destination, BestSpeed())); } } } return; } /////////////////////////////////////////////////////////////////////////////// // // // Non Pathfinding Stuff // // // ///////////////////////////////////////////////////////////////////////////////

// When we have no target in site, we just want to stroll along looking for one. void RandomizeDestination() { destination.X = OrganismRandom.Next(10, WorldWidth - 11); destination.Y = OrganismRandom.Next(10, WorldHeight - 11); if (destination == this.Position) { RandomizeDestination(); } }

// Select the Best Speed for the situation. Needs MUCH more fixing. // Preferrably using "double this.State.EnergyRequiredToMove(distance, speed)" // to select a truly optimal speed based on energy remaining, energy cost, and // situation at hand. For now,we decide based only on EnergyState, and // whether or not we have a purpose. int BestSpeed() { int bestSpeed = 4; int goodSpeed1 = 4; int goodSpeed2 = 4;

switch(State.EnergyState) { case EnergyState.Full: goodSpeed1 = Convert.ToInt32(Species.MaximumSpeed * .80); break; case EnergyState.Normal: goodSpeed1 = Convert.ToInt32(Species.MaximumSpeed * .60); break; case EnergyState.Hungry: goodSpeed1 = Convert.ToInt32(Species.MaximumSpeed * .40); break; default: goodSpeed1 = Convert.ToInt32(Species.MaximumSpeed * .20); break; }

if (prey == null && threats.Count <= 0) { bestSpeed = 4; } else if (threats.Count > 0) { goodSpeed1 = (int) (goodSpeed1 * 1.5) > Species.MaximumSpeed ? Species.MaximumSpeed : (int) (goodSpeed1 * 1.5); goodSpeed2 = (predator.Speed + 3) > Species.MaximumSpeed ? Species.MaximumSpeed : (predator.Speed + 3); bestSpeed = goodSpeed1 > goodSpeed2 ? goodSpeed1 : goodSpeed2; } else if (prey.AnimalSpecies.IsCarnivore) { goodSpeed2 = (prey.Speed + 3) > Species.MaximumSpeed ? Species.MaximumSpeed : (prey.Speed + 3); bestSpeed = goodSpeed1 > goodSpeed2 ? goodSpeed1 : goodSpeed2; } else { bestSpeed = goodSpeed1; } return bestSpeed; } // Scan the area, and sort organisms found into various groups. void ScanArea() {

// Clear our arrays so we can rebuild them. animals.Clear(); others.Clear(); live.Clear(); dead.Clear();

// Scan our surroundings for all creatures creatures = Scan();

// Split list of organisms into animals and "others" (others means plants // really, but who knows if anything goofy will be added in the future). foreach(OrganismState orgState in creatures) { if (orgState is AnimalState) { animals.Add(orgState); } else { others.Add(orgState); } }

// Split list of animals into live and dead. foreach(AnimalState animal in animals) { if (animal.IsAlive) { live.Add(animal); } else { dead.Add(animal); } } return; }

// Looks for potential threats, decides which way is necessary to escape, // and defends against closest predator (or closest living animal if no // predators are present. bool CheckForPredators() { int myDamage = 0; int myDefense = 0; int myHealth = 0; int myBlows = 0; int predDamage = 0; int predDefense = 0; int predHealth = 0; int predBlows = 0; double minDistance = double.MaxValue; bool returnVal = false;

// Clear out threats array and predators so we can rebuild them. threats.Clear(); predator = null;

// Determine if any of the living creatures is a threat to us. foreach(AnimalState animal in live) { if (!IsMySpecies(animal)) {

// Our decision process for threats. if (animal.AnimalSpecies.IsCarnivore) { myDamage = (Species.MaximumAttackDamagePerUnitRadius * State.Radius); myDefense = (Species.MaximumDefendDamagePerUnitRadius * State.Radius); myHealth = ((EngineSettings.DamageToKillPerUnitOfRadius * State.Radius) - State.Damage); predDamage = (animal.AnimalSpecies.MaximumAttackDamagePerUnitRadius * animal.Radius); predDefense = (animal.AnimalSpecies.MaximumDefendDamagePerUnitRadius * animal.Radius); predHealth = ((EngineSettings.DamageToKillPerUnitOfRadius * animal.Radius) - animal.Damage);

// The average number of blows to kill this opponent. myBlows = (myDamage > predDefense) ? (predHealth / (myDamage - predDefense)) : int.MaxValue; predBlows = (predDamage > myDefense) ? (myHealth / (predDamage - myDefense)) : int.MaxValue;

if (predBlows <= myBlows) { threats.Add(animal); if (DistanceTo(animal) < minDistance) { predator = animal; minDistance = DistanceTo(animal); } returnVal = true; } } } } return returnVal; }

// Looks for closest target animal, corpses first, then living. bool CheckForPrey() { double minDistance = double.MaxValue; bool returnVal = false; prey = null; // We chase other carnivores first, then if we have any corpses available, target // them instead of fighting, otherwise, go for the nearest living creature. if (live.Count > 0) { foreach(AnimalState animal in live) { if (animal.AnimalSpecies.IsCarnivore && !IsMySpecies(animal) && (DistanceTo(animal) < minDistance)) { prey = animal; minDistance = DistanceTo(animal); returnVal = true; } } } if (!returnVal && (dead.Count > 0 || live.Count > 0)) { minDistance = double.MaxValue; foreach(AnimalState corpse in dead) { if (DistanceTo(corpse) < minDistance) { prey = corpse; minDistance = DistanceTo(corpse); returnVal = true; } } // } // if (!returnVal && live.Count > 0) { foreach(AnimalState animal in live) { if (!IsMySpecies(animal) && (DistanceTo(animal) < minDistance)) { prey = animal; minDistance = DistanceTo(animal); returnVal = true; } } } return returnVal; }

/////////////////////////////////////////////////////////////////////////////// // // // Pathfinding Stuff // // // // Because the world is split into "cells" of 8x8 pixels each it is easier // // (and more accurate) to calculate movement in terms of the world's // // "grid", which we'll call "gridspace". However, to properly calculate // // what "cells" we move into, we need to draw a line through "pixelspace" // // to get the "cells" that we could potentially occupy along a path. // // // ///////////////////////////////////////////////////////////////////////////////

// Attempts to find the shortest path between two points. // If a path is found, ArrayList will be returned, containing at // most 'maxDepth' waypoints between 'from' and 'to'. bool FindPath(Point pixFrom, Point pixTo, int maxDepth) { Vector pixVector = new Vector((pixTo.X - pixFrom.X), (pixTo.Y - pixFrom.Y)); ArrayList obstacles = ScanForObstacles(); // Clear our list of waypoints. This won't affect our recursion, since we insert // waypoints on the way out of the loop. wayPoints.Clear(); // Recursively check and branch paths, decreasing maxDepth with each recursion. // First, check the straight vector. if (VectorIsClear(pixFrom, pixVector, obstacles)) {

// If vector is clear, add goal to beggining of wayPoints, and return true. wayPoints.Insert(0, pixTo); return true;

// If straight vector is blocked, check recursion depth. } else if (maxDepth > 0) {

// Because we are dealing with squares on a grid, and the straight path was blocked, // we snap the vector for checking to the nearest cardinal of PI/8. double snapTheta = ((int) ((pixVector.Direction + (Math.PI / 16)) / (Math.PI / 8))) * (Math.PI / 8); Vector snapVector = new Vector(Math.Cos(snapTheta), Math.Sin(snapTheta)); double angleIncr = 0.0f;

// scale snapped waypoint snapVector = snapVector.GetUnitVector(); snapVector = snapVector.Scale(40.0);

// We check our snapped angle first, to avoid double checking it in the loop below if (VectorIsClear(pixFrom, snapVector, obstacles)) {

// recurse and test from that point to the goal. // if child succeeds, add point to beginning of list and return true. Point nextPoint = new Point((pixFrom.X + snapVector.Point.X), (pixFrom.Y + snapVector.Point.Y)); if (FindPath(nextPoint, pixTo, maxDepth - 1)) { wayPoints.Insert(0, nextPoint); return true; } }

// For each plausible angle of rotation: for (int i = 0; i < 6; i++) {

// Increment the angle difference to check in each direction angleIncr += (Math.PI / 8);

// rotate and scale counterclockwise waypoint Vector ccwVector = snapVector.Rotate(angleIncr); ccwVector = ccwVector.GetUnitVector(); ccwVector = ccwVector.Scale(40.0); // There has to be a better way to scale.

// rotate and scale clockwise waypoint Vector cwVector = snapVector.Rotate(-angleIncr); cwVector = cwVector.GetUnitVector(); cwVector = cwVector.Scale(40.0); // There has to be a better way to scale.

// Because we always check CCW vector first, we'll have a preference for left // turns, but it shouldn't be too noticable. if (VectorIsClear(pixFrom, ccwVector, obstacles)) {

// recurse and test from that point to the goal. // if child succeeds, add point to beginning of list and return true. Point nextPoint = new Point((pixFrom.X + ccwVector.Point.X), (pixFrom.Y + ccwVector.Point.Y)); if (FindPath(nextPoint, pixTo, maxDepth - 1)) { wayPoints.Insert(0, nextPoint); return true; } // If CCW vector doesn't work out, we'll check the CW vector. } else if (VectorIsClear(pixFrom, cwVector, obstacles)) {

// recurse and test from that point to the goal. // if child succeeds, add point to beginning of list and return true. Point nextPoint = new Point((pixFrom.X + cwVector.Point.X), (pixFrom.Y + cwVector.Point.Y)); if (FindPath(nextPoint, pixTo, maxDepth - 1)) { wayPoints.Insert(0, nextPoint); return true; } } } } return false; }

// Determine if there are any obstacles along a specified vector. bool VectorIsClear(Point pixFrom, Vector pixVector, ArrayList obstacles) { bool returnVal = true; Rectangle vectorBox = new Rectangle();

// Convert our pixel coords to grid form for checking Point pixTo = new Point((pixFrom.X + pixVector.Point.X), (pixFrom.Y + pixVector.Point.Y)); Point gridFrom = PixToGrid(pixFrom); Point gridTo = PixToGrid(pixTo);

// If our vector takes us off of the worldmap, discard it outright. if (pixTo.X < 0 || pixTo.Y < 0 || pixTo.X >= WorldWidth || pixTo.Y >= WorldHeight) { returnVal = false; } else { // Calculate the coordinates for the vector's bounding box in "gridspace". vectorBox.X = gridFrom.X < gridTo.X ? gridFrom.X - State.CellRadius : gridTo.X - State.CellRadius; vectorBox.Y = gridFrom.Y < gridTo.Y ? gridFrom.Y - State.CellRadius : gridTo.Y - State.CellRadius; vectorBox.Width = Math.Abs(gridFrom.X - gridTo.X) + (State.CellRadius * 2); vectorBox.Height = Math.Abs(gridFrom.Y - gridTo.Y) + (State.CellRadius * 2);

// We might be able to discard some plants to make this more // efficient, but for now we'll check them all. foreach(OrganismState orgState in obstacles) {

// Get the bounding box for this obstacle in gridspace Rectangle obstacleBox = GetGridBounds(orgState.Position, orgState.CellRadius); // Check if the obstacle falls within the vector bounding box, // if it doesn't, there is no point in checking if it's along our vector. if (CheckBoxInBoundingBox(obstacleBox, vectorBox)) {

// Check if the obstacle falls along the line we intend to move along. returnVal = !CheckBoxOnLine(pixFrom, pixTo, obstacleBox);

// If we ran into something, this vector is no good, so break and return false. if (!returnVal) { break; } } } } return returnVal; }

// Uses a slightly modified bresenham algorithm to draw a line in pixels from // one point to another. Anytime that line enters a new "cell" we check the // bounding box for our potential position against that of the obstacle's. bool CheckBoxOnLine(Point pixFrom, Point pixTo, Rectangle obstacleBox) { int dx = (pixTo.X - pixFrom.X); // change in X over the span int dy = (pixTo.Y - pixFrom.Y); // change in Y over the span int d; // decision variable int incrE; // increment of d for East int incrNE; // increment of d for NE int x = pixFrom.X; // current "x" coordinate int y = pixFrom.Y; // current "y" coordinate int incrX, incrY; // Increment values for x and y bool flipXY = false; // Flags inversion of x and y for certian cases int tmp; // Used for swapping variables Point newCell = new Point(0,0); Point lastCell = new Point(0,0);

// Horizontal, vertical and 45-degree angles are drawn seperately, // because they have integer slopes already. if (Math.Abs(dx) == Math.Abs(dy) || dx == 0 || dy == 0 ) {

if (pixTo.X > pixFrom.X) { // Left, vertical, or right? incrX = 1; } else if (pixTo.X == pixFrom.X) { incrX = 0; } else { incrX = -1; }

if (pixTo.Y > pixFrom.Y) { // Up, horizontal, or down? incrY = 1; } else if (pixTo.Y == pixFrom.Y) { incrY = 0; } else { incrY = -1; }

// Step through each pixel until the endpoint. while ((incrX < 0 && x > pixTo.X) || (incrX > 0 && x < pixTo.X) || (incrY < 0 && y > pixTo.Y) || (incrY > 0 && y < pixTo.Y)) { x += incrX; y += incrY;

// Determine what "cell" this pixel falls in. newCell = PixToGrid(new Point(x, y));

// Determine if this "cell" is different than the last. if (newCell != lastCell) {

// Check our bounding box at this point with that of the obstacle. if (CheckBoxInBoundingBox(GetGridBounds(new Point(x, y), this.State.CellRadius), obstacleBox)) { return true; } // Assign newCell to lastCell to compare against in the next check. lastCell = newCell; } } } // For all other lines, we use the following. else {

// If Math.Abs(slope) > 1 then we flip x & y for these "steep" lines. if (Math.Abs(dx) < Math.Abs(dy)) {

// Swap "from" X & Y values. tmp = pixFrom.X; pixFrom.X = pixFrom.Y; pixFrom.Y = tmp;

// Swap "to" X & Y values. tmp = pixTo.X; pixTo.X = pixTo.Y; pixTo.Y = tmp;

// We flag the change, so we can unswap X & Y for drawing the actual pixels. flipXY = true; }

// Initialize variables. dx = Math.Abs(pixTo.X - pixFrom.X); // Distance over the span. dy = Math.Abs(pixTo.Y - pixFrom.Y); // Distance over the span. d = 2 * dy - dx; // Initial value of decision variable. x = pixFrom.X; y = pixFrom.Y;

incrE = 2 * Math.Abs(dy); incrNE = 2 * (Math.Abs(dy) - Math.Abs(dx));

if (pixTo.X > pixFrom.X) { // Drawing left or right? incrX = 1; } else { incrX = -1; }

if (pixTo.Y > pixFrom.Y) { // Drawing up or down? incrY = 1; } else { incrY = -1; }

// The main drawing loop. // Because of the x/y reversal for "steep" lines, x is incremented // by 1 (or -1) at every step. Thus, when x reaches the final coordinate, // the line has been completely drawn. while ((incrX < 0 && x > pixTo.X) || (incrX > 0 && x < pixTo.X)) {

// Choose the "East" point if the line is below the midpoint. if (d < 0) { d += incrE; x += incrX; } // Handle exact midpoint decisions, as mentioned in CGPP, page 78. else if (d == 0) { if (incrX > 0) { // Left to right scan, choose E. d += incrE; x += incrX; } else { // Right to left scan, choose NE. d += incrNE; // (actually NW or SW in this case) x += incrX; y += incrY; } } else { // Choose the NE point if none of the d += incrNE; // above cases apply. (ie, line is in x += incrX; // the "upper" half. y += incrY; }

// If we flipped x and y, we must flip them back before drawing // this pixel on the line. if (!flipXY) {

// Determine what "cell" this pixel falls in. newCell = PixToGrid(new Point(x, y));

// Determine if this "cell" is different than the last. if (newCell != lastCell) {

// Check our bounding box at this point with that of the plant. if (CheckBoxInBoundingBox(GetGridBounds(new Point(x, y), this.State.CellRadius), obstacleBox)) { return true; } }

// Our X & Y are normal. } else {

// Determine what "cell" this pixel falls in. newCell = PixToGrid(new Point(y, x));

// Determine if this "cell" is different than the last. if (newCell != lastCell) {

// Check our bounding box at this point with that of the plant. if (CheckBoxInBoundingBox(GetGridBounds(new Point(y, x), this.State.CellRadius), obstacleBox)) { return true; } } }

// Assign newCell to lastCell to compare against in the next check. lastCell = newCell; } } return false; }

// If one bounding box overlaps or shares an edge with a second box, return true. bool CheckBoxInBoundingBox(Rectangle gridBox1, Rectangle gridBox2) { bool returnVal = false; // If any point in the first box falls on the border of, // or within the second box, then return true. returnVal |= CheckPointInBoundingBox( new Point(gridBox1.X, gridBox1.Y), gridBox2); returnVal |= CheckPointInBoundingBox( new Point(gridBox1.X, gridBox1.Y + gridBox1.Height), gridBox2); returnVal |= CheckPointInBoundingBox( new Point(gridBox1.X + gridBox1.Width, gridBox1.Y), gridBox2); returnVal |= CheckPointInBoundingBox( new Point(gridBox1.X + gridBox1.Width, gridBox1.Y + gridBox1.Height), gridBox2);

// Same is true for the reverse if (!returnVal) { returnVal |= CheckPointInBoundingBox( new Point(gridBox2.X, gridBox2.Y), gridBox1); returnVal |= CheckPointInBoundingBox( new Point(gridBox2.X, gridBox2.Y + gridBox2.Height), gridBox1); returnVal |= CheckPointInBoundingBox( new Point(gridBox2.X + gridBox2.Width, gridBox2.Y), gridBox1); returnVal |= CheckPointInBoundingBox( new Point(gridBox2.X + gridBox2.Width, gridBox2.Y + gridBox2.Height), gridBox1); }

// Check for overlapping boxes (cross shaped) if (!returnVal) { returnVal = gridBox1.X >= gridBox2.X && gridBox1.X + gridBox1.Width <= gridBox2.X + gridBox2.Width && gridBox1.Y <= gridBox2.Y && gridBox1.Y + gridBox1.Height >= gridBox2.Y + gridBox2.Height; } // Check for overlapping boxes (reversed) if (!returnVal) { returnVal = gridBox2.X >= gridBox1.X && gridBox2.X + gridBox2.Width <= gridBox1.X + gridBox1.Width && gridBox2.Y <= gridBox1.Y && gridBox2.Y + gridBox2.Height >= gridBox1.Y + gridBox1.Height; } return returnVal; }

// If a point falls on the border of or within a bounding box, return true. bool CheckPointInBoundingBox(Point gridPoint, Rectangle gridBox) { return ( gridPoint.X >= gridBox.X && gridPoint.X <= gridBox.X + gridBox.Width && gridPoint.Y >= gridBox.Y && gridPoint.Y <= gridBox.Y + gridBox.Height ); }

// Used to get any organism's bounds in "gridspace". Rectangle GetGridBounds(Point pixPoint, int gridRadius) {

Rectangle gridBounds = new Rectangle( ((pixPoint.X>>3) - gridRadius), ((pixPoint.Y>>3) - gridRadius), (gridRadius * 2), (gridRadius * 2) ); return gridBounds; }

// Convert "pixel" coordinates to "grid" coordinates Point PixToGrid(Point pixPoint) { Point gridPoint = new Point((pixPoint.X>>3), (pixPoint.Y>>3)); return gridPoint; } // Get a list of anything in our path that isn't a potential meal. ArrayList ScanForObstacles() { ArrayList obstacles = new ArrayList();

// If it isn't what I'm trying to get to, it's an obstacle. foreach(OrganismState orgState in creatures) { if (prey != orgState) { obstacles.Add(orgState); } } return obstacles; } /////////////////////////////////////////////////////////////////////////////// // // // Serialization Stuff // // // ///////////////////////////////////////////////////////////////////////////////

// This gets called whenever an animal is being saved, either because the // game is closing or because you are being teleported. Store anything you // want to remember when you are instantiated again in the stream. public override void SerializeAnimal(MemoryStream m) { // I seriously need to learn how to use this so that we can save: // predator // prey // destination // wayPoints // Although we should be able to obtain all this info again easily, it // would be nice to have it immediately available. Every turn counts. return; }

// This gets called when you are instantiated again after being saved. You // get a chance to pull out any information that you put into the stream // when you were saved. public override void DeserializeAnimal(MemoryStream m) { // Same as above return; } }

ScrewTurn Wiki version 2.0.33. Some of the icons created by FamFamFam. Note: This site is not affiliated with Microsoft Corp. Content is user contributed and provided as-is.