% File "animal.tu". % % Written by Phil Edmonds, Feb 97. % % An animal is a kind of active object. They have a current state (eating, % sleeping, etc.), and a current energy level. There are many routines % here for moving, which calculate speed accross different terrain, but a % basic animal does not move, because it has nowhere to go. This is really % just a frame to build more specific animals out of. % %+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ unit class Animal inherit ActiveObject in "active.tu" import SimVar in "simvar.tu", Terrain in "terrain.tu" export InitAnimal, GetLoc, SetLoc, GetDestination, SetDestination, GetEnergy, SetEnergy, Hungry, Full, OutOfEnergy, IsAlive, Die %========== INSTANCE VARIABLES ============================== %============================================================ % basic info about the animal var speed : int % how far I can move in one turn normally var girth : int % my size around var dest : ^Location % where I'm going currently var alive : boolean % my current state % energy variables var cur_energy : int % current energy level var min_energy : int % minimum energy level before food is required var max_energy : int % maximum energy level var cost_per_move : int % basic cost in energy for moving forward proc Move %========== INITIALIZE ====================================== %============================================================ proc InitAnimal (x, y : real, n : string, c : int, v : int, g : int, max_e : int, min_e, cpm : int) InitActiveObject (x, y, n, c, 1) speed := v girth := g new dest dest -> Copy (loc) alive := true max_energy := max_e min_energy := min_e cur_energy := min_e cost_per_move := cpm end InitAnimal %========== PUBLIC FUNCTIONS ================================ %============================================================ % The following are here so external objects can access the instance % variables. E.g., one animal can change the destination of another % by calling SetDestination %------------------------------------------------------------ function GetLoc : ^Location result loc end GetLoc proc SetLoc (l : ^Location) loc -> Copy (l) end SetLoc function GetDestination : ^Location result dest end GetDestination proc SetDestination (l : ^Location) dest -> Copy (l) end SetDestination function GetEnergy : int result cur_energy end GetEnergy proc SetEnergy (e : int) if e >= 0 and e <= max_energy then cur_energy := e end if end SetEnergy % Returns TRUE if I'm in need of energy %------------------------------------------------------------ function Hungry : boolean result cur_energy < min_energy end Hungry % Returns TRUE if my energy is below zero %------------------------------------------------------------ function OutOfEnergy : boolean result cur_energy < 0 end OutOfEnergy % Returns TRUE if I'm at maximum energy %------------------------------------------------------------ function Full : boolean result cur_energy >= max_energy end Full % Returns TRUE if I'm alive %------------------------------------------------------------ function IsAlive : boolean result alive end IsAlive % Tell me to die %------------------------------------------------------------ proc Die EraseImage alive := false DrawImage end Die % A basic image: just a box %------------------------------------------------------------ body proc DrawImage var x := round (loc -> GetX) var y := round (loc -> GetY) Draw.Box (x - girth, y - girth, x + girth, y + girth, col) end DrawImage % To erase, redraw the part of the world that I cover %------------------------------------------------------------ body proc EraseImage world -> DrawWorldPart (loc, girth) end EraseImage % Taking a turn is very simple, just draw myself and schedule % another turn %------------------------------------------------------------ body proc TakeTurn Move cur_energy -= cost_per_move DrawImage world -> Schedule (self, frequency) end TakeTurn %========== PRIVATE FUNCTIONS ================================ %============================================================ % set my destination to a random place %------------------------------------------------------------ proc SetRandomDestination free dest dest := world -> RandomLocation (girth + speed) end SetRandomDestination % How far can I go now depends on what terrain I'm in. % Simply multiples speed by resistance (0.0 .. 1.0) in the terrain. %------------------------------------------------------------ function MoveDistance (l : ^Location) : int var t : ^Terrain := world -> WhatTerrain (l) result floor (speed * t -> MoveResistance) end MoveDistance % returns true if I've reached my destination %------------------------------------------------------------ function ReachedDestination : boolean result (loc -> LocDiff (dest) < MoveDistance (loc)) end ReachedDestination % compute a new location, given how far we are going % returns the new location, or loc (current loc) if arrived % at destination already %------------------------------------------------------------ function ComputeNextLocation (howfar : int) : ^Location var dist := loc -> LocDiff (dest) if (dist > howfar) then var rate : real := howfar / dist var dx : real := loc -> XDiff (dest) var dy : real := loc -> YDiff (dest) var nextloc : ^Location new nextloc nextloc -> SetX (loc -> GetX - rate * dx) nextloc -> SetY (loc -> GetY - rate * dy) result nextloc else result loc end if end ComputeNextLocation % make a move: Call this to make me move towards my destination % first determines current terrain and computes a new location % from that, makes the move into the terrain, if possible % (just updates location, and erases self, does not make the move) %------------------------------------------------------------ body proc Move var newloc : ^Location newloc := ComputeNextLocation (MoveDistance (loc)) assert newloc not= nil % when we have a newloc, and it's different from our current if newloc not= loc then % check to make sure we will be able move in the new location if (MoveDistance (newloc) > 0) then % do the move EraseImage free loc loc := newloc else % we're stuck, so SetRandomDestination end if end if end Move end Animal