' Wall Tracing from the book Ai For Game Developers. ' Random Maps based on code from Rogue Basin. ' Import mojo ' 'this is the wall tracing entity Class entity Field tilex:Int,tiley:Int Field direction:Int=8 Field speed:Int Method New() findstartposition End Method Method update() walltrace End Method Method walltrace() If direction = 4 If mymap.map[tilex-1][tiley] = 1 tilex -= 1 direction = 2 Elseif mymap.map[tilex][tiley+1] = 1 tiley += 1 direction = 4 Elseif mymap.map[tilex+1][tiley] = 1 tilex += 1 direction = 6 Elseif mymap.map[tilex][tiley-1] = 1 tiley -= 1 direction = 8 End If Elseif direction = 6 If mymap.map[tilex][tiley+1] = 1 tiley += 1 direction = 4 Elseif mymap.map[tilex+1][tiley] = 1 tilex += 1 direction = 6 Elseif mymap.map[tilex][tiley-1] = 1 tiley -= 1 direction = 8 Elseif mymap.map[tilex-1][tiley] = 1 tilex -= 1 direction = 2 End If Elseif direction = 8 If mymap.map[tilex+1][tiley] = 1 tilex += 1 direction = 6 Elseif mymap.map[tilex][tiley-1] = 1 tiley -= 1 direction = 8 Elseif mymap.map[tilex-1][tiley] = 1 tilex -= 1 direction = 2 Elseif mymap.map[tilex][tiley+1] = 1 tiley += 1 direction = 4 End If Elseif direction = 2 If mymap.map[tilex][tiley-1] = 1 tiley -= 1 direction = 8 Elseif mymap.map[tilex-1][tiley] = 1 tilex -= 1 direction = 2 Elseif mymap.map[tilex][tiley+1] = 1 tiley += 1 direction = 4 Elseif mymap.map[tilex+1][tiley] = 1 tilex += 1 direction = 6 End If End If End Method Method findstartposition() ' for tracing the wall we need to be sure ' that the entity is spawned against a wall ' here we check for a random pos on the map ' on the floor(1) with a wall(2) at y+1 Local exitloop:Bool=False While exitloop = False Local x:Int=Rnd(3,mymap.mapwidth-3) Local y:Int=Rnd(3,mymap.mapheight-3) If mymap.map[x][y] = 1 If mymap.map[x][y+1] = 2 exitloop = True tilex = x tiley = y End If End If Wend End Method Method draw() SetColor 255,0,0 DrawOval tilex*mymap.tilewidth, tiley*mymap.tileheight, mymap.tilewidth+1, mymap.tileheight+1 End Method End Class Class map Field tilewidth:Float Field tileheight:Float Field mapwidth:Int Field mapheight:Int Field screenwidth:Int Field screenheight:Int Field map:Int[][] Method New( screenwidth:Int, screenheight:Int, mapwidth:Int, mapheight:Int) Self.screenwidth = screenwidth Self.screenheight = screenheight Self.mapwidth = mapwidth Self.mapheight = mapheight Self.tilewidth = Float(screenwidth)/Float(mapwidth) Self.tileheight = Float(screenheight)/Float(mapheight) map = New Int[mapwidth][] For Local i=0 Until mapwidth map[i] = New Int[mapheight] Next map[mapwidth/2][mapheight/2] = 3 ' 3 is a door makemap End Method Method makemap() Local timeout:Int While timeout<(mapwidth*mapheight)*20 timeout+=1 Local x:Int=Rnd(11,mapwidth-11) Local y:Int=Rnd(11,mapheight-11) If map[x][y] = 3 makeroom(x,y) End If Wend 'here we turn doors into walls 'if they should be walls For Local y1=1 Until mapheight-1 For Local x1=1 Until mapwidth-1 If map[x1][y1] = 3 Local cnt:Int=0 For Local y2=y1-1 To y1+1 For Local x2=x1-1 To x1+1 If map[x2][y2] = 2 Then cnt+=1 Next Next If cnt>3 Then map[x1][y1] = 2 End If Next Next 'here we turn doors into walls if they ' touch tiles that are nothing (0) For Local y1=1 Until mapheight-1 For Local x1=1 Until mapwidth-1 If map[x1][y1] = 3 Local cnt:Int=0 For Local y2=y1-1 To y1+1 For Local x2=x1-1 To x1+1 If map[x2][y2] = 0 Then cnt+=1 Next Next If cnt>0 Then map[x1][y1] = 2 End If Next Next 'here we turn the doors into floors For Local y1=0 Until mapheight For Local x1=0 Until mapwidth If map[x1][y1] = 3 Then map[x1][y1] = 1 Next Next End Method Method makeroom(x:Int,y:Int) Local side:String If map[x][y-1] = 0 side="up" Elseif map[x+1][y] = 0 side="right" Elseif map[x][y+1] = 0 side="down" Elseif map[x-1][y] = 0 side="left" End If Local w:Int=Rnd(5,10) Local h:Int=Rnd(5,10) If side="up" Local x1:Int=x-w/2 Local y1:Int=y-h If roomfits(x1,y1,w,h) insertroom(x1,y1,w,h+1) 'door up map[x1+Rnd(2,w-2)][y1] = 3 ' door right map[x1+w-1][y1+Rnd(2,h-2)] = 3 'door left map[x1][y1+Rnd(2,h-2)] = 3 End If End If If side="right" Local x1:Int=x+1 Local y1:Int=y-h/2 If roomfits(x1,y1,w,h) insertroom(x1-1,y1,w,h) 'door up map[x1+Rnd(2,w-2)][y1] = 3 'door down map[x1+Rnd(2,w-2)][y1+h-1] = 3 ' door right map[x1+w-2][y1+Rnd(2,h-2)] = 3 End If End If If side="left" Local x1:Int=x-w Local y1:Int=y-h/2 If roomfits(x1,y1,w,h) insertroom(x1,y1,w+1,h) 'door up map[x1+Rnd(2,w-2)][y1] = 3 'door down map[x1+Rnd(2,w-2)][y1+h-1] = 3 'door left map[x1][y1+Rnd(2,h-2)] = 3 End If End If If side="down" Local x1:Int=x-w/2 Local y1:Int=y+1 If roomfits(x1,y1,w,h) insertroom(x1,y1-1,w,h) 'door down map[x1+Rnd(2,w-2)][y1+h-2] = 3 'door left map[x1][y1+Rnd(2,h-2)] = 3 ' door right map[x1+w-1][y1+Rnd(2,h-2)] = 3 End If End If End Method Method insertroom(x,y,w,h) For Local y2=y Until y+h For Local x2=x Until x+w If map[x2][y2] <> 3 Then map[x2][y2] = 2 Next Next For Local y2=y+1 Until y+h-1 For Local x2=x+1 Until x+w-1 map[x2][y2] = 1 Next Next End Method Method roomfits(x:Int,y:Int,w:Int,h:Int) For Local y1=y Until y+h For Local x1=x Until x+w If map[x1][y1] = 1 Then Return False Next Next Return True End Method Method draw() For Local y=0 Until mapheight For Local x=0 Until mapwidth Select map[x][y] Case 0 Case 1'floor SetColor 100,100,100 DrawRect x*tilewidth, y*tileheight, tilewidth+1, tileheight+1 Case 2'wall SetColor 200,200,200 DrawRect x*tilewidth, y*tileheight, tilewidth+1, tileheight+1 Case 3'wall SetColor 244,244,0 DrawRect x*tilewidth, y*tileheight, tilewidth+1, tileheight+1 End Select Next Next End Method End Class Global mymap:map Global myentity:List<entity> = New List<entity> Class MyGame Extends App Field cnt:Int Method OnCreate() SetUpdateRate(5) Seed = GetDate[4]+GetDate[5] newmap End Method Method OnUpdate() cnt+=1 For Local i:=Eachin myentity i.update Next If KeyHit(KEY_SPACE) Or cnt>200 cnt=0 newmap End If End Method Method OnRender() Cls 0,0,0 mymap.draw For Local i:=Eachin myentity i.draw Next SetColor 255,255,255 DrawText "Monkey-X - Wall Tracing on Random Maps",10,10 End Method End Class Function Main() New MyGame() End Function Function newmap() Local s:Int=Rnd(40,100) mymap = New map(DeviceWidth,DeviceHeight,s,s) myentity = New List<entity> For Local i=0 Until Rnd(1,s/3) myentity.AddLast(New entity()) Next 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.
Monday, February 20, 2017
Monkey-X - Wall Tracing on Random Maps - code example
Monkey-X - Path Following - code example
' Path following - from the book - ai for game developers Import mojo Class entity Field tilex:Int Field tiley:Int Field entwidth:Int Field entheight:Int Field tilewidth:Float Field tileheight:Float Field mapwidth:Int Field mapheight:Int Field direction:Int=1 ' 1 is up, 2 is upright ect clockwise Method New() mapwidth = mypathfollowing.mapwidth mapheight = mypathfollowing.mapheight entwidth = mypathfollowing.tilewidth entheight = mypathfollowing.tileheight tilewidth = mypathfollowing.tilewidth tileheight = mypathfollowing.tileheight findstartpos End Method Method update() ' terrain analysis ' here we check clockwise around the entity its position ' and fill the ta(terrainanalysis) array with 0 if there is no read there ' and 10 if there is a road Local ta:Int[] = New Int[9] ta[1] = mypathfollowing.map[tiley-1][tilex] ta[2] = mypathfollowing.map[tiley-1][tilex+1] ta[3] = mypathfollowing.map[tiley][tilex+1] ta[4] = mypathfollowing.map[tiley+1][tilex+1] ta[5] = mypathfollowing.map[tiley+1][tilex] ta[6] = mypathfollowing.map[tiley+1][tilex-1] ta[7] = mypathfollowing.map[tiley][tilex-1] ta[8] = mypathfollowing.map[tiley-1][tilex-1] For Local i = 1 To 8 If ta[i] = 0 ta[i] = 0 Else ta[i] = 10 End If Next ' weighted direction analysis ' here we use the current direction and add ' up the current direction with 2 and left and ' right from there with +1 and the opposite ' direction of the entity direction -1 If direction = 1 ta[1] += 2 ta[2] += 1 ta[8] += 1 ta[5] -= 1 End If If direction = 2 ta[2] += 2 ta[3] += 1 ta[1] += 1 ta[6] -= 1 End If If direction = 3 ta[3] += 2 ta[4] += 1 ta[2] += 1 ta[7] -= 1 End If If direction = 4 ta[4] += 2 ta[5] += 1 ta[3] += 1 ta[8] -= 1 End If If direction = 5 ta[5] += 2 ta[6] += 1 ta[4] += 1 ta[1] -= 1 End If If direction = 6 ta[6] += 2 ta[7] += 1 ta[5] += 1 ta[2] -= 1 End If If direction = 7 ta[7] += 2 ta[8] += 1 ta[6] += 1 ta[3] -= 1 End If If direction = 8 ta[8] += 2 ta[1] += 1 ta[7] += 1 ta[4] -= 1 End If ' choosing direction ' here we get the highest weight to get the next direction Local maxterrain:Int Local maxindex:Int For Local i=1 To 8 If ta[i] > maxterrain maxterrain = ta[i] maxindex = i End If Next ' update position ' here we use the new direction and move the entity and set ' the new or last direction If maxindex = 1 direction = 1 tiley -= 1 End If If maxindex = 2 direction = 2 tilex += 1 tiley -= 1 End If If maxindex = 3 direction = 3 tilex += 1 End If If maxindex = 4 direction = 4 tilex += 1 tiley += 1 End If If maxindex = 5 direction = 5 tiley += 1 End If If maxindex = 6 direction = 6 tilex -= 1 tiley += 1 End If If maxindex = 7 direction = 7 tilex -= 1 End If If maxindex = 8 direction = 8 tilex -= 1 tiley -= 1 End If End Method Method findstartpos() For Local y=0 Until mapheight For Local x=0 Until mapwidth If mypathfollowing.map[y][x] = 1 tilex = x tiley = y Return End If Next Next End Method Method draw() SetColor 255,255,0 DrawRect tilex*tilewidth,tiley*tileheight,entwidth,entheight End Method End Class Class pathfollowing Global map:Int[][] = [ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0], [0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0], [0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0], [0,0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0], [0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0], [0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0], [0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0], [0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0], [0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0], [0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0], [0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0], [0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0], [0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0], [0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0], [0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,0], [0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0], [0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0], [0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]] Field screenwidth:Int Field screenheight:Int Field mapwidth:Int Field mapheight:Int Field tilewidth:Float Field tileheight:Float Method New(width:Int,height:Int) mapwidth = map.Length mapheight = map[0].Length screenwidth = width screenheight = height tilewidth = Float(screenwidth)/Float(mapwidth) tileheight = Float(screenheight)/Float(mapheight) End Method Method draw() SetColor 200,100,0 For Local y = 0 Until mapheight For Local x = 0 Until mapwidth If map[y][x] = 1 DrawRect x*tilewidth,y*tileheight, tilewidth+1,tileheight+1 End If Next Next End Method End Class Global mypathfollowing:pathfollowing Global myentity:entity Class MyGame Extends App Method OnCreate() SetUpdateRate(5) mypathfollowing = New pathfollowing(DeviceWidth,DeviceHeight) myentity = New entity() End Method Method OnUpdate() myentity.update End Method Method OnRender() Cls 0,0,0 mypathfollowing.draw myentity.draw SetColor 255,255,255 DrawText "Monkey-X - Path Following Example",10,10 End Method End Class Function Main() New MyGame() End Function
Friday, February 10, 2017
Monkey-X - Particle Attraction - code example
Import mojo
Class gravitypoint Field x:Int,y:Int Method New(x:Int,y:Int) Self.x = x Self.y = y End Method Method draw() SetColor 255,255,0 DrawOval x,y,6,6 End Method End Class Class particle Field x:Float,y:Float Field incx:Float Field incy:Float Method New(x:Float,y:Float) Self.x = x Self.y = y incx = Rnd(-1,1) incy = Rnd(-1,1) End Method Method update() For Local i:=Eachin mygravitypoint Local a:Int=getangle(x,y,i.x,i.y) incx+=Cos(a)/2 incy+=Sin(a)/2 Next x+=incx y+=incy End Method Method draw() SetColor 255,255,255 DrawOval x,y,6,6 End Method Function getangle:Int(x1:Int,y1:Int,x2:Int,y2:Int) Local dx = x2 - x1 Local dy = y2 - y1 Return ATan2(dy,dx)+360 Mod 360 End Function End Class Global myparticle:List<particle> = New List<particle> Global mygravitypoint:List<gravitypoint> = New List<gravitypoint> Class MyGame Extends App Field counter:Int=0 Method OnCreate() Seed = GetDate[5]+GetDate[4] SetUpdateRate(60) createscene End Method Method OnUpdate() counter+=1 If counter > 300 counter = 0 createscene End If For Local i:=Eachin myparticle i.update Next End Method Method OnRender() Cls 0,0,0 For Local i:=Eachin myparticle i.draw Next If MouseDown(MOUSE_LEFT) For Local i:=Eachin mygravitypoint i.draw Next End If SetAlpha 1 SetColor 255,255,255 DrawText "Monkey-X - Particles with 'Attraction' Effect - Thnx to The Coding Train",0,0 DrawText "Particles get attracted to <Left Mouse Button> points on the map",0,DeviceHeight-20 End Method End Class Function createscene() myparticle = New List<particle> mygravitypoint = New List<gravitypoint> For Local i=0 Until Rnd(10,200) myparticle.AddLast(New particle(Rnd(DeviceWidth),Rnd(DeviceHeight))) Next For Local i=0 Until Rnd(1,20) mygravitypoint.AddLast(New gravitypoint(Rnd(DeviceWidth),Rnd(DeviceHeight))) Next End Function Function Main() New MyGame() End Function
Tuesday, February 7, 2017
Monkey-X - Hill Algorithm - code example
Import mojo Global screenwidth:Int=640 Global screenheight:Int=480 Class hill Field map:Int[][] Field mapwidth:Int Field mapheight:Int Field tilewidth:Float Field tileheight:Float Method New(width:Int=20,height:Int=20) mapwidth = width mapheight = height tilewidth=Float(screenwidth)/Float(mapwidth) tileheight=Float(screenheight)/Float(mapheight) map = New Int[mapwidth][] For Local i=0 Until mapwidth map[i] = New Int[mapheight] Next makehill End Method Method makehill() ' Here we draw rectangles on the map adding by +1 ' with the underlaying value. ' Local exitloop:Bool=False While exitloop = False Local x:Int=Rnd(-mapwidth/7,mapwidth) Local y:Int=Rnd(-mapheight/7,mapheight) Local w:Int=Rnd(1,mapwidth/5) Local h:Int=Rnd(1,mapheight/5) If Rnd(2)<1.8 w=1 h=1 End If ' if highest map value > 20 then stop drawing ' rectangles If addrect(x,y,w,h) > 24 Then exitloop = True Wend End Method Method addrect:Int(x:Int,y:Int,w:Int,h:Int) ' This function draws the rectangle and ' returns the highest value it makes in the map ' Local highestvalue:Int For Local y1=y To y+h For Local x1=x To x+w If x1>=0 And x1<mapwidth And y1>=0 And y1<mapheight Local a:Int = map[x1][y1] a+=1 If a>highestvalue Then highestvalue = a map[x1][y1] = a End If Next Next Return highestvalue End Method Method draw() ' world map ' This function draws the map For Local y=0 Until mapheight For Local x=0 Until mapwidth Local c:Int=map[x][y]*10 'water (low) If map[x][y] > 0 Then SetColor 0,0,100 If map[x][y] > 5 Then SetColor 0,0,200 If map[x][y] > 8 Then SetColor 0,0,250 'grass (higher) If map[x][y] >=10 Then SetColor 0,c,0 'hills (higher) If map[x][y] >=15 Then SetColor c,c/2,0 'mountains (highest) If map[x][y] >=20 Then SetColor c,c,c DrawRect Float(x)*tilewidth,Float(y)*tileheight,tilewidth+1,tileheight+1 Next Next 'heightmap SetScissor 320,0,320,480 For Local y=0 Until mapheight For Local x=0 Until mapwidth Local c:Int=map[x][y]*10 SetColor c,c,c DrawRect Float(x)*tilewidth,Float(y)*tileheight,tilewidth+1,tileheight+1 Next Next SetScissor 0,0,640,480 End Method End Class Global mymap:hill Class MyGame Extends App Field count:Int Method OnCreate() Seed = GetDate[5] SetUpdateRate(1) mymap = New hill(50,50) End Method Method OnUpdate() count+=1 If count>2 Then count=0 Local s:Int=Rnd(32,320) mymap = New hill(s,s) End If End Method Method OnRender() Cls 0,0,0 mymap.draw SetColor 255,255,255 DrawText "Hill Algorithm",0,0 DrawText "World Map",0,20 DrawText "Height map",320,20 End Method End Class Function Main() New MyGame() End Function
Friday, February 3, 2017
Monkey-X - Circle Packing - Code Example
Import mojo Class circle Field x:Int,y:Int Field radius:Int Method New() radius = 2 Local exitloop:Bool=False While exitloop = False exitloop=True Self.x = Rnd(DeviceWidth) Self.y = Rnd(DeviceHeight) For Local i:=Eachin mycircle If i.x = x And i.y = y Then Else If circleoverlap( i.x,i.y,i.radius, x,y,2) = True Then exitloop=False End If End If Next Wend End Method Method grow() If x-radius<0 Then Return If x+radius>DeviceWidth Then Return If y-radius<0 Then Return If y+radius>DeviceHeight Then Return For Local i:=Eachin mycircle If i.x=x And i.y=y Then Else If circleoverlap( x,y,radius, i.x,i.y,i.radius) Return End If End If Next radius+=1 End Method Method draw() SetColor 255,255,255 DrawCircle x,y,radius SetColor 0,0,0 DrawCircle x,y,radius-2 End Method Function circleoverlap:Bool(x1:Int,y1:Int,r1:Int,x2:Int,y2:Int,r2:Int) Local dx:Int = x1-x2 Local dy:Int = y1-y2 Local r:Int = r1+r2 If dx*dx+dy*dy <= r*r Then Return True Else Return False End Function End Class Global mycircle:List<circle> = New List<circle> Class MyGame Extends App Field counter:Int Method OnCreate() SetUpdateRate(5) mycircle.AddLast(New circle()) End Method Method OnUpdate() If counter>100 Then Return For Local i=0 Until 5 mycircle.AddLast(New circle()) Next For Local i:=Eachin mycircle i.grow Next counter+=1 End Method Method OnRender() Cls 0,0,0 SetColor 255,255,255 For Local i:=Eachin mycircle i.draw Next End Method End Class Function Main() New MyGame() End Function
Monkey-X - Getting Started - Default Parameters - code example
Import mojo Class MyGame Extends App Method OnCreate() SetUpdateRate(60) End Method Method OnUpdate() End Method Method OnRender() Cls 0,0,0 SetColor 255,255,255 Scale 2,2 defaultparameter(0,0) defaultparameter(0,20,100,200,"Not Default") End Method End Class ' Like with variable declaration you can set a value to ' a variable in a function or method. Function defaultparameter(x:Int,y:Int,a:Int=10,b:Int=20,c:String="default") DrawText "a="+a+" b="+b+" c="+c,x,y End Function Function Main() New MyGame() End Function
Monkey-X - Getting Started - Exit - code example
Import mojo
Class MyGame Extends App
Method OnCreate()
SetUpdateRate(60)
End Method
Method OnUpdate()
End Method
Method OnRender()
Cls 0,0,0
SetColor 255,255,255
Scale 2,2
For Local i=0 To 10
DrawText "value i = "+i,0,i*10
If i>=5 Then Exit ' <<<<<<<<<< Exit here on condition
Next
End Method
End Class
Function Main()
New MyGame()
End Function
Monkey-X - Beginners - Select and Default - code example
Import mojo Class MyGame Extends App Field lastvalue:Int=0 Field laststring:String Method OnCreate() SetUpdateRate(60) End Method Method OnUpdate() If KeyDown(KEY_1) Then lastvalue = 1 If KeyDown(KEY_2) Then lastvalue = 2 If KeyDown(KEY_3) Then laststring = "the string" End Method Method OnRender() Cls 0,0,0 SetColor 255,255,255 Scale 2,2 DrawText "Press the 1 or 2 or 3 keys",0,0 Select lastvalue Case 1 DrawText "Last key pressed is 1",10,20 Case 2 DrawText "Last key pressed is 2",10,20 Default DrawText "1 or 2 key was not pressed.",10,20 End Select Select laststring Case "the string" DrawText "3 Key was pressed.",10,40 Default DrawText "3 Key was not pressed.",10,40 End Select End Method End Class Function Main() New MyGame() End Function
Monkey-X - Floodfill map distance - code example
Import mojo Global screenwidth:Int=640 Global screenheight:Int=480 Global mapwidth:Int=20 Global mapheight:Int=20 Global tilewidth:Float=Float(screenwidth)/Float(mapwidth) Global tileheight:Float=Float(screenheight)/Float(mapheight) Class MyGame Extends App Field flood:Bool=False Field fillval:Int=1 Field delay:Int Field map:Int[][] Field mapd:Int[][] 'map containing the distance (distance map) Field floodx:Stack<Int> = New Stack<Int> 'flood Field floody:Stack<Int> = New Stack<Int> Field floodv:Stack<Int> = New Stack<Int> 'distance Field mx:Int[] = [0,1,0,-1] 'expand up/right/down/left Field my:Int[] = [-1,0,1,0] Method OnCreate() SetUpdateRate(60) map = New Int[mapwidth][] For Local i=0 Until mapwidth map[i] = New Int[mapheight] Next mapd = New Int[mapwidth][] For Local i=0 Until mapwidth mapd[i] = New Int[mapheight] Next For Local x=0 Until mapwidth/2 map[x][mapheight/2] = 6 Next End Method Method OnUpdate() Local tx:Int=MouseX()/tilewidth Local ty:Int=MouseY()/tileheight If MouseDown(MOUSE_LEFT) And flood=False And map[tx][ty] <> 6 Print "Flooding - "+Millisecs() floodx.Clear floody.Clear floodv.Clear For Local y=0 Until mapheight For Local x=0 Until mapwidth mapd[x][y] = 0 Next Next floodx.Push(tx) floody.Push(ty) floodv.Push(1) flood = True fillval+=1 If fillval > 5 Then fillval = 0 map[tx][ty] = fillval mapd[tx][ty] = 1 End If If flood = True If floodx.Length > 0 Local x1:Int=floodx.Top Local y1:Int=floody.Top Local v1:Int=floodv.Top floodx.Pop floody.Pop floodv.Pop For Local i=0 Until 4 Local x2:Int=x1+mx[i] Local y2:Int=y1+my[i] If x2>=0 And x2<mapwidth And y2>=0 And y2<mapheight If map[x2][y2] <> fillval If map[x2][y2] <> 6 map[x2][y2] = fillval ' if you insert the new locations at the bottom ' of the list then you will get correct distance values (flooding) floodx.Insert(0,x2) floody.Insert(0,y2) floodv.Insert(0,v1+1) 'store the distance in the map mapd[x2][y2] = v1+1 End If End If End If Next Else flood=False Print "Flooding done" End If End If End Method Method OnRender() For Local y=0 Until mapheight For Local x=0 Until mapwidth Local col:Int=map[x][y] SetColor col*30,col*30,col*30 DrawRect x*tilewidth,y*tileheight,tilewidth+1,tileheight+1 Next Next For Local i=0 Until floodx.Length SetColor 255,255,0 DrawCircle floodx.Get(i)*tilewidth+tilewidth/2,floody.Get(i)*tileheight+tileheight/2,tilewidth/2 Next SetColor 255,255,255 For Local y=0 Until mapheight For Local x=0 Until mapwidth DrawText mapd[x][y],x*tilewidth+tilewidth-10,y*tileheight+tileheight-10 Next Next SetColor 255,255,255 DrawText "Press Left Mouse (touch) to flood map..",0,0 End Method End Class Function Main() New MyGame() End Function
Monkey-X - RTS - Floodfill or Seedfill group pathfinding - code example
Import mojo Global screenwidth:Int=640 Global screenheight:Int=480 Global mapwidth:Int=20 Global mapheight:Int=20 Global tilewidth:Float=Float(screenwidth)/Float(mapwidth) Global tileheight:Float=Float(screenheight)/Float(mapheight) Global map:Int[][] Global mapd:Int[][] 'map containing the distance (distance map) Global mx:Int[] = [0,1,0,-1] 'expand up/right/down/left Global my:Int[] = [-1,0,1,0] Class ai Field x:Int,y:Int Field speed:Int=Rnd(1,9) Field px:Int,py:Int,pdx:Int,pdy:Int Field delay:Int Field maxdelay:Int Method New() maxdelay=Rnd(20,60) Local exitloop:Bool=False While exitloop=False x=Rnd(mapwidth) y=Rnd(mapheight) If map[x][y] <> 6 Then exitloop = True Wend px = x*tilewidth py = y*tileheight pdx = px pdy = py End Method Method update() For Local s=0 Until speed If px<pdx Then px+=1 If px>pdx Then px-=1 If py<pdy Then py+=1 If py>pdy Then py-=1 If px=pdx And py=pdy Local lx:Stack<Int> = New Stack<Int> Local ly:Stack<Int> = New Stack<Int> Local lv:Stack<Int> = New Stack<Int> For Local i=0 Until 4 Local x1:Int=x+mx[i] Local y1:Int=y+my[i] If x1>=0 And x1<mapwidth And y1>=0 And y1<mapheight If map[x1][y1] <> 6 If mapd[x1][y1] <> 0 Local cont:Bool=True For Local ii:=Eachin myai If ii.x = x1 And ii.y = y1 Then cont=False Next If cont=True lx.Push(x1) ly.Push(y1) lv.Push(mapd[x1][y1]) End If End If End If End If Next ' fidn the lowest value around unit Local lowest:Int=1000 For Local i=0 Until lx.Length If lv.Get(i) < lowest lowest = lv.Get(i) End If Next ' if more then one lowest value then select random Local exitloop:Bool=False While exitloop=False And lx.Length>0 For Local i=0 Until lx.Length If lv.Get(i) = lowest And Rnd()<.2 Then x = lx.Get(i) y = ly.Get(i) pdx = x*tilewidth pdy = y*tileheight exitloop=True End If Next wend End If Next End Method Method draw() SetColor 255,0,0 DrawOval px,py,tilewidth,tileheight End Method End Class Global myai:List<ai> = New List<ai> Class MyGame Extends App Field flood:Bool=False Field fillval:Int=1 Field delay:Int Field floodx:Stack<Int> = New Stack<Int> 'flood Field floody:Stack<Int> = New Stack<Int> Field floodv:Stack<Int> = New Stack<Int> 'distance Method OnCreate() SetUpdateRate(60) map = New Int[mapwidth][] For Local i=0 Until mapwidth map[i] = New Int[mapheight] Next mapd = New Int[mapwidth][] For Local i=0 Until mapwidth mapd[i] = New Int[mapheight] Next For Local x=0 Until mapwidth/2 map[x][mapheight/2] = 6 Next For Local x=6 To 15 For Local y=10 To 15 map[x][y] = 6 Next Next For Local i=0 To 20 myai.AddLast(New ai) Next End Method Method OnUpdate() ' move the ai For Local i:=Eachin myai i.update Next ' Local tx:Int=MouseX()/tilewidth Local ty:Int=MouseY()/tileheight If MouseDown(MOUSE_LEFT) And flood=False And map[tx][ty] <> 6 Print "Flooding - "+Millisecs() floodx.Clear floody.Clear floodv.Clear For Local y=0 Until mapheight For Local x=0 Until mapwidth mapd[x][y] = 0 Next Next floodx.Push(tx) floody.Push(ty) floodv.Push(1) flood = True fillval+=1 If fillval > 5 Then fillval = 0 map[tx][ty] = fillval mapd[tx][ty] = 1 End If If flood = True If floodx.Length > 0 Local x1:Int=floodx.Top Local y1:Int=floody.Top Local v1:Int=floodv.Top floodx.Pop floody.Pop floodv.Pop For Local i=0 Until 4 Local x2:Int=x1+mx[i] Local y2:Int=y1+my[i] If x2>=0 And x2<mapwidth And y2>=0 And y2<mapheight If map[x2][y2] <> fillval If map[x2][y2] <> 6 map[x2][y2] = fillval ' if you insert the new locations at the bottom ' of the list then you will get correct distance values (flooding) floodx.Insert(0,x2) floody.Insert(0,y2) floodv.Insert(0,v1+1) 'store the distance in the map mapd[x2][y2] = v1+1 End If End If End If Next Else flood=False Print "Flooding done" End If End If End Method Method OnRender() For Local y=0 Until mapheight For Local x=0 Until mapwidth Local col:Int=map[x][y] SetColor col*30,col*30,col*30 DrawRect x*tilewidth,y*tileheight,tilewidth+1,tileheight+1 Next Next For Local i=0 Until floodx.Length SetColor 255,255,0 DrawCircle floodx.Get(i)*tilewidth+tilewidth/2,floody.Get(i)*tileheight+tileheight/2,tilewidth/2 Next SetColor 255,255,255 For Local y=0 Until mapheight For Local x=0 Until mapwidth DrawText mapd[x][y],x*tilewidth+tilewidth-10,y*tileheight+tileheight-10 Next Next ' For Local i:=Eachin myai i.draw Next ' SetColor 255,255,255 DrawText "Press Left Mouse (touch) to flood map and move units..",0,0 End Method End Class Function Main() New MyGame() End Function
Monkey-X - Roguelike Maps - code example
' based on a description from the rogue basin forum ' what it does it place random dots with unique id ' connect closest of different id ' make same id of last point ' until all points ' loop through all lines and fill map under the lines. Import mojo Global mapwidth:Int=50 Global mapheight:Int=50 Global sw:Int=640 Global sh:Int=480 Class map Field mw:Int,mh:Int,sw:Int,sh:Int,tw:Float,th:Float Field mypoint:Stack<point> = New Stack<point> Field myline:Stack<line> = New Stack<line> Field map:Int[][] Method New(sw:Int,sh:Int,mw:Int,mh:Int) Self.sw = sw Self.sh = sh Self.mw = mw Self.mh = mh Self.tw = Float(sw)/Float(mw) Self.th = Float(sh)/Float(mh) map = New Int[mw][] For Local i=0 Until mw map[i] = New Int[mh] Next For Local i=0 Until mw*mh/200 Local x:Int=Rnd(5,mw-5) Local y:Int=Rnd(5,mh-5) mypoint.Push(New point(i,x,y)) Next makemap() End Method Method makemap() ' connect point to closest point with unique id 'get first point Local x:Int=mypoint.Get(0).x Local y:Int=mypoint.Get(0).y Local id:Int=mypoint.Get(0).id Local closestindex:Int=0 While closestindex<>-1 'find closest Local dist:Int=10000 closestindex=-1 For Local ii=0 Until mypoint.Length If mypoint.Get(ii).id <> id Local d:Int=distance(x,y,mypoint.Get(ii).x,mypoint.Get(ii).y) If d<dist Then dist=d closestindex = ii End If End If Next If closestindex>-1 mypoint.Get(closestindex).id = id myline.Push(New line(x,y,mypoint.Get(closestindex).x,mypoint.Get(closestindex).y)) x = mypoint.Get(closestindex).x y = mypoint.Get(closestindex).y End If Wend 'make the map For Local i:=Eachin myline Local x1:Int=i.x1 Local y1:Int=i.y1 Local x2:Int=i.x2 Local y2:Int=i.y2 Local exitloop:Bool=False While exitloop=False If x1<x2 Then x1+=1 If x1>x2 Then x1-=1 If y1<y2 Then y1+=1 If y1>y2 Then y1-=1 If x1=x2 And y1=y2 Then exitloop=True ' create the tunnel size Local s:Int=Rnd(1,3) ' sometimes make a wider tunnel If Rnd(mw*mh)< (mw*mh/7) Then s=Rnd(s,s*3) putmap(x1,y1,s) Wend Next End Method Method putmap(x:Int,y:Int,s:Int) For Local y3=-s To s For Local x3=-s To s Local x4:Int=x+x3 Local y4:Int=y+y3 If x4>=0 And x4<mw And y4>=0 And y4<mh map[x4][y4] = 1 End If Next Next End Method Method draw() SetColor 155,50,0 For Local y=0 Until mh For Local x=0 Until mw If map[x][y] = 1 DrawRect x*tw,y*th,tw+1,th+1 End If Next Next SetColor 255,255,0 For Local i:=Eachin myline Local x1:Int=i.x1*tw Local y1:Int=i.y1*th Local x2:Int=i.x2*tw Local y2:Int=i.y2*th DrawLine x1,y1,x2,y2 Next SetColor 255,0,0 For Local i:=Eachin mypoint DrawRect i.x*tw,i.y*th,5,5 DrawText i.id,i.x*tw,i.y*th Next End Method Method distance:Int(x1:Int,y1:Int,x2:Int,y2:Int) Return Abs(x2-x1)+Abs(y2-y1) End Method End Class Class point Field id:Int Field x:Int,y:Int Method New(id:Int,x:Int,y:Int) Self.id = id Self.x = x Self.y = y End Method End Class Class line Field x1:Int,y1:Int Field x2:Int,y2:Int Method New(x1:Int,y1:Int,x2:Int,y2:Int) Self.x1 = x1 Self.y1 = y1 Self.x2 = x2 Self.y2 = y2 End Method End Class Global mymap:map Class MyGame Extends App Field cnt:Int=0 Method OnCreate() SetUpdateRate(10) mymap = New map(640,480,mapwidth,mapheight) End Method Method OnUpdate() cnt+=1 If cnt>100 Or KeyDown(KEY_SPACE) Or MouseDown(MOUSE_LEFT) Then Seed = Millisecs() cnt=0 Local w:Int=Rnd(50,200) Local h:Int=w mymap = New map(640,480,w,h) End If End Method Method OnRender() Cls 0,0,0 mymap.draw SetColor 255,255,255 DrawText "RogueLike maps - Press Space or Mouse Button for new map or wait",0,0 End Method End Class Function Main() New MyGame() End Function
Monkey-X - Roguelike Explorer - code example
Import mojo Global mapwidth:Int=50 Global mapheight:Int=50 Global sw:Int=640 Global sh:Int=480 Class player Field x:Int,y:Int,controldelay:Int Field tleft:Bool,tright:Bool,tup:Bool,tdown:Bool Field tleftx:Int=20 Field tlefty:Int=sh-100 Field trightx:Int=120 Field trighty:Int=sh-100 Field tupx:Int=70 Field tupy:Int=sh-150 Field tdownx:Int=70 Field tdowny:Int=sh-50 Method New() x = mymap.mypoint.Get(1).x y = mymap.mypoint.Get(1).y playerfog(x,y,6) End Method Method update() controldelay+=1 If controldelay<10 Then Return 'touch mouse input If MouseDown(MOUSE_LEFT) Or TouchDown Local x:Int Local y:Int Local w:Int=100 Local h:Int=50 x = tupx y = tupy If rectsoverlap(x,y,w,h,MouseX(),MouseY(),1,1) Then tup=True x = tdownx y = tdowny If rectsoverlap(x,y,w,h,MouseX(),MouseY(),1,1) Then tdown=True x = tleftx y = tlefty If rectsoverlap(x,y,w,h,MouseX(),MouseY(),1,1) Then tleft=True x = trightx y = trighty If rectsoverlap(x,y,w,h,MouseX(),MouseY(),1,1) Then tright=True End If 'keyinput If KeyDown(KEY_UP) Or tup If y-1 >= 0 If mymap.map[x][y-1] = 1 Then y-=1 controldelay = 0 playerfog(x,y,6) End If End If If KeyDown(KEY_DOWN) Or tdown If y+1 < mymap.mh If mymap.map[x][y+1] = 1 Then y+=1 controldelay = 0 playerfog(x,y,6) End If End If If KeyDown(KEY_RIGHT) Or tright If x+1 < mymap.mw If mymap.map[x+1][y] = 1 Then x+=1 controldelay = 0 playerfog(x,y,6) End If End If If KeyDown(KEY_LEFT) Or tleft If x-1 >=0 If mymap.map[x-1][y] = 1 Then x-=1 controldelay = 0 playerfog(x,y,6) End If End If tup=False tright=False tdown=False tleft=false End Method Method draw() SetColor 255,255,255 Local tw:Float=mymap.tw Local th:Float=mymap.th DrawRect x*tw,y*th,tw,th SetColor 255,255,0 DrawRect tleftx,tlefty,100,50 SetColor 255,255,255 DrawText "Left",tleftx+50,tlefty+25,0.5,0.5 SetColor 255,255,0 DrawRect trightx,trighty,100,50 SetColor 255,255,255 DrawText "Right",trightx+50,trighty+25,0.5,0.5 SetColor 255,255,0 DrawRect tupx,tupy,100,50 SetColor 255,255,255 DrawText "Up",tupx+50,tupy+25,0.5,0.5 SetColor 255,255,0 DrawRect tdownx,tdowny,100,50 SetColor 255,255,255 DrawText "Down",tdownx+50,tdowny+25,0.5,0.5 End Method Method playerfog(x1,y1,radius:Float) For Local y2=-radius To radius For Local x2=-radius To radius If (y2*y2+x2*x2) <= radius*radius+radius*0.8 Local x3 = x2+x1 Local y3 = y2+y1 If x3>=0 And x3<mymap.mw And y3>=0 And y3<mymap.mh mymap.fogmap[x3][y3] = True End If End If Next Next End Method Method rectsoverlap:Bool(x1:Int, y1:Int, w1:Int, h1:Int, x2:Int, y2:Int, w2:Int, h2:Int) If x1 >= (x2 + w2) Or (x1 + w1) <= x2 Then Return False If y1 >= (y2 + h2) Or (y1 + h1) <= y2 Then Return False Return True End Method End Class Class map Field mw:Int,mh:Int,sw:Int,sh:Int,tw:Float,th:Float Field mypoint:Stack<point> = New Stack<point> Field myline:Stack<line> = New Stack<line> Field map:Int[][] Field fogmap:Bool[][] Method New(sw:Int,sh:Int,mw:Int,mh:Int) Self.sw = sw Self.sh = sh Self.mw = mw Self.mh = mh Self.tw = Float(sw)/Float(mw) Self.th = Float(sh)/Float(mh) map = New Int[mw][] fogmap = New Bool[mw][] For Local i=0 Until mw map[i] = New Int[mh] fogmap[i] = New Bool[mh] Next For Local i=0 Until mw*mh/200 Local x:Int=Rnd(5,mw-5) Local y:Int=Rnd(5,mh-5) If rectsoverlap(x*tw,y*th,1,1,0,sh-240,320,240) = False mypoint.Push(New point(i,x,y)) End If Next makemap() End Method Method makemap() ' connect point to closest point with unique id 'get first point Local x:Int=mypoint.Get(0).x Local y:Int=mypoint.Get(0).y Local id:Int=mypoint.Get(0).id Local closestindex:Int=0 While closestindex<>-1 'find closest Local dist:Int=10000 closestindex=-1 For Local ii=0 Until mypoint.Length If mypoint.Get(ii).id <> id Local d:Int=distance(x,y,mypoint.Get(ii).x,mypoint.Get(ii).y) If d<dist Then dist=d closestindex = ii End If End If Next If closestindex>-1 mypoint.Get(closestindex).id = id myline.Push(New line(x,y,mypoint.Get(closestindex).x,mypoint.Get(closestindex).y)) x = mypoint.Get(closestindex).x y = mypoint.Get(closestindex).y End If Wend 'make the map For Local i:=Eachin myline Local x1:Int=i.x1 Local y1:Int=i.y1 Local x2:Int=i.x2 Local y2:Int=i.y2 Local exitloop:Bool=False While exitloop=False If x1<x2 Then x1+=1 If x1>x2 Then x1-=1 If y1<y2 Then y1+=1 If y1>y2 Then y1-=1 If x1=x2 And y1=y2 Then exitloop=True ' create the tunnel size Local s:Int=Rnd(1,3) ' sometimes make a wider tunnel If Rnd(mw*mh)< (mw*mh/7) Then s=Rnd(s,s*3) putmap(x1,y1,s) Wend Next End Method Method putmap(x:Int,y:Int,s:Int) For Local y3=-s To s For Local x3=-s To s Local x4:Int=x+x3 Local y4:Int=y+y3 If x4>=0 And x4<mw And y4>=0 And y4<mh map[x4][y4] = 1 End If Next Next End Method Method draw() SetColor 155,50,0 For Local y=0 Until mh For Local x=0 Until mw If map[x][y] = 1 And fogmap[x][y] = true DrawRect x*tw,y*th,tw+1,th+1 End If Next Next End Method Method distance:Int(x1:Int,y1:Int,x2:Int,y2:Int) Return Abs(x2-x1)+Abs(y2-y1) End Method Method rectsoverlap:Bool(x1:Int, y1:Int, w1:Int, h1:Int, x2:Int, y2:Int, w2:Int, h2:Int) If x1 >= (x2 + w2) Or (x1 + w1) <= x2 Then Return False If y1 >= (y2 + h2) Or (y1 + h1) <= y2 Then Return False Return True End Method End Class Class point Field id:Int Field x:Int,y:Int Method New(id:Int,x:Int,y:Int) Self.id = id Self.x = x Self.y = y End Method End Class Class line Field x1:Int,y1:Int Field x2:Int,y2:Int Method New(x1:Int,y1:Int,x2:Int,y2:Int) Self.x1 = x1 Self.y1 = y1 Self.x2 = x2 Self.y2 = y2 End Method End Class Global mymap:map Global myplayer:player Class MyGame Extends App Field mapexplored:Bool=False Field w:Int=mapwidth,h:Int=mapheight Method OnCreate() SetUpdateRate(60) mymap = New map(640,480,w,h) myplayer = New player() End Method Method OnUpdate() If KeyDown(KEY_SPACE) Or mapexplored = True Then mapexplored = False Seed = Millisecs() w+=2 h+=2 If w>300 Then w = 50 ; h = 50 mymap = New map(640,480,w,h) myplayer = New player() End If myplayer.update If Rnd()<.01 And ismapexplored() Then mapexplored=True End Method Method OnRender() Cls 0,0,0 mymap.draw myplayer.draw SetColor 255,255,255 SetAlpha 0.5 DrawText "RogueLike random maps and fog of war and player. space/mouse new map, cursors move.",0,0 DrawText "If everything is explored a new map is created.",0,15 End Method End Class Function ismapexplored:Bool() Local ex:Bool=True For Local y=0 Until mymap.mh For Local x=0 Until mymap.mw If mymap.map[x][y] = 1 And mymap.fogmap[x][y] = False Then ex=False Next Next Return ex End Function Function Main() New MyGame() End Function
Monkey-X - Rectangle Collision Helper - code example
Import mojo Class line Field x1:Int,y1:Int Field x2:Int,y2:Int Field w:Int,h:Int Field r:Int,g:Int,b:Int Method New(x1:Int,y1:Int,w:Int,h:Int) Self.x1 = x1 Self.y1 = y1 Self.w = w Self.h = h Self.x2 = x1+w Self.y2 = y1+h End Method Method newpos(x:Int,y:Int) x1=x y1=y x2=x1+w y2=y1+h End Method Method mysetcolor(r:Int,g:Int,b:Int) Self.r = r Self.g = g Self.b = b End Method Method draw() SetColor r,g,b DrawRect x1,y1,w,h End Method End Class Class MyGame Extends App Field line1:line = New line(150,150,100,100) Field line2:line = New line(200,200,100,100) Method OnCreate() SetUpdateRate(60) line1.mysetcolor(200,0,0) line2.mysetcolor(0,200,0) End Method Method OnUpdate() line1.newpos(MouseX(),MouseY()) End Method Method OnRender() Cls 0,0,0 SetColor 255,255,255 drawthickline 10,10,10,DeviceHeight-10 drawthickline 10,DeviceHeight-10,DeviceWidth-10,DeviceHeight-10 line1.draw line2.draw ' draw the sidelines 'left Local y1:Int=line1.y1 Local y2:Int=line1.y2 SetColor 200,50,0 drawthickline 11,y1,11,y2 y1=line2.y1 y2=line2.y2 SetColor 50,200,0 drawthickline 15,y1,15,y2 'bottom Local x1:Int=line1.x1 Local x2:Int=line1.x2 SetColor 200,50,0 drawthickline x1,DeviceHeight-11,x2,DeviceHeight-11 x1=line2.x1 x2=line2.x2 SetColor 50,200,0 drawthickline x1,DeviceHeight-15,x2,DeviceHeight-15 'info Scale 2,2 SetColor 255,255,255 DrawText "When 2 sets of lines are overlapping then",30,0 DrawText "a rectangular collision is happening...",30,20 DrawText "Move block with mouse",30,(DeviceHeight-50)/2 SetColor 255,255,0 If rectsoverlap( line1.x1,line1.y1,line1.w,line1.h, line2.x1,line2.y1,line2.w,line2.h) DrawText "Collision is happening",30,40 End If End Method End Class Function drawthickline(x1:Int,y1:Int,x2:Int,y2:Int) For Local y=-2 To 2 For Local x=-2 To 2 DrawLine x1+x,y1+y,x2+x,y2+y Next Next End Function Function rectsoverlap:Bool(x1:Int, y1:Int, w1:Int, h1:Int, x2:Int, y2:Int, w2:Int, h2:Int) If x1 >= (x2 + w2) Or (x1 + w1) <= x2 Then Return False If y1 >= (y2 + h2) Or (y1 + h1) <= y2 Then Return False Return True End Function Main() New MyGame() End Function
Subscribe to:
Posts (Atom)