Code: Select all
EnableExplicit
; Element type
Structure LLElement
*Next.LLElement
*Previous.LLElement
*List.LLElementList
EndStructure
Structure LLElementList
*First.LLElement
*Last.LLElement
Size.l
EndStructure
Structure Entity Extends LLElement
Type.l
x.f
y.f
EndStructure
Structure Creature Extends Entity
xv.f
yv.f
EndStructure
Structure Swarmbot Extends Creature
xd.f ; x destination
yd.f ; y destination
State.l
Energy.f
ID.l ; ID number
EndStructure
Structure Food Extends Entity
Amount.f
EndStructure
Structure Sector
*Occupants.LLElementList
EndStructure
Structure Timer
Freq.l
Ticks.l
EndStructure
; PureBasic Visual Designer v3.95 build 1485 (PB4Code)
;- Window Constants
;
Enumeration
#Window_0
EndEnumeration
;- Gadget Constants
;
Enumeration
#Text_0
#Text_1
#Text_2
#Text_3
#CheckBox_0
#CheckBox_1
#CheckBox_2
#Spin_0
#Spin_1
#Spin_2
#Spin_3
EndEnumeration
Procedure Open_Window_0()
If OpenWindow(#Window_0, 412, 122, 320, 380, "Swarmbots AI Demo", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_TitleBar )
If CreateGadgetList(WindowID(#Window_0))
TextGadget(#Text_0, 160, 10, 160, 30, "Visual Range (Sectors): 0-32")
TextGadget(#Text_1, 160, 60, 160, 30, "Max Speed (Pixels/10/Frame): 0-127")
TextGadget(#Text_2, 160, 110, 160, 30, "Acceleration (Pixels/10/Frame/Frame): 0-15")
TextGadget(#Text_3, 160, 160, 160, 30, "Swarminess: 0-100")
CheckBoxGadget(#CheckBox_0, 20, 210, 130, 30, "Show relationship webs")
CheckBoxGadget(#CheckBox_1, 20, 260, 130, 30, "Show ID numbers")
CheckBoxGadget(#CheckBox_2, 20, 310, 130, 30, "Show grid")
SpinGadget(#Spin_0, 20, 10, 130, 30, 0, 32,#PB_Spin_Numeric | #PB_Spin_ReadOnly)
SpinGadget(#Spin_1, 20, 60, 130, 30, 0, 127,#PB_Spin_Numeric | #PB_Spin_ReadOnly)
SpinGadget(#Spin_2, 20, 110, 130, 30, 0, 15,#PB_Spin_Numeric | #PB_Spin_ReadOnly)
SpinGadget(#Spin_3, 20, 160, 130, 30, 0, 100,#PB_Spin_Numeric | #PB_Spin_ReadOnly)
EndIf
EndIf
EndProcedure
Declare.l ThreadProc(*Value)
Declare.l HandleInput()
Declare.l DrawGrid()
Declare.l CreateSwarmbot(x.l,y.l)
Declare.l UpdateSwarmbot(*sb.Swarmbot)
Declare.l DrawSwarmbot(*sb.Swarmbot)
Declare.l CreateFood(x,y)
Declare.l UpdateFood(*f.Food)
Declare.l DrawFood(*f.Food)
Declare.l UpdateEntities()
Declare.l DrawEntities()
Declare.l IndexNeighbours(*e.Entity,Range)
Declare.l CreateTimer(Freq)
Declare.l WaitTimer(*Timer.Timer)
Declare.f Atan2(a.f,b.f)
Declare.f FMod(a.f,b.f)
Declare.l LLRemoveElement(*Element.LLElement)
Declare.l LLAddElement(*Element.LLElement,*List.LLElementList)
Declare.l LLInsertElementAfter(*Element1.LLElement,*Element2.LLElement)
Declare.l LLInsertElementBefore(*Element1.LLElement,*Element2.LLElement)
Declare.l LLDestroyElementList(*ElementList.LLElementList)
Declare.l LLDestroyElement(*Element.LLElement)
Declare.l LLCreateElement(Size.l)
Declare.l LLCreateElementList()
Global THREAD_ON = 1
Global DRAW_GRID = 1
#FPS = 30
#SCREEN_WIDTH = 800
#SCREEN_HEIGHT = 600
#SCREEN_DEPTH = 32
#SECTOR_SIZE = 64
#MAP_WIDTH_SECTORS = 32
#MAP_HEIGHT_SECTORS = 32
#MAP_WIDTH_PIXELS = #SECTOR_SIZE * #MAP_WIDTH_SECTORS
#MAP_HEIGHT_PIXELS = #SECTOR_SIZE * #MAP_HEIGHT_SECTORS
#SCREEN_WIDTH_SECTORS = #SCREEN_WIDTH / #SECTOR_SIZE
#SCREEN_HEIGHT_SECTORS = #SCREEN_HEIGHT / #SECTOR_SIZE
Global Dim MAP.Sector(#MAP_WIDTH_SECTORS - 1,#MAP_HEIGHT_SECTORS - 1)
Define x,y
For y = 0 To #MAP_HEIGHT_SECTORS - 1
For x = 0 To #MAP_WIDTH_SECTORS - 1
MAP(x,y)\Occupants = LLCreateElementList()
Next
Next
Global MAP_ORIGIN_X = 0
Global MAP_ORIGIN_Y = 0
#SCROLL_STEP = 8
; entity constants
Enumeration 1
#ET_SWARMBOT
#ET_FOOD
EndEnumeration
; entity neighbours array for fast access to entities
; near a particular sector
Global Dim *NEIGHBOURS.Entity(0)
; swarmbot constants/globals
#SB_SIZE = 16
Global SB_MAX_SPEED.f = 8
Global SB_RANGE = 4 ; number of sectors the swarmbot can "see"
Global SB_ACCELERATION.f = 0.4 ; how fast the swarmbot can accelerate
Global SB_AUTONOMY.f = 0.25 ; between 0 and 1, sets the swarmbots' inclination to swarm (low = very swarmy, high = very solitary)
Global SB_DRAW_RELATIONSHIP_WEBS = 1
Global SB_DRAW_ID_NUMBERS = 1
Global SB_NEXT_ID = 1
; swarmbot AI state constants
Enumeration 1
#SB_STATE_CRUISING
#SB_STATE_FEEDING
EndEnumeration
; food constants
#FOOD_SIZE = 8
#FOOD_ENERGY_INC = 0.05
RandomSeed(ElapsedMilliseconds())
Define i
Define *Timer = CreateTimer(1000 / #FPS)
Define Window = OpenWindow(1,0,0,#SCREEN_WIDTH,#SCREEN_HEIGHT,"Swarmbots AI Demo")
Define Pings
Define EventID
If Not IsWindow(1)
MessageRequester("OOPS!","Sorry, could not open window.")
End
EndIf
If Not InitSprite()
MessageRequester("OOPS!","Sorry, could not initialise graphics environment.")
End
EndIf
If Not OpenWindowedScreen(WindowID(1),0,0,#SCREEN_WIDTH,#SCREEN_HEIGHT,0,0,0)
MessageRequester("OOPS!","Sorry, could not set graphics mode.")
End
EndIf
If Not InitKeyboard()
CloseScreen()
MessageRequester("OOPS!","Sorry, could not initialise keyboard.")
End
EndIf
RandomSeed(ElapsedMilliseconds())
Global THREAD = CreateThread(@ThreadProc(),0)
Procedure ThreadProc(*Value)
Open_Window_0()
If Not IsWindow(#Window_0)
End
EndIf
SetGadgetState(#Spin_0,4)
SetGadgetState(#Spin_1,80)
SetGadgetState(#Spin_2,4)
SetGadgetState(#Spin_3,70)
SetGadgetState(#CheckBox_2,1)
Protected EventID
While THREAD_ON
EventID = WaitWindowEvent(2)
If EventID = #PB_Event_CloseWindow
Break
EndIf
SB_RANGE = GetGadgetState(#Spin_0)
SB_MAX_SPEED = GetGadgetState(#Spin_1) / 10
SB_ACCELERATION = GetGadgetState(#Spin_2) / 10
SB_AUTONOMY = 1.0 - (GetGadgetState(#Spin_3) / 100.0)
SB_DRAW_RELATIONSHIP_WEBS = GetGadgetState(#CheckBox_0)
SB_DRAW_ID_NUMBERS = GetGadgetState(#CheckBox_1)
DRAW_GRID = GetGadgetState(#CheckBox_2)
Wend
CloseWindow(#Window_0)
EndProcedure
For i = 0 To 31
CreateSwarmbot(Random(#MAP_WIDTH_PIXELS-1),Random(#MAP_HEIGHT_PIXELS-1))
CreateFood(Random(#MAP_WIDTH_PIXELS - 1),Random(#MAP_HEIGHT_PIXELS - 1))
Next
;- Main
Repeat
Pings = WaitTimer(*Timer)
For i = 1 To Pings
UpdateEntities()
HandleInput()
Next
EventID.l = WindowEvent();WaitWindowEvent(2)
Select EventID
Case #PB_Event_CloseWindow
THREAD_ON = 0
WaitThread(Thread)
End
Case #PB_Event_Gadget
EndSelect
ClearScreen(RGB(0,0,0))
If StartDrawing(ScreenOutput())
DrawingMode(#PB_2DDrawing_Transparent)
DrawGrid()
DrawEntities()
StopDrawing()
FlipBuffers()
EndIf
ForEver
End
Procedure HandleInput()
ExamineKeyboard()
If KeyboardPushed(#PB_Key_Escape)
THREAD_ON = 0
WaitThread(THREAD)
End
EndIf
If KeyboardPushed(#PB_Key_Up)
If MAP_ORIGIN_Y > 0
MAP_ORIGIN_Y - #SCROLL_STEP
EndIf
If MAP_ORIGIN_Y < 0
MAP_ORIGIN_Y = 0
EndIf
EndIf
If KeyboardPushed(#PB_Key_Down)
If MAP_ORIGIN_Y < #MAP_HEIGHT_PIXELS - #SCREEN_HEIGHT
MAP_ORIGIN_Y + #SCROLL_STEP
EndIf
If MAP_ORIGIN_Y > #MAP_HEIGHT_PIXELS - #SCREEN_HEIGHT
MAP_ORIGIN_Y = #MAP_HEIGHT_PIXELS - #SCREEN_HEIGHT
If MAP_ORIGIN_Y < 0
MAP_ORIGIN_Y = 0
EndIf
EndIf
EndIf
If KeyboardPushed(#PB_Key_Left)
If MAP_ORIGIN_X > 0
MAP_ORIGIN_X - #SCROLL_STEP
EndIf
If MAP_ORIGIN_X < 0
MAP_ORIGIN_X = 0
EndIf
EndIf
If KeyboardPushed(#PB_Key_Right)
If MAP_ORIGIN_X < #MAP_WIDTH_PIXELS - #SCREEN_WIDTH
MAP_ORIGIN_X + #SCROLL_STEP
EndIf
If MAP_ORIGIN_X > #MAP_WIDTH_PIXELS - #SCREEN_WIDTH
MAP_ORIGIN_X = #MAP_WIDTH_PIXELS - #SCREEN_WIDTH
If MAP_ORIGIN_X < 0
MAP_ORIGIN_X = 0
EndIf
EndIf
EndIf
EndProcedure
Procedure DrawGrid()
If DRAW_GRID = 0
ProcedureReturn
EndIf
Protected ox = -(MAP_ORIGIN_X % #SECTOR_SIZE)
Protected oy = -(MAP_ORIGIN_Y % #SECTOR_SIZE)
; calculate number of visible sectors
Protected vsx = #SCREEN_WIDTH / #SECTOR_SIZE
Protected vsy = #SCREEN_HEIGHT / #SECTOR_SIZE
Protected x,y
If ox <> 0
vsx + 1
EndIf
If #SCREEN_WIDTH % #SECTOR_SIZE
vsx + 1
EndIf
If oy <> 0
vsy + 1
EndIf
If #SCREEN_HEIGHT % #SECTOR_SIZE
vsy + 1
EndIf
If vsx > #MAP_WIDTH_SECTORS
vsx = #MAP_WIDTH_SECTORS
EndIf
If vsy > #MAP_HEIGHT_SECTORS
vsy = #MAP_HEIGHT_SECTORS
EndIf
For y = 0 To vsy
Box(ox,oy+y*#SECTOR_SIZE,vsx*#SECTOR_SIZE,1,RGB(0,128,255))
Next
For x = 0 To vsx
Box(ox+x*#SECTOR_SIZE,oy,1,vsy*#SECTOR_SIZE,RGB(0,128,255))
Next
EndProcedure
Procedure CreateSwarmbot(x.l,y.l)
Protected *sb.Swarmbot = LLCreateElement(SizeOf(Swarmbot))
Protected v.f,a.f
Protected XSector.l,YSector.l
If *sb
*sb\Type = #ET_SWARMBOT
*sb\x = x
*sb\y = y
*sb\ID = SB_NEXT_ID
*sb\State = #SB_STATE_CRUISING
*sb\Energy = 0.0
v = (Random(SB_MAX_SPEED * 100) / 100)
a = Random(628) / 100
*sb\xv = v * Cos(a)
*sb\yv = v * Sin(a)
*sb\xd = Random(#MAP_WIDTH_PIXELS - 1)
*sb\yd = Random(#MAP_HEIGHT_PIXELS - 1)
XSector = x / #SECTOR_SIZE
YSector = y / #SECTOR_SIZE
LLAddElement(*sb,MAP(XSector,YSector)\Occupants)
SB_NEXT_ID + 1
EndIf
ProcedureReturn *sb
EndProcedure
Procedure UpdateSwarmbot(*sb.Swarmbot)
Protected *e.Entity,*f.Food,*sb2.Swarmbot
Protected XSector1.l,YSector1.l
Protected XSector2.l,YSector2.l
Protected sbx,sby
Protected x,y,Width,XSector,YSector
Protected a.f
Protected xt.f
Protected yt.f
Protected c.f ; neighbour count
Protected axv.f
Protected ayv.f
Protected i
Protected aswarm.f ; angle towards swarm
Protected aself.f ; angle of swarmbot
Protected Attr.f
Protected Food ; has the swarmbot found food?
Protected Scale.f
Protected Dist.f
; calculate current sector of swarmbot
sbx = Round(*sb\x,#PB_Round_Down)
sby = Round(*sb\y,#PB_Round_Down)
XSector1 = Round(sbx / #SECTOR_SIZE,#PB_Round_Down)
YSector1 = Round(sby / #SECTOR_SIZE,#PB_Round_Down)
; move swarmbot
*sb\x = *sb\x + *sb\xv ;* *sb\Energy
*sb\y = *sb\y + *sb\yv ;* *sb\Energy
; calculate new sector of swarmbot
sbx = Round(*sb\x,#PB_Round_Down)
sby = Round(*sb\y,#PB_Round_Down)
XSector2 = Round(sbx / #SECTOR_SIZE,#PB_Round_Down)
YSector2 = Round(sby / #SECTOR_SIZE,#PB_Round_Down)
; wrap
If *sb\x >= #SECTOR_SIZE * #MAP_WIDTH_SECTORS
*sb\x = FMod(*sb\x,#SECTOR_SIZE * #MAP_WIDTH_SECTORS)
ElseIf *sb\x < 0.0
*sb\x = #SECTOR_SIZE * #MAP_WIDTH_SECTORS + *sb\x
EndIf
If *sb\y >= #SECTOR_SIZE * #MAP_HEIGHT_SECTORS
*sb\y = FMod(*sb\y,#SECTOR_SIZE * #MAP_HEIGHT_SECTORS)
ElseIf *sb\y < 0.0
*sb\y = #SECTOR_SIZE * #MAP_HEIGHT_SECTORS + *sb\y
EndIf
; if new sector is different from old, wrap sector if
; necessary, then remove swarmbot from the first sector's
; linked list and add it to the second
If XSector2 <> XSector1 Or YSector2 <> YSector1
If XSector2 < 0
XSector2 = #MAP_WIDTH_SECTORS + XSector2
ElseIf XSector2 > (#MAP_WIDTH_SECTORS - 1)
XSector2 % (#MAP_WIDTH_SECTORS) ; - 1)
EndIf
If YSector2 < 0
YSector2 = #MAP_HEIGHT_SECTORS + YSector2
ElseIf YSector2 > (#MAP_HEIGHT_SECTORS - 1)
YSector2 % (#MAP_HEIGHT_SECTORS) ; - 1)
EndIf
LLAddElement(*sb,MAP(XSector2,YSector2)\Occupants)
EndIf
; keep speed within acceptable limits
Protected Speed.f = Sqr(Pow(*sb\xv,2.0) + Pow(*sb\yv,2.0))
If Speed > SB_MAX_SPEED
Scale.f = (SB_MAX_SPEED / Speed)
*sb\xv * Scale
*sb\yv * Scale
EndIf
; index the entities within range of our swarmbot
Protected NeighbourCount = IndexNeighbours(*sb,SB_RANGE)
; loop through neighbours: if any swarmbots are found then add up the
; weighted direction vectors from the current swarmbot to each neighbour
For i = 0 To NeighbourCount - 1
If *NEIGHBOURS(i)\Type = #ET_SWARMBOT
*sb2 = *Neighbours(i)
Dist = Sqr(Pow(*sb2\x - *sb\x,2.0) + Pow(*sb2\y - *sb\y,2.0))
a = ATan2(*sb2\y - *sb\y,*sb2\x - *sb\x)
; find out how attracted the swarmbot is to this particular neighbour.
; this returns a value between -1.0 and 1.0 based on the distance
; between the swarmbots. very close swarmbots will be repelled by
; each other
Attr = Sin(4.712389 * (1.0 - Dist / (SB_RANGE * #SECTOR_SIZE)))
xt + Attr * Cos(a)
yt + Attr * Sin(a)
c + Abs(Attr)
EndIf
Next
; if there were any swarmbots nearby, find the average of all their positions
; relative to the swarmbot that the function was called for
If c > 0.0
axv = xt / c
ayv = yt / c
aswarm = ATan2(ayv,axv)
EndIf
Select *sb\State
Case #SB_STATE_CRUISING
aself = ATan2(*sb\yd - *sb\y,*sb\xd - *sb\x)
; find distance from destination
Dist = Sqr(Pow(*sb\x - *sb\xd,2.0) + Pow(*sb\y - *sb\yd,2.0))
; if practically at destination, pick a new destination at random
If Dist <= 100
*sb\xd = Random(#MAP_WIDTH_PIXELS - 1)
*sb\yd = Random(#MAP_HEIGHT_PIXELS - 1)
EndIf
For i = 0 To NeighbourCount - 1
If *NEIGHBOURS(i)\Type = #ET_FOOD And *sb\Energy < 1.0
*sb\State = #SB_STATE_FEEDING
EndIf
Next
Case #SB_STATE_FEEDING
For i = 0 To NeighbourCount - 1
If *NEIGHBOURS(i)\Type = #ET_FOOD
*f = *NEIGHBOURS(i)
Dist = Sqr(Pow(*sb\x - *f\x,2.0) + Pow(*sb\y - *f\y,2.0))
If Dist <= #FOOD_SIZE + #SB_SIZE
If *sb\Energy < 1.0
*sb\Energy + #FOOD_ENERGY_INC
*f\Amount - #FOOD_ENERGY_INC
Else
*sb\State = #SB_STATE_CRUISING
Break
EndIf
Else
Food = 1
aself = ATan2(*f\y - *sb\y,*f\x - *sb\x)
Break
EndIf
EndIf
Next
If Food = 0 Or *sb\Energy >= 1.0
aself = ATan2(*sb\yd - *sb\y,*sb\xd - *sb\x)
*sb\State = #SB_STATE_CRUISING
EndIf
EndSelect
If c = 0.0
aswarm = aself
EndIf
Protected xr.f
Protected yr.f
xr = (1.0 - SB_AUTONOMY) * Cos(aswarm) + SB_AUTONOMY * Cos(aself)
yr = (1.0 - SB_AUTONOMY) * Sin(aswarm) + SB_AUTONOMY * Sin(aself)
a = ATan2(yr,xr)
*sb\xv + SB_ACCELERATION * Cos(a)
*sb\yv + SB_ACCELERATION * Sin(a)
EndProcedure
Procedure DrawSwarmbot(*sb.Swarmbot)
Protected TW.l,TH.l
Protected ID$
Protected i,NeighbourCount,*sb2.Swarmbot
If *sb
If SB_DRAW_RELATIONSHIP_WEBS
NeighbourCount = IndexNeighbours(*sb,SB_RANGE)
For i = 0 To NeighbourCount - 1
If *NEIGHBOURS(i)\Type = #ET_SWARMBOT
*sb2 = *NEIGHBOURS(i)
Line(*sb\x - MAP_ORIGIN_X,*sb\y - MAP_ORIGIN_Y,*sb2\x - *sb\x,*sb2\y - *sb\y,RGB(0,255,0))
EndIf
Next
EndIf
; draw swarmbot
Circle(*sb\x - MAP_ORIGIN_X,*sb\y - MAP_ORIGIN_Y,#SB_SIZE,RGB(255,0,0))
Circle(*sb\x - MAP_ORIGIN_X,*sb\y - MAP_ORIGIN_Y,#SB_SIZE - 2,RGB(0,0,0))
ID$ = Str(*sb\ID)
TW = TextWidth(ID$)
TH = TextHeight(ID$)
If SB_DRAW_ID_NUMBERS
; draw ID number
DrawText(*sb\x - 0.5 * TW - MAP_ORIGIN_X,*sb\y - 0.5 * TH - MAP_ORIGIN_Y,ID$,RGB(255,255,255),RGB(255,0,0))
EndIf
EndIf
EndProcedure
Procedure CreateFood(x,y)
Protected *f.Food = LLCreateElement(SizeOf(Food))
Protected XSector.l,YSector.l
If *f
*f\Type = #ET_FOOD
*f\x = x
*f\y = y
*f\Amount = 1.0
XSector = x / #SECTOR_SIZE
YSector = y / #SECTOR_SIZE
LLAddElement(*f,MAP(XSector,YSector)\Occupants)
EndIf
ProcedureReturn *f
EndProcedure
Procedure UpdateFood(*f.Food)
Protected *n.Food
If *f
*n = *f\Next
If *f\Amount <= 0
LLDestroyElement(*f)
EndIf
ProcedureReturn *n
EndIf
EndProcedure
Procedure DrawFood(*f.Food)
If *f
Circle(*f\x - MAP_ORIGIN_X,*f\y - MAP_ORIGIN_Y,#FOOD_SIZE * *f\Amount,RGB(0,255,0))
EndIf
EndProcedure
Procedure UpdateEntities()
Protected x.l,y.l
Protected *e.Entity
For y = 0 To #MAP_HEIGHT_SECTORS - 1
For x = 0 To #MAP_WIDTH_SECTORS - 1
;Debug "looping thru occupants"
*e = MAP(x,y)\Occupants\First
While *e <> 0
Select *e\Type
Case #ET_SWARMBOT
UpdateSwarmbot(*e)
Case #ET_FOOD
*e = UpdateFood(*e)
Continue
EndSelect
*e = *e\Next
Wend
Next
Next
EndProcedure
Procedure DrawEntities()
Protected x.l,y.l
Protected *e.Entity
For y = 0 To #MAP_HEIGHT_SECTORS - 1
For x = 0 To #MAP_WIDTH_SECTORS - 1
*e = MAP(x,y)\Occupants\First
While *e <> 0
Select *e\Type
Case #ET_SWARMBOT
DrawSwarmbot(*e)
Case #ET_FOOD
DrawFood(*e)
EndSelect
*e = *e\Next
Wend
Next
Next
EndProcedure
Procedure IndexNeighbours(*e.Entity,Range)
Protected *i.Entity ; iterator
Protected x,y
Protected Width
Protected c = 0 ; counter
Protected Size = 16 ; current size of neighbours array
Protected XSector,YSector
If *e And Range > 0
XSector = Round(*e\x / #SECTOR_SIZE,#PB_Round_Down)
YSector = Round(*e\y / #SECTOR_SIZE,#PB_Round_Down)
If XSector >= 0 And XSector < #MAP_WIDTH_SECTORS And YSector >= 0 And YSector < #MAP_HEIGHT_SECTORS
Dim *NEIGHBOURS.Entity(Size)
; find nearby entities
For y = -Range To Range
Width = Sqr(Range * Range - y*y)
For x = -Width To Width
If (XSector + x) > 0 And (XSector + x) < (#MAP_WIDTH_SECTORS - 1)
If (YSector + y) > 0 And (YSector + y) < (#MAP_HEIGHT_SECTORS - 1)
*i = MAP(XSector + x,YSector + y)\Occupants\First
While *i
If *i <> *e
*NEIGHBOURS(c) = *i
c + 1
If c > Size
Size + 16
ReDim *NEIGHBOURS.Entity(Size)
EndIf
EndIf
*i = *i\Next
Wend
EndIf
EndIf
Next
Next
EndIf
EndIf
ProcedureReturn c
EndProcedure
Procedure CreateTimer(Freq)
Protected *Timer.Timer = AllocateMemory(SizeOf(Timer))
If *Timer
*Timer\Ticks = ElapsedMilliseconds()
*Timer\Freq = Freq
EndIf
ProcedureReturn *Timer
EndProcedure
Procedure WaitTimer(*Timer.Timer)
Protected Elapsed
Protected Pings
If *Timer
Repeat
Elapsed = ElapsedMilliseconds()
If Elapsed - *Timer\Ticks > *Timer\Freq
Break
EndIf
Delay(1)
ForEver
Pings = (Elapsed - *Timer\Ticks) / *Timer\Freq
*Timer\Ticks = *Timer\Ticks + *Timer\Freq * Pings
EndIf
ProcedureReturn Pings
EndProcedure
Procedure DestroyTimer(*Timer.Timer)
If *Timer
FreeMemory(*Timer)
ProcedureReturn 1
EndIf
ProcedureReturn 0
EndProcedure
Procedure.f Atan2(a.f,b.f)
EnableASM
FLD a
FLD b
FPATAN
DisableASM
ProcedureReturn
EndProcedure
Procedure.f FMod(a.f,b.f)
Protected n.f
n.f = a/b
If n >= 0
n = Round(n,#PB_Round_Down)
Else
n = Round(n,#PB_Round_Up)
EndIf
ProcedureReturn a - n * b
EndProcedure
DisableExplicit
Procedure.l LLCreateElement(Size.l)
Protected *Element.LLElement = 0
*Element = AllocateMemory(Size)
ProcedureReturn *Element
EndProcedure
Procedure.l LLCreateElementList()
Protected *List.LLElementList
*List = AllocateMemory(SizeOf(LLElementList))
ProcedureReturn *List
EndProcedure
Procedure.l LLDestroyElement(*Element.LLElement)
Protected Result = 0
If *Element <> 0
If *Element\List <> 0
LLRemoveElement(*Element)
EndIf
FreeMemory(*Element)
Result = 1
EndIf
ProcedureReturn Result
EndProcedure
Procedure.l LLDestroyElementList(*ElementList.LLElementList)
Protected Result = 0
Protected *Element.LLElement
Protected *Element2.LLElement
;Debug "destroying Element list"
;Debug ""
If *ElementList <> 0
*Element = *ElementList\First
While Element <> 0
count = count + 1
;Debug "destroying Element " + count
*Element2 = *Element\Next
If Not LLDestroyElement(*Element)
Result = 0
ProcedureReturn Result
EndIf
*Element = *Element2
Wend
FreeMemory(*ElementList)
Result = 1
EndIf
ProcedureReturn Result
EndProcedure
Procedure.l LLInsertElementBefore(*Element1.LLElement,*Element2.LLElement)
Protected Result = 0
If *Element1 <> 0
If *Element2 <> 0
If *Element2\List <> 0
If *Element1 <> *Element2
If *Element1\List <> 0
LLRemoveElement(*Element1)
EndIf
If *Element2 = *Element2\List\First
*Element2\List\First = *Element1
EndIf
*Element1\List = *Element2\List
*Element1\Previous = *Element2\Previous
*Element1\Next = *Element2
*Element2\Previous = *Element1
*Element2\List\Size = *Element2\List\Size + 1
Result = 1
EndIf
EndIf
EndIf
EndIf
ProcedureReturn Result
EndProcedure
Procedure.l LLInsertElementAfter(*Element1.LLElement,*Element2.LLElement)
Protected Result = 0
If *Element1 <> 0
If *Element2 <> 0
If *Element2\List <> 0
If *Element1 <> *Element2
If *Element1\List <> 0
LLRemoveElement(*Element1)
; Remove
EndIf
If *Element2 = *Element2\List\Last
*Element2\List\Last = *Element1
EndIf
*Element1\List = *Element2\List
*Element1\Next = *Element2\Next
*Element1\Previous = *Element2
*Element2\Next = *Element1
*Element2\List\Size = *Element2\List\Size + 1
Result = 1
;Else
; Debug "Elements are the same."
EndIf
;Else
; Debug "Element2 isn't attached to a list."
EndIf
;Else
; Debug "Element2 is 0."
EndIf
;Else
; Debug "Element1 is 0"
EndIf
ProcedureReturn Result
EndProcedure
Procedure.l LLAddElement(*Element.LLElement,*List.LLElementList)
Protected Result = 0
If *Element <> 0
If *List <> 0
If *Element\List <> 0
LLRemoveElement(*Element)
; Remove
EndIf
If *List\First = 0
*List\First = *Element
EndIf
If *List\Last <> 0
*List\Last\Next = *Element
*Element\Previous = *List\Last
EndIf
*List\Last = *Element
*Element\Next = 0
*Element\List = *List
*List\Size = *List\Size + 1
Result = 1
EndIf
EndIf
ProcedureReturn Result
EndProcedure
Procedure.l LLRemoveElement(*Element.LLElement)
Protected Result = 0
If *Element <> 0
If *Element\List <> 0
If *Element = *Element\List\First
*Element\List\First = *Element\Next
EndIf
If *Element = *Element\List\Last
*Element\List\Last = *Element\Previous
EndIf
If *Element\Previous <> 0
*Element\Previous\Next = *Element\Next
EndIf
If *Element\Next <> 0
*Element\Next\Previous = *Element\Previous
EndIf
*Element\List\Size = *Element\List\Size - 1
*Element\List = 0
*Element\Next = 0
*Element\Previous = 0
Result = 1
EndIf
EndIf
ProcedureReturn Result
EndProcedure