Import mojo
Class enemy
Field x:Int,y:Int
Field w:Int,h:Int
Field state:String
Field deleteme:Bool
Field cooldown:Int
Field waittime:Int
Field path:List<pathnode> = New List<pathnode>
Method New()
w = mymap.tilewidth-4
h = mymap.tileheight-4
findstartpos()
End Method
Method update()
' enemy shoot diagnal
If Rnd(100)<2 Then
Local px:Int=myplayer.x
Local py:Int=myplayer.y
If distance(px,py,x,y) < 200
If px<x And py<y And mymap.mapcollide(x-5,y-5,w/3,h/3) = False
mybullet.AddLast(New bullet(x,y,"leftup","enemy"))
End If
If px>x And py<y And mymap.mapcollide(x+5,y-5,w/3,h/3) = False
mybullet.AddLast(New bullet(x,y,"rightup","enemy"))
End If
If px<x And py>y And mymap.mapcollide(x-5,y+5,w/3,h/3) = False
mybullet.AddLast(New bullet(x,y,"leftdown","enemy"))
End If
If px>x And py>y And mymap.mapcollide(x+5,y+5,w/3,h/3) = False
mybullet.AddLast(New bullet(x,y,"rightdown","enemy"))
End If
End If
End If
'enemy shoot horizontal vertical
If Rnd(100)<2 Then
Local px:Int=myplayer.x
Local py:Int=myplayer.y
If distance(px,py,x,y) < 200
If px<x And mymap.mapcollide(x-5,y,w/3,h/3) = False
mybullet.AddLast(New bullet(x,y,"left","enemy"))
End If
If px>x And mymap.mapcollide(x+5,y-5,w/3,h/3) = False
mybullet.AddLast(New bullet(x,y,"right","enemy"))
End If
If py>y And mymap.mapcollide(x,y+5,w/3,h/3) = False
mybullet.AddLast(New bullet(x,y,"down","enemy"))
End If
If py<y And mymap.mapcollide(x,y-5,w/3,h/3) = False
mybullet.AddLast(New bullet(x,y,"up","enemy"))
End If
End If
End If
' move around the enemy
If path.IsEmpty = False
' add 2 to the destination coords or gets stuck
Local dx:Int=path.First.x*mymap.tilewidth+2
Local dy:Int=path.First.y*mymap.tileheight+2
If x<dx And mymap.mapcollide(x+1,y,w,h) = False Then x+=1
If x>dx And mymap.mapcollide(x-1,y,w,h) = False Then x-=1
If y<dy And mymap.mapcollide(x,y+1,w,h) = False Then y+=1
If y>dy And mymap.mapcollide(x,y-1,w,h) = False Then y-=1
'if near destination
If distance(x,y,dx,dy) < 2 Then
path.RemoveFirst
x = dx
y = dy
End If
End If
'if no more moves left find new cover spot
If path.IsEmpty
If waittime>0 Then waittime-=1
If waittime<=0
For Local i:Int=0 Until 100
Local dx:Int=Rnd(2,mymap.mapwidth-2)
Local dy:Int=Rnd(2,mymap.mapheight-2)
If mymap.covermap[dx][dy] = 1 Then
createpath(dx,dy)
waittime=200
Exit
End If
Next
End If
End If
'if player is nearby then move to closest cover spot
If distance(myplayer.x,myplayer.y,x,y) < 160
If cooldown>0 Then cooldown-=1
If cooldown=0
cooldown=100
Local d:Int=10000
Local destx:Int,desty:Int
Local fnd:Bool=False
' random spots until coverspot, log closest
For Local i:Int=0 Until 250
Local dx:Int=Rnd(2,mymap.mapwidth-2)
Local dy:Int=Rnd(2,mymap.mapheight-2)
If mymap.covermap[dx][dy] = 1 Then
Local d2:Int = distance(dx,dy,x/mymap.tilewidth,y/mymap.tileheight)
If d2 < d Then
d = d2
destx = dx
desty = dy
fnd=True
End If
End If
Next
' if we have a new dest then plan it
If fnd=True Then createpath(destx,desty)
End If
End If
End Method
Method createpath(ex:Int,ey:Int)
path = New List<pathnode>
Local dx:Int=x/mymap.tilewidth
Local dy:Int=y/mymap.tileheight
' x = dx*mymap.tilewidth
' y = dy*mymap.tileheight
myastar.findpath(dx,dy,ex,ey)
For Local i:=Eachin myastar.path
path.AddLast(New pathnode(i.x,i.y))
Next
End Method
Method drawpath()
SetColor 255,0,0
For Local i:=Eachin path
DrawOval i.x*mymap.tilewidth,i.y*mymap.tileheight,w/2,h/2
Next
End Method
Method findstartpos()
Local cnt:Int=400
Local cnt2:Int=0
Repeat
Local nx:Int=Rnd(0,mymap.screenwidth-20)
Local ny:Int=Rnd(0,mymap.screenheight-20)
Local found:Bool=True
' if the map position a tile
If mymap.mapcollide(nx,ny,w,h) Then found = False
' if the position is to close other enemy
For Local i:=Eachin myenemy
If i=Self Then Continue
If distance(i.x,i.y,nx,ny) < 30 Then found = False ;Exit
Next
' if the position to close to player
If distance(myplayer.x,myplayer.y,nx,ny) < cnt Then found = False
If found = True Then
x = nx
y = ny
Exit
Else
If cnt>32 Then cnt -=1
End If
Forever
End Method
Method draw()
SetColor 255,0,255
DrawOval x,y,w,h
End Method
Function distance:Int(x1:Int,y1:Int,x2:Int,y2:Int)
Return Abs(x2-x1)+Abs(y2-y1)
End Function
End Class
Class bullet
Field x:Float,y:Float
Field w:Int,h:Int
Field mx:Int,my:Int
Field direction:String
Field deleteme:Bool=False
Field speed:Int=2
Field shooter:String
Method New(x:Int,y:Int,direction:String,shooter:String)
Self.x = x
Self.y = y
Self.w = myplayer.w/3
Self.h = myplayer.h/3
Self.shooter = shooter
Self.direction = direction
If direction = "left" Then mx = -1
If direction = "right" Then mx = 1
If direction = "up" Then my = -1
If direction = "down" Then my = 1
If direction = "leftup" Then mx = -1 ; my = -1
If direction = "rightup" Then mx = 1 ; my = -1
If direction = "leftdown" Then mx = -1 ; my = 1
If direction = "rightdown" Then mx = 1 ; my = 1
End Method
Method update()
'move the bullet and see collision with walls
For Local i:Int=0 Until speed
x += mx
y += my
If x < 0 Or x > mymap.screenwidth Then deleteme = True
If y < 0 Or y > mymap.screenheight Then deleteme = True
If mymap.mapcollide(x,y,w,h) Then deleteme = True
Next
' collision with bullet vs enemy
' delete bullet and delete enemy
If shooter = "player"
For Local i:=Eachin myenemy
If distance(i.x+(i.w/2),i.y+(i.h/2),x,y)<myplayer.w/1.5 Then
deleteme = True
i.deleteme = True
End If
Next
End If
' collision with bullet vs player
' delete bullet and kill player
If shooter = "enemy"
If distance(myplayer.x+(myplayer.w/2),myplayer.y+(myplayer.h/2),x,y)<myplayer.w/1.5 Then
deleteme = True
myplayer.died = True
End If
End If
End Method
Method draw()
SetColor 255,255,0
DrawOval x,y,w,h
End Method
Function distance:Int(x1:Int,y1:Int,x2:Int,y2:Int)
Return Abs(x2-x1)+Abs(y2-y1)
End Function
End Class
Class player
Field x:Float,y:Float
Field w:Int,h:Int
Field direction:String="up"
Field died:Bool=False
Method New()
w = mymap.tilewidth-4
h = mymap.tileheight-4
findstartingpos()
End Method
Method update()
playercontrols()
makecovermap()
End Method
Method playercontrols()
' movement
If KeyDown(KEY_UP) And Not mymap.mapcollide(x,y-1,w,h)
y-=1
direction = "up"
End If
If KeyDown(KEY_LEFT) And Not mymap.mapcollide(x-1,y,w,h)
x-=1
direction = "left"
End If
If KeyDown(KEY_RIGHT) And Not mymap.mapcollide(x+1,y,w,h)
x+=1
direction = "right"
End If
If KeyDown(KEY_DOWN) And Not mymap.mapcollide(x,y+1,w,h)
y+=1
direction = "down"
End If
If KeyDown(KEY_LEFT) And KeyDown(KEY_UP) Then direction = "leftup"
If KeyDown(KEY_RIGHT) And KeyDown(KEY_UP) Then direction = "rightup"
If KeyDown(KEY_LEFT) And KeyDown(KEY_DOWN) Then direction = "leftdown"
If KeyDown(KEY_RIGHT) And KeyDown(KEY_DOWN) Then direction = "rightdown"
' shooting
If KeyHit(KEY_F)
mybullet.AddLast(New bullet(x,y,direction,"player"))
End If
End Method
Method makecovermap()
If Rnd(60)>2 Then Return
For Local y:Int=0 Until mymap.mapheight
For Local x:Int=0 Until mymap.mapwidth
mymap.covermap[x][y] = 1
If mymap.map[x][y] <> 0 Then mymap.covermap[x][y] = 2
Next
Next
' shoot bullets into random directions around
' the player and see if any position is a cover position
For Local i:Int=0 Until 600
Local x2:Float=x
Local y2:Float=y
Local xa:Float=Rnd(-1,1)
Local ya:Float=Rnd(-1,1)
For Local d:Int=0 Until 40
x2+=xa*Float(mymap.tilewidth/2)
y2+=ya*Float(mymap.tileheight/2)
Local mx:Int=x2/mymap.tilewidth
Local my:Int=y2/mymap.tileheight
If mx>=0 And my>=0 And mx<mymap.mapwidth And my<mymap.mapheight
mymap.covermap[mx][my] = 0
Else
Exit
End If
If mymap.mapcollide(x2,y2,w/3,h/3) Then Exit
Next
Next
'
' Remove every coverpoint except if they
' are near a wall.
For Local y2:Int=0 Until mymap.mapheight
For Local x2:Int=0 Until mymap.mapwidth
Local remove:Bool=True
For Local y3:Int=y2-1 To y2+1
For Local x3:Int=x2-1 To x2+1
If x3<0 Or y3<0 Or x3>=mymap.mapwidth Or y3>=mymap.mapheight Then Continue
If mymap.map[x3][y3] <> 0 Then remove = False ; Exit
Next
Next
If remove = True Then
mymap.covermap[x2][y2] = 0
End If
Next
Next
'if closer to the player then higher movement cost per tile
For Local y2:Int=0 Until mymap.mapheight
For Local x2:Int=0 Until mymap.mapwidth
If myastar.map[x2][y2] <> 1000 Then myastar.map[x2][y2] = 100-distance(myplayer.x/mymap.tilewidth,myplayer.y/mymap.tileheight,x2,y2)
If myastar.map[x2][y2] < 85 Then myastar.map[x2][y2] = 0
If mymap.covermap[x2][y2] = 1 Then myastar.map[x2][y2] = 0
Next
Next
End Method
Method findstartingpos()
Repeat
Local x1:Int = Rnd(0,mymap.mapwidth)
Local y1:Int = Rnd(0,mymap.mapheight)
Local istaken:Bool=False
For Local x2:Int=x1-4 To x1+4
For Local y2:Int=y1-4 To y1+4
If x2<0 Or y2<0 Or x2>=mymap.mapwidth Or y2>=mymap.mapheight Then Continue
If mymap.map[x2][y2] <> 0 Then istaken = True ; Exit
Next
Next
If istaken=False Then
x = x1*mymap.tilewidth
y = y1*mymap.tileheight
Exit
End If
Forever
End Method
Method draw()
SetColor 255,255,255
DrawOval x,y,w,h
End Method
Function distance:Int(x1:Int,y1:Int,x2:Int,y2:Int)
Return Abs(x2-x1)+Abs(y2-y1)
End Function
End Class
Class astar
Field mapwidth:Int,mapheight:Int
Field sx:Int,sy:Int
Field ex:Int,ey:Int
Field olmap:Int[][]
Field clmap:Int[][]
Field map:Int[][]
Field ol:List<openlist> = New List<openlist>
Field cl:List<closedlist> = New List<closedlist>
Field path:List<pathnode> = New List<pathnode>
Field xstep:Int[] = [0,-1,1,0]
Field ystep:Int[] = [-1,0,0,1]
Method New()
mapwidth = mymap.mapwidth
mapheight = mymap.mapheight
olmap = New Int[mapwidth][]
clmap = New Int[mapwidth][]
map = New Int[mapwidth][]
For Local i:Int=0 Until mapwidth
olmap[i] = New Int[mapheight]
clmap[i] = New Int[mapheight]
map[i] = New Int[mapheight]
Next
' Copy the map into the astar class map
For Local y:=0 Until mapheight
For Local x:=0 Until mapwidth
map[x][y] = mymap.map[x][y]
Next
Next
End Method
' This creates the map and copies the
' path in the path list
Method findpath:Bool(sx:Int,sy:Int,ex:Int,ey:Int)
If sx = ex And sy = ey Then Return False
Self.sx = sx
Self.sy = sy
Self.ex = ex
Self.ey = ey
For Local y=0 Until mapheight
For Local x=0 Until mapwidth
olmap[x][y] = 0
clmap[x][y] = 0
Next
Next
ol.Clear
cl.Clear
path.Clear
ol.AddFirst(New openlist(sx,sy))
Local tx:Int
Local ty:Int
Local tf:Int
Local tg:Int
Local th:Int
Local tpx:Int
Local tpy:Int
Local newx:Int
Local newy:Int
Local lowestf:Int
olmap[sx][sy] = 1
While ol.IsEmpty() = False
lowestf = 100000
For Local i:=Eachin ol
If i.f < lowestf
lowestf = i.f
tx = i.x
ty = i.y
tf = i.f
tg = i.g
th = i.h
tpx = i.px
tpy = i.py
End If
Next
If tx = ex And ty = ey
cl.AddLast(New closedlist(tx,ty,tpx,tpy))
findpathback
Return True
Else
removefromopenlist(tx,ty)
olmap[tx][ty] = 0
clmap[tx][ty] = 1
cl.AddLast(New closedlist(tx,ty,tpx,tpy))
For Local i:Int=0 Until xstep.Length
Local x:Int=xstep[i]
Local y:Int=ystep[i]
newx = tx+x
newy = ty+y
If newx>=0 And newy>=0 And newx<mapwidth And newy<mapheight
If olmap[newx][newy] = 0
If clmap[newx][newy] = 0
olmap[newx][newy] = 1
Local gg = map[newx][newy]+1
Local hh = distance(newx,newy,ex,ey)
Local ff = gg+hh
ol.AddLast(New openlist(newx,newy,ff,gg,hh,tx,ty))
End If
End If
End If
Next
End If
Wend
Return False
End Method
Method drawpath:Void()
Local cnt:Int=1
For Local i:=Eachin path
SetColor 255,255,0
DrawOval i.x*mymap.tilewidth,i.y*mymap.tileheight,4,4
SetColor 255,255,255
DrawText cnt,i.x*mymap.tilewidth,i.y*mymap.tileheight
cnt+=1
Next
End Method
' Here we calculate back from the end back to the
' start and create the path list.
Method findpathback:Bool()
Local x=ex
Local y=ey
path.AddFirst(New pathnode(x,y))
Repeat
For Local i:=Eachin cl
If i.x = x And i.y = y
x = i.px
y = i.py
path.AddFirst(New pathnode(x,y))
End If
Next
If x = sx And y = sy Then Return True
Forever
End Method
Method removefromopenlist:Void(x1:Int,y1:Int)
For Local i:=Eachin ol
If i.x = x1 And i.y = y1
ol.Remove i
Exit
End If
Next
End Method
Function distance:Int(x1:Int,y1:Int,x2:Int,y2:Int)
Return Abs(x2-x1)+Abs(y2-y1)
End Function
End Class
Class map
Field mapwidth:Int
Field mapheight:Int
Field screenwidth:Int
Field screenheight:Int
Field tilewidth:Float
Field tileheight:Float
Field map:Int[][]
Field covermap:Int[][]
Method New(screenwidth:Int,screenheight:Int,mapwidth:Int,mapheight:Int)
Self.screenheight = screenheight
Self.screenwidth = screenwidth
Self.mapwidth = mapwidth
Self.mapheight = mapheight
tilewidth = Float(screenwidth) / Float(mapwidth)
tileheight = Float(screenheight) / Float(mapheight)
map = New Int[mapwidth][]
covermap = New Int[mapwidth][]
For Local i:Int=0 Until mapwidth
map[i] = New Int[mapheight]
covermap[i] = New Int[mapheight]
Next
makemap(10)
End Method
Method makemap:Void(numobstacles:Int)
For Local y=0 Until mapheight
For Local x=0 Until mapwidth
map[x][y] = 0
Next
Next
' Here we create short lines on the map
' and sometimes draw some random blocks near them.
For Local i:Int=0 Until numobstacles
Local x1:Int=Rnd(4,mapwidth-4)
Local y1:Int=Rnd(4,mapheight-4)
Local dist:Int=Rnd(3,5)
Local angle:Int=Rnd(0,360)
Local x2:Float=x1
Local y2:Float=y1
While dist >= 0
x2 += Cos(angle) * 1
y2 += Sin(angle) * 1
dist -= 1
If x2<0 Or y2<0 Or x2>=mapwidth Or y2>=mapheight Then continue
map[x2][y2] = 1000
' If Rnd(10) < 2 Then
' If x2>2 And x2<mymap.mapwidth-2 And y2>2 And y2<mymap.mapheight-2 Then
' map[x2+Rnd(-1,1)][y2+Rnd(-1,1)] = 10
' end if
' End If
Wend
Next
End Method
Method mapcollide:Bool(x:Int,y:Int,w:Int,h:Int)
Local lefttopx:Int =((x)/tilewidth)
Local lefttopy:Int =((y)/tileheight)
Local righttopx:Int =((x+w)/tilewidth)
Local righttopy:Int =((y)/tileheight)
Local leftbottomx:Int =((x)/tilewidth)
Local leftbottomy:Int =((y+h)/tileheight)
Local rightbottomx:Int =((x+w)/tilewidth)
Local rightbottomy:Int =((y+h)/tileheight)
If lefttopx < 0 Or lefttopx >= mapwidth Then Return True
If lefttopy < 0 Or lefttopy >= mapheight Then Return True
If righttopx < 0 Or righttopx >= mapwidth Then Return True
If righttopy < 0 Or righttopy >= mapheight Then Return True
If leftbottomx < 0 Or leftbottomx >= mapwidth Then Return True
If leftbottomy < 0 Or leftbottomy >= mapheight Then Return True
If rightbottomx < 0 Or rightbottomx >= mapwidth Then Return True
If rightbottomy < 0 Or rightbottomy >= mapheight Then Return True
If map[lefttopx][lefttopy] <> 0 Then Return True
If map[righttopx][righttopy] <> 0 Then Return True
If map[leftbottomx][leftbottomy] <> 0 Then Return True
If map[rightbottomx][rightbottomy] <> 0 Then Return True
Return False
End Method
Method drawmap:Void()
For Local y=0 Until mapheight
For Local x=0 Until mapwidth
If map[x][y] = 1000
SetColor 255,255,255
DrawRect x*tilewidth,y*tileheight,tilewidth,tileheight
End If
Next
Next
End Method
Method drawcovermap:Void()
For Local y=0 Until mapheight
For Local x=0 Until mapwidth
If covermap[x][y] = 1
SetColor 0,15,0
DrawOval x*mymap.tilewidth,y*mymap.tileheight,tilewidth,tileheight
End If
Next
Next
End Method
Function distance:Int(x1:Int,y1:Int,x2:Int,y2:Int)
Return Abs(x2-x1)+Abs(y2-y1)
End Function
End Class
' The open list used by the astar
Class openlist
Field x:Int
Field y:Int
Field f:Int
Field g:Int
Field h:Int
Field px:Int
Field py:Int
Method New( x:Int=0,y:Int=0,f:Int=0,
g:Int=0,h:Int=0,px:Int=0,py:Int=0)
Self.x=x
Self.y=y
Self.f=f
Self.g=g
Self.h=h
Self.px=px
Self.py=py
End Method
End Class
' The closed list used by the astar
Class closedlist
Field x:Int
Field y:Int
Field px:Int
Field py:Int
Method New(x:Int,y:Int,px:Int,py:Int)
Self.x = x
Self.y = y
Self.px = px
Self.py = py
End Method
End Class
' the pathnodes (x and y variables)
' used by the astar
Class pathnode
Field x:Int
Field y:Int
Method New(x:Int,y:Int)
Self.x = x
Self.y = y
End Method
End Class
Global mymap:map
Global myastar:astar
Global myplayer:player
Global mybullet:List<bullet> = New List<bullet>
Global myenemy:List<enemy> = New List<enemy>
Class MyGame Extends App
Field playerwins:Int
Field enemywins:Int
Method OnCreate()
Seed = GetDate[4] * GetDate[5]
SetUpdateRate(60)
' Create a new map
mymap = New map(DeviceWidth(),DeviceHeight(),30,30)
myastar = New astar()
myplayer = New player()
For Local i:Int=0 Until 10
myenemy.AddLast(New enemy())
Next
End Method
Method OnUpdate()
myplayer.update()
For Local i:=Eachin myenemy
i.update()
Next
For Local i:=Eachin myenemy
If i.deleteme = True Then myenemy.Remove(i)
Next
For Local i:=Eachin mybullet
i.update()
Next
For Local i:=Eachin mybullet
If i.deleteme = True Then mybullet.Remove(i)
Next
' if no more enemies then reset level
If myenemy.IsEmpty Or myplayer.died Or KeyHit(KEY_TILDE)
If myplayer.died Then enemywins+=1
If myenemy.IsEmpty Then playerwins+=1
mymap = New map(DeviceWidth(),DeviceHeight(),30,30)
myastar = New astar()
myenemy = New List<enemy>
mybullet = New List<bullet>
myplayer = New player()
For Local i:Int=0 Until 10
myenemy.AddLast(New enemy())
Next
End If
End Method
Method OnRender()
Cls 0,0,0
SetColor 255,255,255
mymap.drawmap()
mymap.drawcovermap()
For Local i:=Eachin myenemy
i.draw()
Next
For Local i:=Eachin mybullet
i.draw()
Next
myplayer.draw()
'drawdebug
SetColor 255,255,255
DrawText "AI - 'Taking Cover' Locations - Example.`(Tilde = new level)",0,DeviceHeight-15
DrawText "Controls - cursor u/d/l/r and F - fire",0,15
DrawText "Matches Player Wins : " + playerwins + " vs Enemy Wins : " + enemywins,DeviceWidth/2,0,.5,0
End Method
End Class
Function drawdebug()
SetColor 255,255,255
For Local i:=Eachin myenemy
i.drawpath()
Next
Return
Scale(.7,.7)
For Local y:Int=0 Until mymap.mapwidth
For Local x:Int=0 Until mymap.mapheight
DrawText myastar.map[x][y],(x*mymap.tilewidth)*1.4,(y*mymap.tileheight)*1.4
Next
Next
End Function
Function Main()
New MyGame()
End Function
Artificial intelligence/templates/examples/rts/rpg/strategy ect. in MonkeyX/CerberusX language. You can download the free version of MonkeyX from itch.io or Cerberus-x.com The Flash applets will stop working in around 2020.
Thursday, October 19, 2017
Monkey-X - Ai Taking Cover - Shooter - code example
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.