Ok, bored of this one now.
The buildings are in, but aren't functional at the moment. There's a good base of code here for inexperienced coders to learn from and more experienced coders to advance upon. Plenty of comments.
Here's the source:
`TYPE DEFINITIONS
`Stores info about animation frames
type AnimType
zomwalkcount as word
zomwalkframe as word
humwalkcount as word
humwalkframe as word
endtype
`General camera information
type CameraType
tendToX# as float
tendToZ# as float
tendToY# as float
endtype
`Info about map squares
type SquareType
occupied as word
class as word
building as word
endtype
`Zombie data
type ZombieType
inscreen as boolean
alive as boolean
waiting as word
obj as word
x as integer
z as integer
x# as float
z# as float
speed# as float
speeddiag# as float
skill as word
targetman as word
targetbuilding as word
direcx as integer
direcz as integer
direcxcount as word
direczcount as word
angle as word
humansearch as word
endtype
`Human data
type HumanType
inscreen as boolean
alive as boolean
waiting as word
obj as word
x as integer
z as integer
x# as float
z# as float
speed# as float
speeddiag# as float
tiredspeed# as float
tiredspeeddiag# as float
skill as word
infection as integer
fatigue as word
maxFatigue as word
targetbuilding as word
targetzom as word
direcx as integer
direcz as integer
direcxcount as word
direczcount as word
angle as word
zombiesearch as word
fleeing as boolean
inBuilding as word
endtype
`Building data
type BuildingType
exists as boolean
inscreen as boolean
obj as word
btype as word
doorOpen as boolean
barricade as word
foodManDays as word
x as word
z as word
endtype
`Data structure for passing back information from functions about movements
type DMoveType
xmove as integer
zmove as integer
xcount as word
zcount as word
angle as word
found as byte
endtype
`Number positions of images
type ImagePositionsType
zomimage as word
humimage as word
infimage as word
dedimage as word
floortex as word
house1image as word
endtype
`Number positions of object primitives
type ObjBaseType
plain as word
zomwalk as word
humwalk as word
infwalk as word
dedbase as word
house1base as word
endtype
`CONSTANT DEFINITIONS
`***** DO NOT MODIFY *****
#constant TRUE = 1
#constant FALSE = 0
#constant Const_ZombieClassID = 1
#constant Const_HumanClassID = 2
#constant Const_YPosMap = -0.1
#constant Const_YPosBuilding = -0.08
#constant Const_YPosCorpse = -0.06
#constant Const_YPosLiving = -0.04
`***** MODIFIABLE CONSTANTS *****
`Maximum time a zombie will wait around for before doing something (30 = 1 second)
#constant Const_MaxZombieWaitTime = 50 `[50 DEFAULT]
`Maximum number of zombie that can be alive at any time
#constant Const_MaxZombies = 5000 `[5000 DEFAULT]
`Smoothing value to smooth zombie image rotation
#constant Const_ZombieTurnAnimationSpeed = 8 `[8 DEFAULT]
`Amount of time before zombie looks for closest human (30 = 1 second)
`Setting this value low will result in poor performance
#constant Const_ZombieHumanSearch = 100 `[100 DEFAULT]
`Range in squares zombie searches when looking for things
`Setting this value too high will result in poor performance
#constant Const_ZombieSearchRange = 20 `[20 DEFAULT]
`Maximum amount of squares a zombie will move when wondering before changing direction
#constant Const_ZombieDirectionChange = 10 `[10 DEFAULT]
`Level of zombie skill (Smaller values are more skillful!) 1 = 50% chance of kill,
`2 = 25% chance of kill etc.
#constant Const_MinZombieSkill = 2 `[2 DEFAULT]
#constant Const_MaxZombieSkill = 10 `[10 DEFAULT]
`Minimum and maximum speed a human can run at (with no particular units)
`This value is halved when a human becomes fatigued
#constant Const_MinZombieSpeed = 300 `[300 DEFAULT]
#constant Const_MaxZombieSpeed = 800 `[800 DEFAULT]
`How many SYNCs occur before animations change frames
#constant Const_ZombieWalkAnimSpeed = 4 `[4 DEFAULT]
#constant Const_HumanWalkAnimSpeed = 4 `[4 DEFAULT]
`As Zombie constants, with a few exceptions ...
#constant Const_MaxHumanWaitTime = 20 `[20 DEFAULT]
#constant Const_MaxHumans = 5000 `[5000 DEFAULT]
#constant Const_HumanTurnAnimationSpeed = 8 `[8 DEFAULT]
#constant Const_HumanSearchRange = 20 `[20 DEFAULT]
#constant Const_HumanZombieSearch = 30 `[30 DEFAULT]
`The time it takes for the zombie infection to turn a human to a zombie (30 = 1 second)
`This value is halved on each subsequent zombie bite, so packs of zombies kill much quicker
#constant Const_InfectionStartLife = 2000 `[2000 DEFAULT]
#constant Const_HumanDirectionChange = 20 `[20 DEFAULT]
`The time it takes (in squares) for a human to get exhausted and for it's speed to
`half (fatigue it recooped by a stay in a building)
#constant Const_MinFatigue = 20 `[20 DEFAULT]
#constant Const_MaxFatigue = 40 `[40 DEFAULT]
`Minimum and maximum speed a human can run at (with no particular units)
`This value is halved when a human becomes fatigued
#constant Const_MinHumanSpeed = 600 `[600 DEFAULT]
#constant Const_MaxHumanSpeed = 1200 `[1200 DEFAULT]
`As with zombie skills
#constant Const_MinHumanSkill = 2 `[2 DEFAULT]
#constant Const_MaxHumanSkill = 10 `[10 DEFAULT]
`This value determines how aggressive humans are. Any human with a skill level
`below this value fancies themselves as a hardcase and will attack zombies. The
`rest will leg it. Set it higher to increase lunatics and set it low to increase
`cowards.
#constant Const_AttackZombieSkillThreshhold = 4 `[4 DEFAULT]
`Maximum possible buildings
#constant Const_MaxBuildings = 100 `[100 DEFAULT]
`Chance a newly created buildings doors will be open. Higher numbers mean smaller
`chance. Open doors allow zombies to roam inside the building
#constant Const_DoorsOpenChance = 100 `[10 DEFAULT]
`Amount of turns a building can sustain 1 man
#constant Const_BuildingMinFoodTurns = 100 `[100 DEFAULT]
#constant Const_BuildingMaxFoodTurns = 1000 `[1000 DEFAULT]
`Zoom sensitivity multiplier
#constant Const_CameraZoomSensitivity = 0.1 `[0.1 DEFAULT]
`Scroll sensitivity multiplier
#constant Const_CameraScrollSensitivity = 0.1 `[0.1 DEFAULT]
`This determines how far the camera has to zoom out before animation and other visual
`effects are stopped to increase performance. 100 looks visually about right.
#constant Const_CameraAnimationThreshhold = 100 `[100 DEFAULT]
`Map dimensions
#constant Const_MapWidth = 200
#constant Const_MapHeight = 200
`****************************************************************
`GAME SETUP
InitialiseSystemAndVariables()
LoadMedia()
MakeLand()
MakeBaseObjects()
MakeBuilding(100,100,1)
MakeBuilding(106,100,1)
MakeBuilding(103,108,1)
MakeBuilding(101,93,1)
MakeZombiePack(95,100,50,10)
MakeHumanPack(105,100,10,10)
`-----------------------------------------------------------------
`MAIN GAME LOOP
repeat
sync
until scancode()
repeat
ControlCamera()
WhatsInScreen()
ProcessZombies()
ProcessHumans()
AnimateInstances()
OutputData()
fastsync
until escapekey()
end
`-----------------------------------------------------------------
`Returns a free object number
function GetNextFreeObject()
obj = 0
repeat
obj = obj + 1
until object exist(obj) = 0
endfunction obj
`Returns a free image number
function GetNextFreeImage()
img = 0
repeat
img = img + 1
until image exist(img) = 0
endfunction img
`Returns a free position in the Zombie array
function GetNextFreeZombie()
for z = 1 to Const_MaxZombies
if Zombie(z).alive = 0 then exitfunction z
next z
endfunction -1
`Returns a free position in the Human array
function GetNextFreeHuman()
for h = 1 to Const_MaxHumans
if Human(h).alive = 0 then exitfunction h
next h
endfunction -1
function GetNextFreeBuilding()
for b = 1 to Const_MaxBuildings
if Building(b).exists = 0 then exitfunction b
next b
endfunction -1
`Includes/excludes objects out of the screen and flags
`a variable for them, reducing the processing done on those
`objects
function WhatsInScreen()
for zom=1 to Const_MaxZombies
Zombie(zom).inscreen = 1
obj = Zombie(zom).obj
if obj > 0
if object in screen(obj)
exclude object off obj
Zombie(zom).inscreen = 1
else
exclude object on obj
Zombie(zom).inscreen = 0
endif
endif
next z
for hum=1 to Const_MaxHumans
Human(hum).inscreen = 1
obj = Human(hum).obj
if obj > 0
if object in screen(obj)
exclude object off obj
Human(hum).inscreen = 1
else
exclude object on obj
Human(hum).inscreen = 0
endif
endif
next z
endfunction
`Animates the various base objects that all elements
`are instanced from
function AnimateInstances()
`Don't bother to animate if the cam is zoomed out miles
if CamZoomInRange()
`Animate zombie walk
inc Anim.zomwalkcount,1
if Anim.zomwalkcount = Const_ZombieWalkAnimSpeed
Anim.zomwalkcount = 0
inc Anim.zomwalkframe,1
if Anim.zomwalkframe > 9
Anim.zomwalkframe = 0
endif
SetObjectFrame(ObjBase.zomwalk,Anim.zomWalkFrame)
endif
`Animate human and infected walk
inc Anim.humwalkcount,1
if Anim.humwalkcount = Const_HumanWalkAnimSpeed
Anim.humwalkcount = 0
inc Anim.humwalkframe,1
if Anim.humwalkframe > 9
Anim.humwalkframe = 0
endif
SetObjectFrame(ObjBase.humwalk,Anim.humWalkFrame)
SetObjectFrame(ObjBase.infwalk,Anim.humWalkFrame)
endif
endif
endfunction
`Simple function that returns 1 if the camera is zoomed in enough
`to see any action
function CamZoomInRange()
if camera position y() < Const_CameraAnimationThreshhold
exitfunction 1
endif
endfunction 0
function SetObjectFrame(obj,frame)
iXTile = frame mod 4
iYTile = 3-((frame-iXTile)/4)
u# = 0.25 * iXTile;
v# = 1.0-(0.25*iYTile)
lock vertexdata for limb obj, 0
set vertexdata uv 0, 0, u# + 0.25, v# - 0.25
set vertexdata uv 1, 0, u#, v# - 0.25
set vertexdata uv 2, 0, u# + 0.25, v#
set vertexdata uv 3, 0, u#, v# - 0.25
set vertexdata uv 4, 0, u#, v#
set vertexdata uv 5, 0, u# + 0.25, v#
unlock vertexdata
endfunction
`Takes a direction determined by dx and dz (a kind of gradient)
` e.g. 5,1 (move 5 right for every 1 up)
`Uses the values dxc and dzc (count values determining how many
`moves in each direction are remaining to complete a cycle
` e.g. 3,1 (3 moves right and 1 move up remaining)
`And then returns what the next movement should be, using 8
`directional movement, and populates the DMove data structure.
function DirectionalMove(dx,dz,dxc,dzc)
`A cycle has been completed, so reset it
if dxc = 0 and dzc = 0
`Reset movement
dxc = abs(dx)
dzc = abs(dz)
endif
`Work out what direction we're moving in and decrement the
`correct count values
if abs(dxc) > abs(dzc)
`Move on x-axis as there is more distance move on x than z
if dx > 0
xmove = 1
angle = 90
else
xmove = -1
angle = 270
endif
dxc = dxc - 1
else
if abs(dxc) < abs(dzc)
`Move on z-axis, as there is more distance to move on z than x
if dz > 0
zmove = 1
angle = 0
else
zmove = -1
angle = 180
endif
dzc = dzc - 1
else
`Move diagonally, as there is an equal distance to move on x and z
if dz > 0
if dx > 0
xmove = 1
zmove = 1
angle = 45
else
xmove = -1
zmove = 1
angle = 315
endif
else
if dx > 0
xmove = 1
zmove = -1
angle = 135
else
xmove = -1
zmove = -1
angle = 225
endif
endif
dzc = dzc - 1
dxc = dxc - 1
endif
endif
`Populate global DMove data structure with return data
DMove.xmove = xmove
DMove.zmove = zmove
DMove.xcount = dxc
DMove.zcount = dzc
Dmove.angle = angle
endfunction
`If the square the entity wishes to move to is occupied, or out of bounds
`this function determines an alternative square to move to. Based on the passed
`angle, it determines a list of next best angles in priority order, and then
`searches the squares pointed to by those angles to see if they're vacant
function FindNextBestSquare(angle,x,z,class)
`Set up angle priority array
select angle
case 0
AC(0)=315:AC(1)=45:AC(2)=270:AC(3)=90:AC(4)=225:AC(5)=135:AC(6)=180
endcase
case 45
AC(0)=0:AC(1)=90:AC(2)=315:AC(3)=135:AC(4)=270:AC(5)=180:AC(6)=225
endcase
case 90
AC(0)=45:AC(1)=135:AC(2)=0:AC(3)=180:AC(4)=315:AC(5)=225:AC(6)=270
endcase
case 135
AC(0)=90:AC(1)=180:AC(2)=45:AC(3)=225:AC(4)=0:AC(5)=270:AC(6)=315
endcase
case 180
AC(0)=135:AC(1)=225:AC(2)=90:AC(3)=270:AC(4)=45:AC(5)=315:AC(6)=0
endcase
case 225
AC(0)=180:AC(1)=270:AC(2)=135:AC(3)=315:AC(4)=90:AC(5)=0:AC(6)=45
endcase
case 270
AC(0)=225:AC(1)=315:AC(2)=180:AC(3)=0:AC(4)=135:AC(5)=45:AC(6)=90
endcase
case 315
AC(0)=270:AC(1)=0:AC(2)=225:AC(3)=45:AC(4)=180:AC(5)=90:AC(6)=135
endcase
endselect
`Check in priority order and populate the DMove data structure
`if the square can be moved to
for a = 0 to 6
DMove.angle = AC(a)
select AC(a)
case 0
if InMapRange(x,z+1)
if Square(x,z+1).occupied = 0
DMove.xmove = 0
DMove.zmove = 1
exitfunction TRUE
endif
endif
endcase
case 45
if InMapRange(x+1,z+1)
if Square(x+1,z+1).occupied = 0
DMove.xmove = 1
DMove.zmove = 1
exitfunction TRUE
endif
endif
endcase
case 90
if InMapRange(x+1,z)
if Square(x+1,z).occupied = 0
DMove.xmove = 1
DMove.zmove = 0
exitfunction TRUE
endif
endif
endcase
case 135
if InMapRange(x+1,z-1)
if Square(x+1,z-1).occupied = 0
DMove.xmove = 1
DMove.zmove = -1
exitfunction TRUE
endif
endif
endcase
case 180
if InMapRange(x,z-1)
if Square(x,z-1).occupied = 0
DMove.xmove = 0
DMove.zmove = -1
exitfunction TRUE
endif
endif
endcase
case 225
if InMapRange(x-1,z-1)
if Square(x-1,z-1).occupied = 0
DMove.xmove = -1
DMove.zmove = -1
exitfunction TRUE
endif
endif
endcase
case 270
if InMapRange(x-1,z)
if Square(x-1,z).occupied = 0
DMove.xmove = -1
DMove.zmove = 0
exitfunction TRUE
endif
endif
endcase
case 315
if InMapRange(x-1,z+1)
if Square(x-1,z+1).occupied = 0
DMove.xmove = -1
DMove.zmove = 1
exitfunction TRUE
endif
endif
endcase
endselect
next a
`If we get to there, there was no vacant square, so return false
endfunction FALSE
`This is the main zombie processing function that iterates through
`all the zombies and performs all the actions
function ProcessZombies()
for zom = 1 to Const_MaxZombies
if Zombie(zom).alive = TRUE
ProcessZombie(zom)
endif
next zom
endfunction
`Processes a single zombie, indicated by zom, performing all logic etc.
function ProcessZombie(zom)
`This zombie is waiting for some reason, so don't do any movement stuff
if Zombie(zom).waiting > 0
dec Zombie(zom).waiting,1
exitfunction
endif
`Zombie is ready to look for the closest human if the human search
`variable is at 0. Notice we only do this every now and then for speed
`reasons. By decreasing how often the zombies search for the next closest
`human (via a simple counter variable) we reduce how many times the CPU
`intensive functions are executed
if Zombie(zom).humansearch = 0
Zombie(zom).humansearch = Const_ZombieHumanSearch
`Find a human and put it's id into the targetman variable for
`this zombie. ClassInRange will return 0 if no human found.
Zombie(zom).targetman = ClassInRange(Zombie(zom).x,Zombie(zom).z,Const_HumanClassID,Const_ZombieSearchRange)
else
dec Zombie(zom).humansearch,1
endif
`If we're on our destination square, we're ready to navigate to the next
`one. This if block considers an 'accuracy threshhold' as the zombies x,z
`position may not be exactly equally to the square its aiming for due to
`floating point number precision and random movement speeds.
`All actions that occur within this if block only occur once as the zombie
`arives at a new square, and therefore can be more CPU intensive
`Values have to be multiplied by 1.0 so DB doesn't round the calculation
if abs(Zombie(zom).x*1.0 - Zombie(zom).x#) < Zombie(zom).speeddiag#
if abs(Zombie(zom).z*1.0 - Zombie(zom).z#) < Zombie(zom).speeddiag#
Zombie(zom).x# = Zombie(zom).x
Zombie(zom).z# = Zombie(zom).z
position object Zombie(zom).obj,Zombie(zom).x#,Const_YPosLiving,Zombie(zom).z#
`If we have a human target, attack it if we're close enough or
` set the direc variables to point at it if we're not
if Zombie(zom).targetman > 0
targ = Zombie(zom).targetman
`Chase human
SetNewZombieDirection(zom,Human(targ).x - Zombie(zom).x,Human(targ).z - Zombie(zom).z)
`If close enough, attack
if abs(Zombie(zom).x - Human(targ).x) < 2 and abs(Zombie(zom).z - Human(targ).z) < 2
`Random chance for zombie to strike human
if rnd(Zombie(zom).skill) = 0
`Zombie has successfully struck this human
`so infect it
InfectHuman(targ)
endif
endif
else
`Zombie doesn't have a target, so zombie will wonder around randomly
`There is a random chance for it to change direction
if rnd(Const_ZombieDirectionChange) = 0
`Zombie is changing direction, so work out new direction values
SetNewZombieDirection(zom,-10 + rnd(20),-10 + rnd(20))
endif
endif
`Call a function to populate the DMove data structure with
`data about which direction this zombie should move next
DirectionalMove(Zombie(zom).direcx,Zombie(zom).direcz,Zombie(zom).direcxcount,Zombie(zom).direczcount)
newx = Zombie(zom).x + DMove.xmove
newz = Zombie(zom).z + DMove.zmove
`If the square it wants to move to is occupied or outside the map,
`we'll have to search for the next best square and move to that instead
sqrMove = CanMoveToSquare(newx,newz,Const_ZombieClassID)
if sqrMove = 0
`SQUARE IS OCCUPIED
`Find the next best square to move to
if FindNextBestSquare(DMove.angle,Zombie(zom).x,Zombie(zom).z,Const_ZombieClassID) = TRUE
newx = Zombie(zom).x + DMove.xmove
newz = Zombie(zom).z + DMove.zmove
else
`No square was found to move to, so just wait for a bit
Zombie(zom).waiting = 1+rnd(Const_MaxZombieWaitTime)
exitfunction
endif
else
if sqrMove = -1
`SQUARE IS OFF THE MAP
`We'll make the zombie head back towards the center of the
`map to avoid them bunching at the map border.
SetNewZombieDirection(zom,Const_MapWidth/2-Zombie(zom).x,Const_MapHeight/2-Zombie(zom).z)
`We'll skip this turn since the square the zombie was going
`to move to before setting a new direction was illegal
exitfunction
endif
endif
Zombie(zom).direcxcount = DMove.xcount
Zombie(zom).direczcount = DMove.zcount
oldx = Zombie(zom).x
oldz = Zombie(zom).z
Zombie(zom).x = newx
Zombie(zom).z = newz
Zombie(zom).angle = DMove.angle
`Set old zombie square as vacant and new zombie square as occupied
Square(oldx,oldz).occupied = 0
Square(oldx,oldz).class = 0
Square(newx,newz).occupied = zom
Square(newx,newz).class = Const_ZombieClassID
endif
endif
`Rotate the zombie object to face the right way gradually, but only if it's in screen
if Zombie(zom).inscreen = 1 and CamZoomInRange()
yrotate object Zombie(zom).obj,curveangle(Zombie(zom).angle,object angle y(Zombie(zom).obj),Const_ZombieTurnAnimationSpeed)
endif
`Move zombie coordinates to gradually move to target square
`We use precalculated speed values and no trig to save cpu cycles.
`Note, this is a crappy way to code IF blocks, but apprarently they're
`faster than select statements, and I didn't want the nesting going
`across too far
ang = Zombie(zom).angle
if ang = 0
inc Zombie(zom).z#, Zombie(zom).speed#
else
if ang = 45
inc Zombie(zom).z#, Zombie(zom).speeddiag#
inc Zombie(zom).x#, Zombie(zom).speeddiag#
else
if ang = 90
inc Zombie(zom).x#, Zombie(zom).speed#
else
if ang = 135
inc Zombie(zom).x#, Zombie(zom).speeddiag#
dec Zombie(zom).z#, Zombie(zom).speeddiag#
else
if ang = 180
dec Zombie(zom).z#, Zombie(zom).speed#
else
if ang = 225
dec Zombie(zom).x#, Zombie(zom).speeddiag#
dec Zombie(zom).z#, Zombie(zom).speeddiag#
else
if ang = 270
dec Zombie(zom).x#, Zombie(zom).speed#
else
if ang = 315
dec Zombie(zom).x#, Zombie(zom).speeddiag#
inc Zombie(zom).z#, Zombie(zom).speeddiag#
endif:endif:endif:endif:endif:endif:endif:endif
`Only position the object if its in screen. This may seem a bit messed up sinse
`the zombie can't move onto the screen if we don't position it, but further up
`in the function there is a position statement which occurs each time it completes
`the trip into the next square. These subtle optimisations make a massive difference
`when dealing with thousands of objects
if Zombie(zom).inscreen = 1 and CamZoomInRange()
position object Zombie(zom).obj,Zombie(zom).x#,Const_YPosLiving,Zombie(zom).z#
endif
endfunction
`Resets zombies direction and movement values
function SetNewZombieDirection(zom,dx,dz)
`If no direction has been passed in, default to walking right
if dx = 0 and dz = 0 then dx = 1
Zombie(zom).direcx = dx
Zombie(zom).direcz = dz
Zombie(zom).direcxcount = abs(Zombie(zom).direcx)
Zombie(zom).direczcount = abs(Zombie(zom).direcz)
endfunction
`Resets humans direction and movement values
function SetNewHumanDirection(hum,dx,dz)
`If no direction has been passed in, default to walking right
if dx = 0 and dz = 0 then dx = 1
Human(hum).direcx = dx
Human(hum).direcz = dz
Human(hum).direcxcount = abs(Human(hum).direcx)
Human(hum).direczcount = abs(Human(hum).direcz)
endfunction
`Process all humans
function ProcessHumans()
for hum = 1 to Const_MaxHumans
if Human(hum).alive = 1
ProcessHuman(hum)
endif
next hum
endfunction
`Main human processing function - works similar to zombie function.
function ProcessHuman(hum)
`This Human is waiting for some reason, so don't do any movement stuff
if Human(hum).waiting > 0
dec Human(hum).waiting,1
exitfunction
endif
`If this human is infected, we need to do some processing
if Human(hum).infection > -1
`Count infection down to 0, which is zombification time
dec Human(hum).infection,1
`This human will now turn into a zombie
if Human(hum).infection = 0
ZombifyHuman(hum)
exitfunction
endif
endif
`Human is ready to look for the closest zombie
if Human(hum).zombiesearch = 0
`Default this human as not legging it from a zombie
Human(hum).fleeing = 0
Human(hum).zombiesearch = Const_HumanZombieSearch
`Will be 0 if no zombie was found, or the zombie ID
zom = ClassInRange(Human(hum).x,Human(hum).z,Const_ZombieClassID,Const_HumanSearchRange)
if zom > 0
`A zombie was found. If the human is skillful enough he will
`attack it, otherwise he will run away from it, so calculate
`human movement direction values accordingly
if Human(hum).skill < Const_AttackZombieSkillThreshhold
Human(hum).targetzom = zom
SetNewHumanDirection(hum,Zombie(zom).x - Human(hum).x,Zombie(zom).z - Human(hum).z)
else
SetNewHumanDirection(hum,Human(hum).x - Zombie(zom).x,Human(hum).z - Zombie(zom).z)
Human(hum).fleeing = 1
endif
endif
else
dec Human(hum).zombiesearch,1
endif
`Directional movement
`If we're on our destination square, find a new one (consider threshhold)
if abs((Human(hum).x*1.0) - Human(hum).x#) < Human(hum).speeddiag#
if abs((Human(hum).z*1.0) - Human(hum).z#) < Human(hum).speeddiag#
`Position human object
Human(hum).x# = Human(hum).x
Human(hum).z# = Human(hum).z
position object Human(hum).obj,Human(hum).x#,Const_YPosLiving,Human(hum).z#
`Deal with fatigue
if Human(hum).inbuilding > 0
`If the human is inside a building, his fatigue is restored
Human(hum).fatigue = Human(hum).maxFatigue
else
`If the human is outside, his fatigue is reduced
if Human(hum).fatigue > 0
dec Human(hum).fatigue,1
endif
endif
`If the human has a zombie in his sights and is targeting
`it for serious head bludgeoning ...
if Human(hum).targetzom > 0
targ = Human(hum).targetzom
`Run at zombie like a craven fool
SetNewHumanDirection(hum,Zombie(targ).x - Human(hum).x,Zombie(targ).z - Human(hum).z)
`If zombie is in range, attempt to batter it
if abs(Human(hum).x - Zombie(targ).x) < 2 and abs(Human(hum).z - Zombie(targ).z) < 2
`Random chance to kill it
if rnd(Human(hum).skill) = 0
KillZombie(targ)
endif
endif
else
`Human doesn't have a target, so Human will wonder around randomly
`There is a random chance for it to change direction
if rnd(Const_HumanDirectionChange) = 0
`Human is changing direction, so work out new direction values
SetNewHumanDirection(hum,-10 + rnd(20),-10 + rnd(20))
endif
`As the human has no target zombie to kill, it'll wait around
`unless it's legging it from one
`if Human(hum).fleeing = 0
` Human(hum).waiting = 1+rnd(Const_MaxHumanWaitTime)
` exitfunction
`endif
endif
`Find next square to move to
DirectionalMove(Human(hum).direcx,Human(hum).direcz,Human(hum).direcxcount,Human(hum).direczcount)
newx = Human(hum).x + DMove.xmove
newz = Human(hum).z + DMove.zmove
`The square it wants to move to is occupied, so we'll have to search
`for the next best square and move to that instead
sqrMove = CanMoveToSquare(newx,newz,Const_HumanClassID)
if sqrMove = 0
`SQUARE IS OCCUPIED
if FindNextBestSquare(DMove.angle,Human(hum).x,Human(hum).z,Const_HumanClassID) = TRUE
newx = Human(hum).x + DMove.xmove
newz = Human(hum).z + DMove.zmove
else
`Theres no alternative square, so just wait for a bit
Human(hum).waiting = 1+rnd(Const_MaxHumanWaitTime)
exitfunction
endif
else
if sqrMove = -1
`SQUARE IS OFF THE MAP
`We'll make the Human head back towards the center of the
`map to avoid them bunching at the map border.
SetNewHumanDirection(hum,Const_MapWidth/2-Human(hum).x,Const_MapHeight/2-Human(hum).z)
`We'll skip this turn since the square the Human was going
`to move to before setting a new direction was illegal
exitfunction
endif
endif
Human(hum).direcxcount = DMove.xcount
Human(hum).direczcount = DMove.zcount
oldx = Human(hum).x
oldz = Human(hum).z
Human(hum).x = newx
Human(hum).z = newz
Human(hum).angle = DMove.angle
Square(oldx,oldz).occupied = 0
Square(oldx,oldz).class = 0
Square(newx,newz).occupied = hum
Square(newx,newz).class = Const_HumanClassID
endif
endif
if Human(hum).inscreen = 1 and CamZoomInRange()
yrotate object Human(hum).obj,curveangle(Human(hum).angle,object angle y(Human(hum).obj),Const_HumanTurnAnimationSpeed)
endif
`Move to our destination square
ang = Human(hum).angle
`Choose the speed we're going to use based on the human's fatigue
if Human(hum).fatigue = 0
speed# = Human(hum).tiredspeed#
speeddiag# = Human(hum).tiredspeeddiag#
else
speed# = Human(hum).speed#
speeddiag# = Human(hum).speeddiag#
endif
if ang = 0
inc Human(hum).z#, speed#
else
if ang = 45
inc Human(hum).z#, speeddiag#
inc Human(hum).x#, speeddiag#
else
if ang = 90
inc Human(hum).x#, speed#
else
if ang = 135
inc Human(hum).x#, speeddiag#
dec Human(hum).z#, speeddiag#
else
if ang = 180
dec Human(hum).z#, speed#
else
if ang = 225
dec Human(hum).x#, speeddiag#
dec Human(hum).z#, speeddiag#
else
if ang = 270
dec Human(hum).x#, speed#
else
if ang = 315
dec Human(hum).x#, speeddiag#
inc Human(hum).z#, speeddiag#
endif:endif:endif:endif:endif:endif:endif:endif
if Human(hum).inscreen = 1 and CamZoomInRange()
position object Human(hum).obj,Human(hum).x#,Const_YPosLiving,Human(hum).z#
endif
endfunction
`Returns 1 if the square can be moved to, 0 if it's occuped and -1 if its off the map
function CanMoveToSquare(x,z,class)
if InMapRange(x,z)
`If its occupied, return 0
if Square(x,z).occupied > 0
exitfunction 0
else
`If there is a building at this square ...
if Square(x,z).building > 0
b = Square(x,z).building
select class
case Const_HumanClassID
exitfunction 1
endcase
case Const_ZombieClassID
`If the door is open, the zombie can go inside, else it cant
if Building(b).doorOpen = 1
exitfunction 1
else
exitfunction 0
endif
endcase
endselect
else
exitfunction 1
endif
endif
else
exitfunction -1
endif
endfunction 0
`Given a coordinate, a class to look for and a search range
`this function searches outwards from the central point and
`returns the closest instance found, using square distancing.
function ClassInRange(x,z,class,range)
for r=1 to range
`Search top line
sz = z+r
if sz < Const_MapHeight
minx = x-r: if minx < 0 then minx = 0
maxx = x+r: if maxx > Const_MapWidth then maxx = Const_MapWidth
for sx=minx to maxx
if Square(sx,sz).class = class
hum = Square(sx,sz).occupied
exitfunction hum
endif
next sx
endif
`Search bottom line
sz = z-r
if sz > 0
minx = x-r: if minx < 0 then minx = 0
maxx = x+r: if maxx > Const_MapWidth then maxx = Const_MapWidth
for sx=minx to maxx
if Square(sx,sz).class = class
hum = Square(sx,sz).occupied
exitfunction hum
endif
next sx
endif
`Search left line
sx = x-r
if sx > 0
minz = z-r+1: if minz < 0 then minz = 0
maxz = z+r-1: if maxz > Const_MapHeight then maxz = Const_MapHeight
for sz=minz to maxz
if Square(sx,sz).class = class
hum = Square(sx,sz).occupied
exitfunction hum
endif
next sz
endif
`Search right line
sx = x+r
if sx < Const_MapWidth
minz = z-r+1: if minz < 0 then minz = 0
maxz = z+r-1: if maxz > Const_MapHeight then maxz = Const_MapHeight
for sz=minz to maxz
if Square(sx,sz).class = class
hum = Square(sx,sz).occupied
exitfunction hum
endif
next sz
endif
next r
endfunction 0
`Moves the camera about with the mouse smoothly
function ControlCamera()
if mouseclick()
endif
Camera.tendToY# = Camera.tendToY# - (mousemovez() * Const_CameraZoomSensitivity)
Camera.tendToX# = Camera.tendToX# + (mousemovex() * Const_CameraScrollSensitivity)
Camera.tendToZ# = Camera.tendToZ# - (mousemovey() * Const_CameraScrollSensitivity)
if Camera.tendToY# < 10 then Camera.tendToY# = 10
if Camera.tendToY# > 200 then Camera.tendToY# = 200
if Camera.tendToX# < 0 then Camera.tendToX# = 0
if Camera.tendToX# > Const_MapWidth then Camera.tendToX# = Const_MapWidth
if Camera.tendToZ# < 0 then Camera.tendToZ# = 0
if Camera.tendToZ# > Const_MapHeight then Camera.tendToZ# = Const_MapHeight
camx# = Curvevalue(Camera.tendToX#,Camera position x(),10)
camy# = Curvevalue(Camera.tendToY#,Camera position y(),10)
camz# = Curvevalue(Camera.tendToZ#,Camera position Z(),10)
position camera camx#,camy#,camz#
endfunction
`Simple data output function
function OutputData()
zomcount = 0
for z=1 to Const_MaxZombies
if Zombie(z).alive = 1
inc zomcount,1
endif
next z
humcount = 0
infcount = 0
for h=1 to Const_MaxHumans
if Human(h).alive = 1
if Human(h).infection = -1
inc humcount,1
endif
if Human(h).infection > 0
inc infcount,1
endif
endif
next z
set text font "Arial"
set text size 36
text 1,1,"Zombies: " + str$(zomcount)
text 1,30,"Humans: " + str$(humcount)
text 1,59,"Infected: " + str$(infcount)
text 1,88,"FPS: " + str$(screen fps())
endfunction
`Kills the passed in zombie
function KillZombie(targ)
Zombie(targ).alive = 0
Square(Zombie(targ).x,Zombie(targ).z).occupied = 0
Square(Zombie(targ).x,Zombie(targ).z).class = 0
`Kill zombie and create corpse
newobj = Zombie(targ).obj
dedx# = object position x(Zombie(targ).obj)
dedz# = object position z(Zombie(targ).obj)
delete object newobj
instance object newobj,ObjBase.dedbase
yrotate object newobj,rnd(359)
position object newobj,dedx#,Const_YPosCorpse,dedz#
set sound speed 3,16000+rnd(12000)
play sound 3
`Ensure all humans targetting this zombie stop chasing it
for hum = 1 to Const_MaxHumans
if Human(hum).targetzom = targ
Human(hum).targetzom = 0
endif
next hum
endfunction
`Zombifies the passed in human
function ZombifyHuman(hum)
Human(hum).alive = 0
delete object Human(hum).obj
Square(Human(hum).x,Human(hum).z).occupied = 0
Square(Human(hum).x,Human(hum).z).class = 0
MakeZombie(Human(hum).x,Human(hum).z)
set sound speed 1,16000+rnd(12000)
play sound 1
endfunction
`Infects a human with the zombie virus, or accelerates
`the humans death if it already has the virus
function InfectHuman(targ)
if Human(targ).infection = -1
Human(targ).infection = Const_InfectionStartLife
delete object Human(targ).obj
instance object Human(targ).obj,ObjBase.infwalk
position object Human(targ).obj,Human(targ).x#,0,Human(targ).z#
else
`Double the speed of infection for each bite
Human(targ).infection = Human(targ).infection / 2.0
endif
set sound speed 2,16000+rnd(12000)
play sound 2
endfunction
`Returns 1 if the passed coordinate is within the map bounds,
`otherwise returns 0
function InMapRange(x,z)
if x > -1
if x < Const_MapWidth + 1
if z > -1
if z < Const_MapHeight + 1
exitfunction 1
endif
endif
endif
endif
endfunction 0
`******************************************************************
`INITIALISATION AND CREATION FUNCTIONS
`Initialise global variables and data structures and system settings
function InitialiseSystemAndVariables()
dim Zombie(Const_MaxZombies) as ZombieType
dim Human(Const_MaxHumans) as HumanType
dim Building(Const_MaxBuildings) as BuildingType
dim Square(Const_MapWidth,Const_MapHeight) as SquareType
global ImagePositions as ImagePositionsType
global DMove as DMoveType
global ObjBase as ObjBaseType
dim AC(6) as word
global Camera as CameraType
global Anim as AnimType
set display mode 1024,768,32
sync on
sync rate 30
autocam off
color backdrop 0
position camera Const_MapWidth/2,40,Const_MapHeight/2
Camera.tendToX# = Camera position x()
Camera.tendToY# = Camera position y()
Camera.tendToZ# = Camera position z()
xrotate camera 90
set ambient light 30
disable escapekey
hide mouse
endfunction
`Makes a textured plain for the land
function MakeLand()
landobj = GetNextFreeObject()
make object plain landobj,Const_MapWidth+2,Const_MapHeight+2
texture object landobj,ImagePositions.floortex
scale object texture landobj,Const_MapWidth/50,Const_MapHeight/50
xrotate object landobj,270
` color object landobj,rgb(20,120,20)
position object landobj,Const_MapWidth/2,Const_YPosMap,Const_MapHeight/2
endfunction
`Load in images and sounds
function LoadMedia()
`This is the global transparent colour
set image colorkey 255,128,255
`Zombie image
ImagePositions.zomimage = GetNextFreeImage()
load image "images\zombie1.bmp",ImagePositions.zomimage
`Human image
ImagePositions.humimage = GetNextFreeImage()
load image "images\human2.bmp",ImagePositions.humimage
`Infected image
ImagePositions.infimage = GetNextFreeImage()
load image "images\infected.bmp",ImagePositions.infimage
`Dead image
ImagePositions.dedimage = GetNextFreeImage()
load image "images\zomdead.bmp",ImagePositions.dedimage
`Floor
ImagePositions.floortex = GetNextFreeImage()
load image "floor.bmp",ImagePositions.floortex
ImagePositions.house1image = GetNextFreeImage()
load image "images\house1.bmp",ImagePositions.house1image
load sound "zomchange.wav",1
load sound "zominfect.wav",2
load sound "zomkilled.wav",3
endfunction
`This function makes the base objects that all other objects are spawned
`from using the instance object command.
function MakeBaseObjects()
`Base plain
obj = GetNextFreeObject()
ObjBase.plain = obj
make object plain obj,1,1
xrotate object obj,270
fix object pivot obj
set object texture obj,0,0
set object transparency obj,4
exclude object on obj
`Make zombie base
obj = GetNextFreeObject()
ObjBase.zomwalk = obj
clone object obj,ObjBase.plain
texture object obj,ImagePositions.zomimage
SetObjectFrame(obj,0)
exclude object on obj
`ObjBase.humbase
obj = GetNextFreeObject()
ObjBase.humwalk = obj
clone object obj,ObjBase.plain
texture object obj,ImagePositions.humimage
SetObjectFrame(obj,0)
exclude object on obj
`ObjBase.infbase
obj = GetNextFreeObject()
ObjBase.infwalk = obj
clone object obj,ObjBase.plain
texture object obj,ImagePositions.infimage
SetObjectFrame(obj,0)
exclude object on obj
`ObjBase.dedbase
obj = GetNextFreeObject()
ObjBase.dedbase = obj
clone object obj,ObjBase.plain
texture object obj,ImagePositions.dedimage
exclude object on obj
`House 1 base
obj = GetNextFreeObject()
ObjBase.house1base = obj
make object plain obj,4,4
xrotate object obj,270
fix object pivot obj
set object texture obj,0,0
set object transparency obj,4
texture object obj,ImagePositions.house1image
exclude object on obj
endfunction
`Makes a zombie and places it at the specified coordinates
function MakeZombie(x,z)
`Exit if square is occupied or outside map boundary
if Square(x,z).occupied > 0 or x < 0 or z < 0 or x > Const_MapWidth or z > Const_MapHeight
exitfunction -1
endif
zom = GetNextFreeZombie()
`Exit if there is no free slot
if zom = -1 then exitfunction -1
obj = GetNextFreeObject()
Zombie(zom).alive = 1
Zombie(zom).obj = obj
Zombie(zom).x = x
Zombie(zom).z = z
Zombie(zom).x# = x
Zombie(zom).z# = z
Zombie(zom).speed# = (Const_MinZombieSpeed+rnd(Const_MaxZombieSpeed-Const_MinZombieSpeed))/20000.0
Zombie(zom).speeddiag# = sin(45)*Zombie(zom).speed#
Zombie(zom).skill = Const_MinZombieSkill+rnd(Const_MaxZombieSkill-Const_MinZombieSkill)
Zombie(zom).targetman = 0
Zombie(zom).targetbuilding = 0
Zombie(zom).humansearch = rnd(Const_ZombieHumanSearch)
`Randomise an initial walking direction
SetNewZombieDirection(zom,-10 + rnd(20),-10 + rnd(20))
instance object obj,ObjBase.zomwalk
position object obj,x,Const_YPosLiving,z
yrotate object obj,rnd(359)
`Set the square as occupied
Square(x,z).occupied = zom
Square(x,z).class = Const_ZombieClassID
endfunction 1
`This function makes a human at the passed coordinate
function MakeHuman(x,z)
`Exit of square is occupied or outside map boundary
if Square(x,z).occupied > 0 or x < 0 or z < 0 or x > Const_MapWidth or z > Const_MapHeight
exitfunction -1
endif
hum = GetNextFreeHuman()
`Exit if there is no free slot
if hum = -1 then exitfunction -1
obj = GetNextFreeObject()
Human(hum).alive = 1
Human(hum).obj = obj
Human(hum).x = x
Human(hum).z = z
Human(hum).x# = x
Human(hum).z# = z
Human(hum).speed# = (Const_MinHumanSpeed + rnd(Const_MaxHumanSpeed-Const_MinHumanSpeed))/20000.0
Human(hum).speeddiag# = sin(45)*Human(hum).speed#
Human(hum).tiredspeed# = Human(hum).speed# * 0.5
Human(hum).tiredspeeddiag# = Human(hum).speeddiag# * 0.5
Human(hum).skill = Const_MinHumanSkill+rnd(Const_MaxHumanSkill-Const_MinHumanSkill)
Human(hum).infection = -1
Human(hum).maxFatigue = Const_MinFatigue + rnd(Const_MaxFatigue-Const_MinFatigue)
Human(hum).fatigue = Human(hum).maxFatigue
Human(hum).targetbuilding = 0
`Randomize a direction for this human to walk in
SetNewHumanDirection(hum,-10 + rnd(20),-10 + rnd(20))
instance object obj,ObjBase.humwalk
position object obj,x,Const_YPosLiving,z
yrotate object obj,rnd(359)
Square(x,z).occupied = hum
Square(x,z).class = Const_HumanClassID
endfunction 1
`Makes a building of the passed type at the passed coordinates
function MakeBuilding(x,z,btype)
`If theres no free slot then exit
b = GetNextFreeBuilding()
if b = -1 then exitfunction -1
obj = GetNextFreeObject()
`Randomize building orientation and check appropriate number
`of squares are available. Calculate building placement at
`the same time since we already have a select case statement
`going
angle = rnd(3)*90
select btype
case 1
`Building type 1
if angle = 0
width = 5:height = 6
posx# = x+1.5: posz# = z+2.5
else
if angle = 90
width = 6:height = 5
posx# = x+2.5: posz# = z+1.5
else
if angle = 180
width = 5:height = 6
posx# = x+2.5: posz# = z+2.5
else
width = 6:height = 5
posx# = x+2.5: posz# = z+2.5
endif
endif
endif
endcase
endselect
`Search squares
for bx=x to x+width-1
for bz=z to z+height-1
`If any one of the squares is taken then we can't build
`this house, so someone will be poor and homeless and
`have to live on the street and eat out of trash cans
if Square(bx,bz).occupied > 0 or Square(bx,bz).building > 0
exitfunction -1
endif
next bz
next bx
`Squares are available so we're good to make the building
Building(b).exists = 1
Building(b).obj = obj
Building(b).btype = 1
if rnd(Const_DoorsOpenChance)=0
Building(b).doorOpen = 1
else
Building(b).doorOpen = 0
endif
Building(b).foodManDays = Const_BuildingMinFoodDays + rnd(Const_BuildingMaxFoodDays-Const_BuildingMinFoodDays)
Building(b).barricade = 0
Building(b).x = x
Building(b).z = z
instance object obj,ObjBase.house1base
yrotate object obj,angle
position object obj,posx#,Const_YPosBuilding,posz#
select btype
case 1
`Outhouse type building, so no food multiplier
endcase
endselect
`Flag squares as occupied with this building
for bx=x to x+width-1
for bz=z to z+height-1
Square(bx,bz).building = b
next bz
next bx
endfunction 1
`Randomly makes a pack of zombies
function MakeZombiePack(x,z,num,range)
for zom=1 to num
zx = x-(range/2)+rnd(range)
zz = z-(range/2)+rnd(range)
MakeZombie(zx,zz)
next zom
endfunction
`Randomly makes a pack of humans
function MakeHumanPack(x,z,num,range)
for hum=1 to num
zx = x-(range/2)+rnd(range)
zz = z-(range/2)+rnd(range)
MakeHuman(zx,zz)
next hum
endfunction
Here's a zip with some media to work from and the dba in the right place:
ZombieSim.zip
I've had my fun with it, so hope someone else can make use of it. Feel free to use the code anywhere, or add any updates here.