Code archives/Miscellaneous/Project PLASMA FPS 2004: Bot.bb
This code has been declared by its author to be Public Domain code.
Download source code
| |||||
WARNING!!! This is code is still under development and not yet suitable for use. Once complete I will remove this warning.The Bot Module is designed to support the following requirements: 1. Modular Code Design. Currently there are three objects in the Bot.bb file: Bot, Waypoint, Path. 2. Non-geometry Properties stored in *.csv comma delimited file. I will write the Level Loader portion for bots. 3. Single or MultiMesh geometry w/Bone animation stored in *.b3d format. Geometry can be loaded from within the level or independently. Standard Blitz Animation. 4. Finite State Machine AI Model a) Bots must be able to 'see' and 'hear' players and or other bots within their field of view/range. b) Bots must be able to attack, evade, find ammo, and find health dependent on their health and ammo status. c) Bots must be able to navigate indoor/outdoor geometry avoiding collision with static and moving objects. Finite State Machine The function of the FSM for bots is divided into 3 functions: Sensor, Reaction, and Motor. A Sensor is a conditional check (linepicks, health compares, etc) that compares the bot against the environment setting its AI state. A Reaction is a 'action function' selected dependent on the bots ai state. The Motor controls movement and animation dependent on the Reaction. The PathFinding System The Pathfinding System can support the use of Precalculated PathTables, Real-time A* Multi-tier Pathfinding, CollisionMaps w/ variable Terrain Cost for Realtime pathfinding, and Precalculated and Realtime Hybrid. Precalculated PathTables offer the greatest performance advantage and are ideal for indoor level maps. Realtime Pathfinding is ideal for avoiding moving obstacles. Summary of functions: Last Update 02/03/04 Check out the Wip Zip for demos and more code! | |||||
;============================
;WAYPOINT
;============================
Const WAYPOINT_MAX=2048
Dim waypointId.waypoint(WAYPOINT_MAX)
Global waypointIndex.stack=stackIndexCreate(WAYPOINT_MAX)
Global waypointAvail.stack=stackIndexCreate(WAYPOINT_MAX)
Global waypoint0.waypoint=New waypoint
Global waypoints%
Type waypoint
Field id%
Field typeid%
Field parent.waypoint
Field e% ;terrain cost
Field f% ;fcost
Field g% ;parent+cost
Field h% ;heuristic manhattan
Field state%
Field position.vector
Field entity
End Type
Function waypointStart()
entity%=CreateCube()
HideEntity entity%
For loop = 1 To WAYPOINT_MAX
this.waypoint=waypointNew()
this\entity%=CopyEntity(entity%)
EntityPickMode this\entity,1,False
ScaleEntity this\entity%,.1,.1,.1
HideEntity this\entity%
Next
DebugLog("Waypoint Initialized ["+Str(WAYPOINT_MAX)+"]")
End Function
Function waypointStop()
For this.waypoint=Each waypoint
waypointDelete(this)
Next
End Function
Function waypointNew.waypoint()
this.waypoint=New waypoint
this\id%=0
this\typeid%=0
this\e%=0
this\f%=0
this\g%=0
this\h%=0
this\state%=0
this\position.vector=vectorNew()
this\id%=StackPop(waypointIndex.stack)
waypointId(this\id)=this
Return this
End Function
Function waypointDelete(this.waypoint)
waypointId(this\id)=Null
StackPush(waypointIndex.stack,this\id%)
vectorDelete(this\position.vector)
this\state%=0
Delete this
End Function
Function waypointRead.waypoint(file)
this.waypoint=New waypoint
this\id%=ReadInt(file)
this\typeid%=ReadInt(file)
;this\parent.waypoint=waypointRead(file)
this\e%=ReadInt(file)
this\f%=ReadInt(file)
this\g%=ReadInt(file)
this\h%=ReadInt(file)
this\state%=ReadByte(file)
this\position.vector=vectorRead(file)
Return this
End Function
Function waypointWrite(file,this.waypoint)
WriteInt(file,this\id%)
WriteInt(file,this\typeid%)
;waypointWrite(file,this\parent.waypoint)
WriteInt(file,this\e%)
WriteInt(file,this\f%)
WriteInt(file,this\g%)
WriteInt(file,this\h%)
WriteByte(file,this\state%)
vectorWrite(file,this\position.vector)
End Function
Function waypointSave(filename$="Default")
file=WriteFile(filename$+".waypoint")
For this.waypoint= Each waypoint
waypointWrite(file,this)
Next
CloseFile(file)
End Function
Function waypointOpen(filename$="Default")
file=ReadFile(filename+".waypoint")
Repeat
waypointRead(file)
Until Eof(file)
CloseFile(file)
End Function
Function waypointCopy.waypoint(this.waypoint)
copy.waypoint=New waypoint
copy\id%=this\id%
copy\typeid%=this\typeid%
copy\parent.waypoint=waypointCopy(this\parent.waypoint)
copy\e%=this\e%
copy\f%=this\f%
copy\g%=this\g%
copy\h%=this\h%
copy\state%=this\state%
copy\position.vector=vectorCopy(this\position.vector)
Return copy
End Function
Function waypointMimic(mimic.waypoint,this.waypoint)
mimic\id%=this\id%
mimic\typeid%=this\typeid%
waypointMimic(mimic\parent.waypoint,this\parent.waypoint)
mimic\e%=this\e%
mimic\f%=this\f%
mimic\g%=this\g%
mimic\h%=this\h%
mimic\state%=this\state%
vectorMimic(mimic\position.vector,this\position.vector)
End Function
Function waypointCreate.waypoint(typeid%,parent.waypoint,g%,startx#,starty#,startz#,goalx#,goaly#,goalz#)
this.waypoint=waypointNew()
this\typeid%=typeid%
this\parent.waypoint=parent
this\position\x#=startx#
this\position\y#=starty#
this\position\z#=startz#
this\g%=g%+this\parent\g%;find g
this\h%=10*(Abs(goalx#-this\position\x#)+Abs(goalz#-this\position\z#));find h (manhattan)
;this\h%=10*(Abs(goalx#-this\position\x#)+Abs(goaly#-this\position\y#)+Abs(goalz#-this\position\z#));find h (manhattan)
this\f%=this\g%+this\h%;find f%
Return this
End Function
Function waypointSet(this.waypoint,typeid%,parent.waypoint,g%,startx#,starty#,startz#,goalx#,goaly#,goalz#)
this\typeid%=typeid%
this\parent.waypoint=parent.waypoint
this\position\x#=startx#
this\position\y#=starty#
this\position\z#=startz#
this\g%=g%+this\parent\g%;find g
this\h%=10*(Abs(goalx#-this\position\x#)+Abs(goalz#-this\position\z#));find h (manhattan)
;this\h%=10*(Abs(goalx#-this\position\x#)+Abs(goaly#-this\position\y#)+Abs(goalz#-this\position\z#));find h (manhattan)
this\f%=this\g%+this\h%;find f%
this\state%=0;set/reset
End Function
;============================
;PATH
;============================
Const PATH_MAX=255
Dim pathId.path(PATH_MAX)
Global pathIndex.stack=stackIndexCreate(PATH_MAX)
Global pathAvail.stack=stackIndexCreate(PATH_MAX);return id of available queue
Dim pathTable(WAYPOINT_MAX,WAYPOINT_MAX)
Dim pathCollisionMap(MAP_WIDTH,MAP_HEIGHT,MAP_DEPTH)
;create open & close stack list
;Global pathopen.queue=queueCreate()
;Global pathclose.queue=queueCreate()
Type path
Field id%
Field typeid%
Field waypoints%
Field route.stack
Field state% ;1=building, 2=destroying ;process slicing
Field waypoint.waypoint
Field goal.vector
Field radius# ;tier worker radius
Field distance# ;tier increment
Field worker.worker[8]
Field waypointadjacentg[8]
Field open.queue
Field close.queue
Field waypointadjacent
End Type
Function pathStop()
For this.path=Each path
pathDelete(this)
Next
End Function
Function pathNew.path()
this.path=New path
this\id%=0
this\typeid%=0
this\waypoints%=0
this\id%=StackPop(pathIndex.stack)
pathId(this\id)=this
Return this
End Function
Function pathDelete(this.path)
pathId(this\id)=Null
StackPush(pathIndex.stack,this\id%)
stackDelete(this\route)
Delete this
End Function
Function pathUpdate()
For this.path=Each path
Select this\state%
;Path Building
Case 1 pathWaypointAdjacentBuild(this)
Case 12 pathWaypointAdjacentCollisionBuild(this)
Case 13 pathWaypointNextBuild(this)
;Path Created
Case 2 pathRouteBuild(this)
;Path Destroy
Case 3 pathOpenClean(this)
Case 4 pathCloseClean(this)
Case 5 pathDestroy(this)
End Select
Next
End Function
;A* path generation
;http://www.gamedev.net/reference/articles/article2003.asp
;http://www.policyalmanac.org/games/twoTiered.htm
Function pathCreate.path(pathstart.vector,pathgoal.vector,pathdistance#=2.0,pathradius#=1.0) ;two-tier A* reduce distance and worker radius for finer searches
this.path=pathNew()
this\open.queue=queueId(stackPop(queueAvail)) ;get available queues id
this\close.queue=queueId(stackPop(queueAvail))
this\goal.vector=pathgoal.vector
this\distance#=pathdistance#
this\radius#=pathradius#
this\route.stack=stackCreate(127)
;create startnode at start vector
this\waypoint.waypoint=waypointId(stackPop(waypointAvail))
waypointSet(this\waypoint,1,waypoint0,0,pathstart\x#,pathstart\y#,pathstart\z#,pathgoal\x#,pathgoal\y#,pathgoal\z#)
;place waypointstartid id close queue
queuePush(this\close,this\waypoint\f%,this\waypoint\id%)
this\waypointadjacent=8
pathWaypointAdjacentBuild(this)
Return this
End Function
Function pathWaypointAdjacentBuild(this.path)
this\state%=12
PositionEntity this\waypoint\entity%,this\waypoint\position\x#,this\waypoint\position\y#,this\waypoint\position\z#:EntityColor this\waypoint\entity%,0,255,0:EntityAlpha this\waypoint\entity%,.9:ShowEntity this\waypoint\entity%
;For Each of these squares, save point A as its "parent square"
;Look at all the reachable or walkable squares adjacent to the starting point,
;Move worker in 8 adjacent positions X#,Z# (Y# influenced by gravity)
;other configurations: 3-8 star
Restore waypointadjacent
For loop = 1 To this\waypointadjacent
this\worker.worker[loop]=workerID(stackPop(workerAvail))
EntityRadius this\worker[loop]\entity%,this\radius#
ScaleEntity worker\entity%,this\radius#,this\radius#,this\radius#
;Uses waypointadjacent dataset for adjacent waypoint position & cost
Read waypointadjacentx%,waypointadjacentz%,waypointadjacentg%
;adjust adjacent positions to parent
this\worker[loop]\vector\x#=this\waypoint\position\x#+(this\distance#*waypointadjacentx%)
this\worker[loop]\vector\z#=this\waypoint\position\z#+(this\distance#*waypointadjacentz%)
;floor collision check/gravity adjust this\worker[loop]\vector\y#=waypointgravityoffsety#
;navigator wall collsion check
this\waypointadjacentg%[loop]=waypointadjacentg%
PositionEntity this\worker%[loop]\entity%,this\worker[loop]\vector\x#,this\worker[loop]\vector\y#,this\worker[loop]\vector\z#
DebugLog "pathxy workerentity["+Str(this\worker[loop]\entity%)+"]="+Str(EntityX(this\worker[loop]\entity%))+","+Str(EntityY(this\worker[loop]\entity%))+","+Str(EntityZ(this\worker[loop]\entity%))
Next
End Function
Function pathWaypointAdjacentBuild2(this.path)
this\state%=12
;testing
PositionEntity this\waypoint\entity%,this\waypoint\position\x#,this\waypoint\position\y#,this\waypoint\position\z#:EntityColor this\waypoint\entity%,0,255,0:EntityAlpha this\waypoint\entity%,.9:ShowEntity this\waypoint\entity%
;For Each of these squares, save point A as its "parent square"
;Look at all the reachable or walkable squares adjacent to the starting point,
;Move worker in 8 adjacent positions X#,Z# (Y# influenced by gravity)
;other configurations: 3-8 star
Restore waypointadjacentang
For loop = 1 To this\waypointadjacent
this\worker.worker[loop]=workerID(stackPop(workerAvail))
EntityRadius this\worker[loop]\entity%,this\radius#
;ScaleEntity worker\entity%,this\radius#,this\radius#,this\radius#
;Uses waypointadjacent dataset for adjacent waypoint position & cost
Read waypointadjacentangle#,waypointadjacentg%
;adjust adjacent positions to parent
this\worker[loop]\vector\x#=this\waypoint\position\x#+Cos2#(waypointadjacentangle#)*this\distance#
this\worker[loop]\vector\z#=this\waypoint\position\z#+Sin2#(waypointadjacentangle#)*this\distance#
;floor collision check/gravity adjust this\worker[loop]\vector\y#=waypointgravityoffsety#
;navigator wall collsion check
this\waypointadjacentg%[loop]=waypointadjacentg%
PositionEntity this\worker%[loop]\entity%,this\worker[loop]\vector\x#,this\worker[loop]\vector\y#,this\worker[loop]\vector\z#
Next
End Function
Function pathWaypointAdjacentCollisionBuild(this.path)
this\state%=13
For loop = 1 To this\waypointadjacent
DebugLog "collision workerentity["+Str(this\worker[loop]\entity%)+"]="+Str(EntityX(this\worker[loop]\entity%))+","+Str(EntityY(this\worker[loop]\entity%))+","+Str(EntityZ(this\worker[loop]\entity%))
;ignore illegal terrain
If Not pathNavigatorCollision2(this\worker%[loop],level\map) ;pathCollisionMap(pathx#,pathy#,pathz#)) ;collision/ collision map array
;ignore illegal positions on the closed list
If pathWaypointInQueue(this\close,EntityX(this\worker[loop]\entity%),EntityY(this\worker[loop]\entity%),EntityZ(this\worker[loop]\entity%))=Null ;position does not match a waypoint\position on the close list
;If an adjacent position is already on the open list, check To see If this path To that square is a better one.
waypoint.waypoint=pathWaypointInQueue(this\open,EntityX(this\worker[loop]\entity%),EntityY(this\worker[loop]\entity%),EntityZ(this\worker[loop]\entity%))
;In other words, check To see If the G score For that square is Lower If we use the current square
;If Not, don't do anything.
If waypoint<>Null
;On the other hand, if the G cost of the new path is lower, change the parent of the adjacent
;node to the selected square (in the diagram above, change the direction of the pointer
;to point at the selected square).
waypointnewg%=this\waypoint\g%+this\waypointadjacentg%[loop]
If waypointnewg%<waypoint\g%
waypoint\parent=this\waypoint
;Finally, recalculate both the F And G scores of that square.
waypoint\g%=waypointnewg%
waypoint\f%=waypoint\g%+waypoint\h%
;resort
EndIf
Else
;new waypoint
waypoint.waypoint=waypointId(stackPop(waypointAvail))
waypointSet(waypoint,1,this\waypoint,this\waypointadjacentg%[loop],EntityX(this\worker[loop]\entity%),EntityY(this\worker[loop]\entity%),EntityZ(this\worker[loop]\entity%),this\goal\x#,this\goal\y#,this\goal\z#)
;Add them To the open list, too.
queuePush(this\open,waypoint\f%,waypoint\id%)
;testing
PositionEntity waypoint\entity%,waypoint\position\x#,waypoint\position\y#,waypoint\position\z#:EntityColor waypoint\entity%,255,255,0:EntityAlpha waypoint\entity%,.9:ShowEntity waypoint\entity%
EndIf
EndIf
Else ;testing to be removed?
navigator%=CreateSphere(8)
ScaleEntity navigator%,this\radius#,this\radius#,this\radius#
PositionEntity navigator%,EntityX(this\worker[loop]\entity%),EntityY(this\worker[loop]\entity%),EntityZ(this\worker[loop]\entity%)
EntityAlpha navigator%,.5
RenderWorld()
Flip()
EndIf
stackPush(workerAvail,this\worker[loop]\id)
Next
End Function
Function pathWaypointNextBuild(this.path)
this\state%=1
;to continue the search
;we simply choose the waypoint with the lowest F score square from all those that are on the open list
;Drop it from the open list And add it To the closed list.
If Not this\open\queueitems RuntimeError("No items in open queue | collisioncount["+Str(entitycollisioncount%)+"]") ;testing
EntityColor this\waypoint\entity%,0,0,255 ;testing
this\waypoint=waypointId(queuePop(this\open))
queuePush(this\close,this\waypoint\f%,this\waypoint\id%)
EntityColor this\waypoint\entity,0,255,255;testing
entitycollisioncount=reset
;path found
; If this\waypoint\h%=0 this\state%=2
If this\waypoint\position\x#<this\goal\x#+this\distance# And this\waypoint\position\x#>this\goal\x#-this\distance#
; ;If this\waypoint\position\y#<this\goal\y#+this\distance# And this\waypoint\position\y#>this\goal\y#-this\distance#; omit for gravity and collision
If this\waypoint\position\z#<this\goal\z#+this\distance# And this\waypoint\position\z#>this\goal\z#-this\distance#
this\state%=2;Path Route
EndIf
; ;EndIf
EndIf
End Function
Function pathNavigatorCollision%(worker.worker,typeofentity%);perform non stop collision detection
DebugLog("worker\entity["+Str(worker\entity%)+"]="+Str$(worker\collision%)+","+Str(EntityX(worker\entity%))+","+Str(EntityY(worker\entity%))+","+Str(EntityZ(worker\entity%)))
worker\collision%=EntityCollided(worker\entity%,typeofentity%)
If worker\collision%
ResetEntity worker\entity%
entitycollisioncount%=entitycollisioncount%+1 ;testing
EndIf
Return worker\collision%
End Function
Function pathNavigatorCollision2%(worker.worker,typeofentity%);perform non stop collision detection
DebugLog("worker\entity["+Str(worker\entity%)+"]="+Str$(worker\collision%)+","+Str(EntityX(worker\entity%))+","+Str(EntityY(worker\entity%))+","+Str(EntityZ(worker\entity%)))
worker\collision%=MeshesIntersect(worker\entity%,typeofentity%)
If worker\collision% entitycollisioncount%=entitycollisioncount%+1 ;testing
Return worker\collision%
End Function
Global waypointincqueuecount,waypointincqueuecountmax ;testing
Function pathWaypointInQueue.waypoint(queue.queue,x#,y#,z#)
For loop = 1 To queue\queueitems%
waypointincqueuecount=loop;testing
waypoint.waypoint=waypointId(queue\queueitem[loop]\dat%)
If waypoint=Null Return waypoint
If waypoint\position\x#=x#
;If waypoint\position\y#=y#
If waypoint\position\z#=z#
Return waypoint
EndIf
;EndIf
EndIf
Next
End Function
Function pathOpenClean(this.path)
waypoint.waypoint=waypointId(queuePopLast(this\open))
If waypoint<>Null
stackPush(waypointAvail,waypoint\id%)
HideEntity waypoint\entity;testing
If Not this\open\queueitems%
stackPush(queueAvail,this\open\id%)
this\state%=4
EndIf
EndIf
End Function
Function pathCloseClean(this.path)
waypoint.waypoint=waypointId(queuePopLast(this\close))
If waypoint<>Null
EntityColor waypoint\entity,255,0,0;testing
If Not waypoint\state%
stackPush(waypointAvail,waypoint\id%)
HideEntity waypoint\entity;testing
EndIf
EndIf
If Not this\close\queueitems%
stackPush(queueAvail,this\close\id%)
this\state%=5
EndIf
End Function
Function pathDestroy(this.path)
If Not this\waypoints% pathDelete(this)
End Function
Function pathRouteBuild(this.path)
this\waypoint\state%=1
this\waypoints%=this\waypoints%+1
EntityColor this\waypoint\entity%,0,255,0;testing
stackPush(this\route,this\waypoint\id%)
this\waypoint=this\waypoint\parent
If Not this\waypoint\parent\id this\state%=3 ;path build complete
End Function
Function pathRouteNext(this.path)
this\waypoint=waypointId(stackPop(this\route))
DebugLog("pathRouteNext="+Str(this\waypoint\id))
stackPush(waypointAvail,this\waypoint\id%)
HideEntity this\waypoint\entity%;testing
this\waypoints%=this\waypoints%-1
End Function
Function pathCollisionMapLoad(filename$)
file=ReadFile(filename$+".collisionmap")
If file
While Not Eof(file)
; MAP_WIDTH MAP_HIEGHT MAP_WIDTH walkableflg
pathCollisionMap(ReadByte(file),ReadByte(file),ReadByte(file))=ReadByte(file)
Wend
CloseFile(file)
EndIf
End Function
Function pathCollisionMapBuild(this.path)
End Function
.waypointadjacent
;+14 +10 +14 ;diagonal movement g = 14
; \ | /
; \ | /
; \|/
;+10----0------+10 ;horizontal\vertical g = 10
; /|\
; / | \
; / | \
;+14 +10 +14
;... x, z, gcost
Data 0, 1, 10
Data -1, 0, 10
Data 0, -1, 10
Data 1, 0, 10
Data -1, 1, 14
Data -1, -1, 14
Data 1, -1, 14
Data 1, 1, 14
.waypointadjacent3D
;horizontal\vertical g = 10
;diagonal movement g = 14
;... x, y, z, gcost
Data 0, 0, 1, 10
Data -1, 0, 0, 10
Data 0, 0, -1, 10
Data 1, 0, 0, 10
Data -1, 0, 1, 14
Data -1, 0, -1, 14
Data 1, 0, -1, 14
Data 1, 0, 1, 14
Data 0, -1, 0, 10
Data 0, 1, 0, 10
Data 0, -1, 1, 14
Data -1, -1, 0, 14
Data 0, -1, -1, 14
Data 1, -1, 0, 14
Data 0, 1, 1, 14
Data -1, 1, 0, 14
Data 0, 1, -1, 14
Data 1, 1, 0, 14
Data -1, -1, 1, 14
Data -1, -1, -1, 14
Data 1, -1, -1, 14
Data 1, -1, 1, 14
Data -1, 1, 1, 14
Data -1, 1, -1, 14
Data 1, 1, -1, 14
Data 1, 1, 1, 14
.waypointadjacentang
;Data angle,cost
;hort,vert 90 deg
Data 0.0,10
Data 90.0,10
Data 180.0,10
Data 270.0,10
;diagonal 45 deg
Data 45.0,14
Data 135.0,14
Data 225.0,14
Data 315.0,14
;diag 22.5
Data 22.5,12
Data 67.5,12
Data 112.5,12
Data 157.7,12
Data 202.5,12
Data 247.5,12
Data 292.5,12
Data 337.5,12
;diagonal 15
Data 15.0,13
Data 30.0,13
Data 60.0,13
Data 75.0,13
Data 105.0,13
Data 120.0,13
Data 150.0,13
Data 165.0,13
Data 195.0,13
Data 210.0,13
Data 240.0,13
Data 255.0,13
Data 285.0,13
Data 300.0,13
Data 330.0,13
Data 345.0,13
;============================
;WEAPON (temp)
;============================
Type weapon
End Type
;============================
;BOT
;============================
Const BOT_MAX=64
Dim botId.bot(BOT_MAX)
Global botIndex.stack=stackIndexCreate(BOT_MAX)
;Const BOT_STATE_STAND_IDLE=0
;Const BOT_STATE_STAND_FIRE =0
;Const BOT_STATE_STAND_TAUNT=0
;Const BOT_STATE_WALK=0
;Const BOT_STATE_WALK_FIRE =0
;Const BOT_STATE_RUN=0
;Const BOT_STATE_RUN_REVERSE=0
;Const BOT_STATE_RUN_REVERSE_FIRE=0
;Const BOT_STATE_RUN_FIRE=0
;Const BOT_STATE_STRAFE_LEFT=0
;Const BOT_STATE_STRAFE_RIGHT=0
;Const BOT_STATE_STRAFE_LEFT_FIRE=0
;Const BOT_STATE_STRAFE_RIGHT_FIRE=0
;Const BOT_STATE_JUMP=0
;Const BOT_STATE_JUMP_FIRE=0
;Const BOT_STATE_SCAN=0
;Const BOT_STATE_ACQUIRE_WEAPON=0
;Const BOT_STATE_ACQUIRE_AMMO=0
;Const BOT_STATE_ACQUIRE_ARMOR=0
;Const BOT_STATE_ACQUIRE_COVER=0
;Const BOT_STATE_ACQUIRE_TEAMMEMBER=0
;Const BOT_STATE_CALL_TEAMMEMBER=0
Type bot
Field id%
Field typeid%
Field entity%,thinker%; temp
Field texture.texture
Field position.vector
Field old.vector
Field angle.vector
;sensors
Field sight.vector
Field hearing#
Field fullhealth#
Field health#
Field speed#
Field turn#
;motor
Field direction%
Field target%
Field goal.vector
Field path.path
Field waypointID%
Field sequence%
Field frame%
Field weapon.weapon
;ai
Field state%
Field action.action
End Type
Function botStop()
For this.bot=Each bot
botDelete(this)
Next
End Function
Function botNew.bot()
this.bot=New bot
this\id%=0
this\typeid%=0
this\entity%=0
;this\texture.texture=textureNew()
this\position.vector=vectorNew()
this\old.vector=vectorNew()
this\angle.vector=vectorNew()
this\sight.vector=vectorNew()
this\hearing#=0.0
this\fullhealth#=0.0
this\health#=0.0
this\speed#=0.0
this\goal.vector=vectorNew()
;this\path.path=pathNew()
this\frame%=0
;this\weapon.weapon=weaponNew()
this\state%=0
this\action.action=actionNew()
this\id%=StackPop(botIndex.stack)
botId(this\id)=this
Return this
End Function
Function botDelete(this.bot)
botId(this\id)=Null
StackPush(botIndex.stack,this\id%)
actionDelete(this\action.action)
;weaponDelete(this\weapon.weapon)
;pathDelete(this\path.path)
vectorDelete(this\goal.vector)
this\speed#=0.0
this\health#=0.0
this\fullhealth#=0.0
this\hearing#=0.0
vectorDelete(this\sight.vector)
vectorDelete(this\angle.vector)
vectorDelete(this\old.vector)
vectorDelete(this\position.vector)
;textureDelete(this\texture.texture)
FreeEntity this\entity%
Delete this
End Function
Function botUpdate()
For this.bot = Each bot
Select this\typeid
Case 1
botSensor(this)
botReaction(this)
botMotor(this)
Default
If player_visible ; this mode will let us watch them walking along the waypoints
;botSDSensor(this)
Else
botSDMotor(this)
EndIf
End Select
Next
End Function
Function botRead.bot(file)
this.bot=New bot
this\id%=ReadInt(file)
this\typeid%=ReadInt(file)
this\entity%=ReadInt(file)
;this\texture.texture=textureRead(file)
this\position.vector=vectorRead(file)
this\old.vector=vectorRead(file)
this\angle.vector=vectorRead(file)
this\sight.vector=vectorRead(file)
this\hearing#=ReadFloat(file)
this\fullhealth#=ReadFloat(file)
this\health#=ReadFloat(file)
this\speed#=ReadFloat(file)
this\goal.vector=vectorRead(file)
;this\path.path=pathRead(file)
this\frame%=ReadInt(file)
;this\weapon.weapon=weaponRead(file)
this\state%=ReadInt(file)
;this\action.action=actionRead(file)
Return this
End Function
Function botWrite(file,this.bot)
WriteInt(file,this\id%)
WriteInt(file,this\typeid%)
WriteInt(file,this\entity%)
;textureWrite(file,this\texture.texture)
vectorWrite(file,this\position.vector)
vectorWrite(file,this\old.vector)
vectorWrite(file,this\angle.vector)
vectorWrite(file,this\sight.vector)
WriteFloat(file,this\hearing#)
WriteFloat(file,this\fullhealth#)
WriteFloat(file,this\health#)
WriteFloat(file,this\speed#)
vectorWrite(file,this\goal.vector)
;pathWrite(file,this\path.path)
WriteInt(file,this\frame%)
;weaponWrite(file,this\weapon.weapon)
WriteInt(file,this\state%)
;actionWrite(file,this\action.action)
End Function
Function botSave(filename$="Default")
file=WriteFile(filename$+".bot")
For this.bot= Each bot
botWrite(file,this)
Next
CloseFile(file)
End Function
Function botOpen(filename$="Default")
file=ReadFile(filename+".bot")
Repeat
botRead(file)
Until Eof(file)
CloseFile(file)
End Function
Function botCopy.bot(this.bot)
copy.bot=New bot
copy\id%=this\id%
copy\typeid%=this\typeid%
copy\entity%=this\entity%
;copy\texture.texture=textureCopy(this\texture.texture)
copy\position.vector=vectorCopy(this\position.vector)
copy\old.vector=vectorCopy(this\old.vector)
copy\angle.vector=vectorCopy(this\angle.vector)
copy\sight.vector=vectorCopy(this\sight.vector)
copy\hearing#=this\hearing#
copy\fullhealth#=this\fullhealth#
copy\health#=this\health#
copy\speed#=this\speed#
copy\goal.vector=vectorCopy(this\goal.vector)
;copy\path.path=pathCopy(this\path.path)
copy\frame%=this\frame%
;copy\weapon.weapon=weaponCopy(this\weapon.weapon)
copy\state%=this\state%
copy\action.action=actionCopy(this\action.action)
Return copy
End Function
Function botMimic(mimic.bot,this.bot)
mimic\id%=this\id%
mimic\typeid%=this\typeid%
mimic\entity%=this\entity%
;textureMimic(mimic\texture.texture,this\texture.texture)
vectorMimic(mimic\position.vector,this\position.vector)
vectorMimic(mimic\old.vector,this\old.vector)
vectorMimic(mimic\angle.vector,this\angle.vector)
vectorMimic(mimic\sight.vector,this\sight.vector)
mimic\hearing#=this\hearing#
mimic\fullhealth#=this\fullhealth#
mimic\health#=this\health#
mimic\speed#=this\speed#
vectorMimic(mimic\goal.vector,this\goal.vector)
;pathMimic(mimic\path.path,this\path.path)
mimic\frame%=this\frame%
;weaponMimic(mimic\weapon.weapon,this\weapon.weapon)
mimic\state%=this\state%
actionMimic(mimic\action.action,this\action.action)
End Function
Function botCreate.bot(id%,typeid%,entity%,texture.texture,position.vector,old.vector,angle.vector,sight.vector,hearing#,fullhealth#,health#,speed#,goal.vector,path.path,frame%,weapon.weapon,state%,action.action)
this.bot=botNew()
this\id%=id%
this\typeid%=typeid%
this\entity%=entity%
;this\texture.texture=texture.texture
this\position.vector=position.vector
this\old.vector=old.vector
this\angle.vector=angle.vector
this\sight.vector=sight.vector
this\hearing#=hearing#
this\fullhealth#=fullhealth#
this\health#=health#
this\speed#=speed#
this\goal.vector=goal.vector
this\path.path=path.path
this\frame%=frame%
;this\weapon.weapon=weapon.weapon
this\state%=state%
this\action.action=action.action
Return this
End Function
Function botSet(this.bot,id%,typeid%,entity%,texture.texture,position.vector,old.vector,angle.vector,sight.vector,hearing#,fullhealth#,health#,speed#,goal.vector,path.path,frame%,weapon.weapon,state%,action.action)
this\id%=id%
this\typeid%=typeid%
this\entity%=entity%
this\texture.texture=texture.texture
this\position.vector=position.vector
this\old.vector=old.vector
this\angle.vector=angle.vector
this\sight.vector=sight.vector
this\hearing#=hearing#
this\fullhealth#=fullhealth#
this\health#=health#
this\speed#=speed#
this\goal.vector=goal.vector
this\path.path=path.path
this\frame%=frame%
this\weapon.weapon=weapon.weapon
this\state%=state%
this\action.action=action.action
End Function
Function botSensor(this.bot);input
; If LinePick(this\position\x#,this\position\y#,this\position\z#,this\sight\x#,this\sight\y#,this\sight\z#,radius#)=player this\state=1 ;visual check
; If ChannelPlaying(sound) And EntityDistance(this\entity%,sound)<=this\hear# this\state=1;audio check
; If this\health<this\fullhealth*.25 And this\state=0 this\state=2;health check
; If this\health<this\fullhealth*.25 And this\state=1 this\state=3;health check 2
; If this\weapon[this\weapon\current]\ammo<this\fullammo*.25 this\state=4;ammo check
; If this\health<0 this\state=5
If this\state=4 ;seek path
If this\path<>Null
If this\path\waypoints%=0 this\state%=0;goal achieve
Else
this\state=0
EndIf
EndIf
If this\state=3 ;building path
If this\path\state=3
pathRouteNext(this\path)
this\state%=4;moving
EndIf
EndIf
If this\state=1 Or this\state=2 this\state=3
If KeyHit(2) ;create path
;Delay 2000
this\state=1;testing
EndIf
If KeyHit(3) ;create path
;Delay 2000
this\state=2;testing
EndIf
If KeyHit(4)
vectorSet(this\position,Int(Rnd(40)*2),0,Int(Rnd(40)*2))
PositionEntity this\entity,this\position\x#,this\position\y#,this\position\z#
EndIf
If KeyHit(5)
vectorSet(this\goal,Int(Rnd(40)*2),0,Int(Rnd(40)*2))
PositionEntity goalentity,this\goal\x#,this\goal\y#,this\goal\z#
EndIf
If KeyHit(6) botSpawn()
End Function
Function botReaction(this.bot);action
Select this\state
;idle long acquisition
Case 1 this\path.path=pathCreate(this\position,this\goal,2,.2)
;short acquisition
Case 2 this\path.path=pathCreate(this\position,this\goal,1,.2)
End Select
End Function
Function botMotor(this.bot);movement and animation
; ;follow path
If this\state=4 botwaypointSeek(this);moving
If this\state=6 botWaypointSeek2(this);testing
; If collision regeneratepath
; If this\animation botAnimate(this)
; If this\physics botPhysics(this)
End Function
Function botwaypointSeek(this.bot)
;simple this\path\waypoint AI
PositionEntity this\path\waypoint\entity%,this\path\waypoint\position\x#,this\path\waypoint\position\y#,this\path\waypoint\position\z#
PointEntity this\entity,this\path\waypoint\entity%
MoveEntity this\entity,0,0,this\speed#
vectorSet(this\position,EntityX(this\entity),EntityY(this\entity),EntityZ(this\entity))
If this\position\x#<this\path\waypoint\position\x#+.5 And this\position\x#>this\path\waypoint\position\x#-.5
;If this\position\y#<this\path\waypoint\position\y#+.5 And this\position\y#>this\path\waypoint\position\y#-.5; omit for gravity and collision
If this\position\z#<this\path\waypoint\position\z#+.5 And this\position\z#>this\path\waypoint\position\z#-.5
pathRouteNext(this\path)
EndIf
;EndIf
EndIf
End Function
Function botWaypointSeek2(this.bot)
;simple waypoint AI
PositionEntity this\path\waypoint\entity%,this\path\waypoint\position\x#,this\path\waypoint\position\y#,this\path\waypoint\position\z#
PointEntity this\entity,this\path\waypoint\entity%
MoveEntity this\entity,0,0,this\speed#
vectorSet(this\position,EntityX(this\entity),EntityY(this\entity),EntityZ(this\entity))
If this\position\x#<this\path\waypoint\position\x#+.5 And this\position\x#>this\path\waypoint\position\x#-.5
;If this\position\y#<this\path\waypoint\position\y#+.5 And this\position\y#>this\path\waypoint\position\y#-.5; omit for gravity and collision
If this\position\z#<this\path\waypoint\position\z#+.5 And this\position\z#>this\path\waypoint\position\z#-.5
this\path\waypoint=After this\path\waypoint
If this\path\waypoint=Null this\path\waypoint=waypointId(1)
EndIf
;EndIf
EndIf
End Function
Function botSpawn.bot()
this.bot=botNew()
this\typeid%=typeid%
this\entity%=CreateCylinder(8,True)
EntityColor this\entity,Rnd(255),Rnd(255),Rnd(255)
vectorSet(this\position,Int(Rnd(40)*2),0,Int(Rnd(40)*2))
PositionEntity this\entity%,this\position\x#,this\position\y#,this\position\z#
vectorSet(this\goal,Int(Rnd(40)*2),0,Int(Rnd(40)*2))
PositionEntity goalentity,this\goal\x#,this\goal\y#,this\goal\z#
this\speed#=Rnd(.01,.5)
Return this
End Function
Function botSDSensor(this.bot)
;PositionEntity worker\entity%,waypointID(this\waypointID%+this\direction)\position\x#,waypointID(this\waypointID%+this\direction)\position\y#,waypointID(this\waypointID+this\direction)\position\z#
EntityColor this\thinker%,0,255,0
cur_d#=EntityDistance(this\target%,this\entity%) ;(Abs(EntityX(this\target)-this\position\x#)+Abs(EntityY(this\target)#-this\position\y#)+Abs(EntityZ(this\target)#-this\position\z#));
If cur_d<1.0
this\state%=0
botSDReaction(this)
Return
ElseIf cur_d>10.0
this\state%=3
botSDReaction(this)
Return
EndIf
If cur_d<targetrange
If EntityVisible(this\entity%,this\target%)
; target% visible from current bot position
cur_v=1
EndIf
EndIf
PositionEntity worker\entity%,waypointID(this\waypointID%+this\direction%)\position\x,waypointID(this\waypointID%+this\direction%)\position\y,waypointID(this\waypointID%+this\direction%)\position\z
nex_d#=EntityDistance(worker\entity%,this\target%)
If nex_d< targetrange
If EntityVisible(worker\entity%,this\target%)
; target% visible from next bot waypoint
nex_v=1
EndIf
EndIf
PositionEntity worker\entity%,waypointID(this\waypointID%-this\direction%)\position\x,waypointID(this\waypointID%-this\direction%)\position\y,waypointID(this\waypointID%-this\direction%)\position\z
prev_d#=EntityDistance(this\target%,worker\entity%)
If prev_d< targetrange
If EntityVisible(worker\entity%,this\target%)
; target% visible from prev. bot waypoint
prev_v=1
EndIf
EndIf
this\state%=3
If cur_v=1 And nex_v=1 And prev_v=1 ; visible from all 3 points
If cur_d<nex_d And cur_d<prev_d
; current pos optimal
this\state%=0
EndIf
If nex_d<cur_d And nex_d<prev_d
; next waypoint better
this\state%=1
EndIf
If prev_d<nex_d And prev_d<cur_d
; prev waypoint better
this\state%=2
EndIf
EndIf
If cur_v=1 And nex_v=1 And prev_v=0 ; visible from cur and nex
If cur_d<nex_d
; current pos optimal
this\state%=0
Else
this\state%=1
EndIf
EndIf
If cur_v=1 And nex_v=0 And prev_v=1 ; visible from cur and prev
If cur_d<prev_d
; current pos optimal
this\state%=0
Else
this\state%=2
EndIf
EndIf
If cur_v=0 And nex_v=1 And prev_v=1 ; visible from nex and prev
If nex_d<prev_d
; current pos optimal
this\state%=1
Else
this\state%=2
EndIf
EndIf
If cur_v=1 And nex_v=0 And prev_v=0 ; visible only from cur
this\state%=0
EndIf
If cur_v=0 And nex_v=1 And prev_v=0 ; visible only from nex
this\state%=1
EndIf
If cur_v=0 And nex_v=0 And prev_v=1 ; visible only from prev
this\state%=2
EndIf
botSDReaction(this)
;optimization notes: reduce the number of entitydistance calls, or faster alternative
End Function
Function botSDReaction(this.bot)
Select this\state%
Case 0; already in optimal position to attack
EntityColor this\thinker%,255,0,0
Case 1; next waypoint is better, go there
EntityColor this\thinker%,255,255,0
botSDMotor(this)
Case 2 ; prev waypoint is better, go there
EntityColor this\thinker%,255,255,0
If this\direction=1
this\direction=-1
Else
this\direction=1
EndIf
botSDMotor(this)
Case 3 ; not visible at all - walk waypoints
EntityColor this\thinker%,0,255,255
botSDMotor(this)
End Select
End Function
Function botSDMotor(this.bot)
PositionEntity worker\entity%,waypointID(this\waypointID%+this\direction)\position\x#,waypointID(this\waypointID%+this\direction)\position\y#,waypointID(this\waypointID+this\direction)\position\z#
; rotate
this\old\y#=EntityYaw(this\entity%)
PointEntity this\entity%, worker\entity%
this\angle\y#=EntityYaw(this\entity%)
MoveEntity this\entity%,0,0,this\speed#
adist#=this\old\y#-this\angle\y#
turn#=Abs(this\old\y#-this\angle\y#)
If turn#>180 turn#=360.0-turn#
turn=turn*this\turn#
If (adist <-180) Or (adist >0 And adist <180)
RotateEntity this\entity%,0,this\old\y#-turn#,0
Else
RotateEntity this\entity%,0,this\old\y#+turn#,0
EndIf
; move
If player_visible ; this mode will let us watch them walking along the waypoints
this\speed#=.5
Else
this\speed#=.25
EndIf
If EntityX(this\entity%)>=EntityX(worker\entity%)-this\speed# And EntityX(this\entity%)<=EntityX(worker\entity%)+this\speed#
If EntityY(this\entity%)>=EntityY(worker\entity%)-this\speed# And EntityY(this\entity%)<=EntityY(worker\entity%)+this\speed#
If EntityZ(this\entity%)>=EntityZ(worker\entity%)-this\speed# And EntityZ(this\entity%)<=EntityZ(worker\entity%)+this\speed#
this\waypointID%=this\waypointID%+this\direction
EndIf
EndIf
EndIf
If this\waypointID%>=waypoints%
this\direction=-1
EndIf
If this\waypointID%<=1
this\direction=1
EndIf
;animate
Select this\state%
Case 1
If AnimSeq(this\entity)<>stillfire Animate this\entity%,1,this\speed#*1.25,stillfire
Default
If AnimSeq(this\entity)<>RunFire Animate this\entity%,1,this\speed#*1.25,RunFire
End Select
End Function |
Comments
None.
Code Archives Forum
WARNING!!! This is code is still under development and not yet suitable for use. Once complete I will remove this warning.