Problem with monster AI

BlitzMax Forums/BlitzMax Programming/Problem with monster AI

Takis76(Posted 2015) [#1]
Hello my friends,

I am creating the monster artificial intelligence functions and I have a little problem.

I have my monsters moving in the dungeons and I try to make them collide with each other.
What I mean , when some monster crawls in the dungeon , collides with the environment (Walls , doors , etc..) but when it arrives in square with other monster I want to change direction and never one square occupies 2 or monsters. (Monster overlapping).

The type of the monster is:

Type My_Monster
	
	Field X:Byte 'position X on the map
	Field Y:Byte 'position Y on the map
	Field Direction:Byte 'The direction the monster goes
	Field Graphic:String 'the graphic of the monster "01"=Skeleton
	Field State:Byte '1=Walk 2=Attack 3=Guard 4=Patrol

	Field Square_Transition_frame:Int
	Field Walking:Byte 'If the monster is walking
	Field Attacking:Byte
	Field Attack_animation:Byte

	Field Frame:Byte 'Animation frame
        .
        .
        .

End Type
Global Monster:My_Monster[max_levels + 1, Max_Monsters + 1]
For LVL = 1 To Max_Levels
	For All_Monsters = 0 To Max_Monsters
		Monster[LVL, All_Monsters] = New My_Monster
	Next
Next



The monster when it moves change squares smoothly.
There is a variable with name Square_Transition_frame.
This square_Transition_frame variable takes values from -2 to 2
The monster when it enters in the square have an animation sequence and move its sprite:
When it goes NORTH it moves from square_Transition_frame -2 then -1 and then finally 0.
At the square_Transition_frame 0 the monster is at the absolute center position of the square.
If the monster continue go to the NORTH then the monster will go from the square_Transition_frame 0 to 1 until arrives at the
square_Transition_frame 2 after then the square_Transition_frame becomes -2 again and the monster changes its position y
on the map.

The opposite happens when the monster goes SOUTH the square_Transition_frame increases its value from -2 to -1 to 0.
At the 0 the monster is at the center of the square. If it continue going SOUTH the square_Transition_frame becomes -1 to -2 and finally becomes
2 again and the monster increases ins y position on the map.

The same happens when the monster goes WEST or EAST.
In few words the 0 is the center position of the square and -2 -1 0 1 2 are 5 smooth animation transitions for the monster.
The monster always before decide which direction will go , must arrive in square_Transition_frame =0 and then checks the AI if there are
obstacles around like doors , statues or even other monsters.
Everything are ok when I check non movable things , but when I am checking if there is another monster I have problem.

I tried to check if the monster+1 and monster-1 is at the same position but it doesn't seems to work.

I use this
	For All_Monsters = 1 To Max_Monsters
 	      If Monster[Party.Level, All_Monsters].X > 0 And Monster[Party.Level, All_Monsters].Y > 0 Then

                    'Check the previous monster
	            If Monster[Party.Level, All_Monsters].X = Monster[Party.Level, All_Monsters - 1].X And Monster[Party.Level, All_Monsters].Y - 1 = Monster[Party.Level, All_Monsters - 1].Y Then
	            'Stop this monster
	            Monster[Party.Level, All_Monsters].Walking = 0
	            'Stop and the previous monster
	            Monster[Party.Level, All_Monsters - 1].Walking = 0

	            Monster[Party.Level, All_Monsters].Deadline_time = Current_time + Monster[Party.Level, All_Monsters].Time_Delay
 	            Monster[Party.Level, All_Monsters].Go_Direction = Rnd(1, 3)
								
		    If Monster[Party.Level, All_Monsters].Go_Direction = 1 Then Monster[Party.Level, All_Monsters].Direction = 2 Continue
		    If Monster[Party.Level, All_Monsters].Go_Direction = 2 Then Monster[Party.Level, All_Monsters].Direction = 3 Continue
		    If Monster[Party.Level, All_Monsters].Go_Direction = 3 Then Monster[Party.Level, All_Monsters].Direction = 4 Continue

                    'Check the next monster
	            If Monster[Party.Level, All_Monsters].X = Monster[Party.Level, All_Monsters + 1].X And Monster[Party.Level, All_Monsters].Y - 1 = Monster[Party.Level, All_Monsters + 1].Y Then
	            'Stop this monster
	            Monster[Party.Level, All_Monsters].Walking = 0
	            'Stop and the previous monster
	            Monster[Party.Level, All_Monsters + 1].Walking = 0

	            Monster[Party.Level, All_Monsters].Deadline_time = Current_time + Monster[Party.Level, All_Monsters].Time_Delay
 	            Monster[Party.Level, All_Monsters].Go_Direction = Rnd(1, 3)
								
		    If Monster[Party.Level, All_Monsters].Go_Direction = 1 Then Monster[Party.Level, All_Monsters].Direction = 2 Continue
		    If Monster[Party.Level, All_Monsters].Go_Direction = 2 Then Monster[Party.Level, All_Monsters].Direction = 3 Continue
		    If Monster[Party.Level, All_Monsters].Go_Direction = 3 Then Monster[Party.Level, All_Monsters].Direction = 4 Continue
								
	      EndIf
      Next



The quick code above tells to check if the all_monsters-1 and all_monsters+1 will going to have equal positions with all_monsters
It doesn't seems to work.
The only thing I try to do , when the monster see if there is other monster in front (Depends which direction the monster travels) will change direction and the other monster will change direction to.
If monster goes NORTH check Y-1 and goes other random direction except NORTH
If monster goes SOUTH check Y+1 and goes other random direction except SOUTH

If monster goes WEST check X-1 and goes other random direction except WEST
If monster goes EAST check X+1 and goes other random direction except EAST


Matty(Posted 2015) [#2]
Theres a lot of (poorly) formatted text to read there but I think I know what the problem is.

The underlying method you are using should be fine - and probably is working under the hood but just looks wrong visually.

The common problem with a method like this is that even though the monsters are in different cells their visual representation may overlap because the collision check is between two points while visually the monster takes up an area.

The solution is to provide a radial checking distance for each monster which it uses when checking the grid cells instead.

It gets complex when you introduce monsters that are larger than a grid cell however.

I hope that adds some light.


Takis76(Posted 2015) [#3]

Theres a lot of (poorly) formatted text to read there but I think I know what the problem is.



It seems the code tag wasn't activated and the text code wasn't appear well :)
Better now?


The common problem with a method like this is that even though the monsters are in different cells their visual representation may overlap because the collision check is between two points while visually the monster takes up an area.



Yes my monsters moves in squares and each monster see what it have in front of it.
But when both monsters look at the counter direction , then both monsters will look or land in square which don't have monster so my collision believes there is no monster and then the monsters will overlap each other. (checking a monster -1 and monster +1 doesn't help much). The collision works with monster and static object.


Click here! to downlaod game demo to see how the monsters overlapping



The solution is to provide a radial checking distance for each monster which it uses when checking the grid cells instead.



My game moves in squares so I don't know if radial checking will help because if I check in a radius of distance between squares , I have and other obstacles , like the walls or the environment , which need the monster to check too , not the other monster , but if there is any closed door or any them moving or static object. (Spells , decoration).

I use a similarity of distance checking (Look the square in front)

Example:
if Monsters[Party.level,All_monsters].y-1 = Monsters[Party.level,All_monsters-1].y


If the current monster in all_monsters loop position Y - 1 is equal with the Y of the all_monsters - 1 loop count.(The previous one) and loop +1 for the next one. This method is buggy when I want to turn my monster and if the space is small , for example narrow corridors or a lots of monsters coalition. in small rooms , if for example one small room with for squares have 3 monsters , the monsters when they try to turn or check all of those other monsters they will loose their collision and will continue to walk and then overlap.

If you can translate your idea with code?


It gets complex when you introduce monsters that are larger than a grid cell however.


There is no problem here , none of my monsters are larger than a grid cell , all monsters occupies only one cell (Square) everything fits in one square no larger. So present your idea you are welcome.


Matty(Posted 2015) [#4]
Im not at a pc at the moment (on holidays by the beach) - but im sure there is something similar in the code archives....I seem to remember an entry with keywords "shifted grid collisions".

Re radial distance - I was implying use it in conjunction with the grid.


Takis76(Posted 2015) [#5]
I found one nice code which calculate the distance between to points.

Function Monster_Distance:Int(x0:Int, y0:Int, x1:Int, y1:Int)
	Local dx:Int = x0 - x1
	Local dy:Int = y0 - y1
	Return Sqr(dx * dx + dy * dy)
End Function


Global m_dist_previous:Byte[101]
Global m_dist_next:Byte[101]
for all_monsters=1 to 100
m_dist_previous[All_Monsters]= Monster_Distance(Monster[Party.Level, All_Monsters].X, Monster[Party.Level, All_Monsters].Y, Monster[Party.Level, All_Monsters - 1].X, Monster[Party.Level, All_Monsters - 1].Y)
m_dist_next[All_Monsters]= Monster_Distance(Monster[Party.Level, All_Monsters].X, Monster[Party.Level, All_Monsters].Y, Monster[Party.Level, All_Monsters + 1].X, Monster[Party.Level, All_Monsters + 1].Y)
next


This code calculates the distance in squares between my next and previous but I don't know exactly how to calculate the next and previous.
This is an illustration.
I am near to the solution


Takis76(Posted 2015) [#6]
I am very near.
The monsters stops when the distance between monsters is 1
But the problem is they remain stopped because when 2 monsters are near to each other they have always the distance 1 and the monster remains stopped.

So it needs to check if there is another monster in front of the monster regardless the collision.

My previous function.


Takis76(Posted 2015) [#7]
After I made a lots of test and I recreated my whole AI code more than 7 times , then it still doesn't work.

When 2 monsters simultaneously try to take advance in the same square , the monster overlaps each other again.
The distance code doesn't help much.

If you have some idea how to avoid each monster arrive in the same X and Y position.

Before each monster arrive in the next square , there is a smooth transition , I described it in the first post. But when 2 or more monsters are in transition progress from 4 different directions they are going to occupy the same square again.

I thought ,not use smooth transition between monster moves , but then the monster will move convulsive or skip frames between squares , like to move a piece on chess.

In worst case if I will not achieve it , I will use the convulsive monster movement like chess.

If you have some idea , I will describe you what I want to do exactly.


AdamStrange(Posted 2015) [#8]
If you have tried several different ways and still no joy, then I would suggest putting the code you are working on 'to bed' - and do a brand new simple project just to get this issue sorted.

So, taking that approach you can throw away all complex code and focus on the problem:
* square tile based map
* tiles (an int)are either free (floor)(0) or not free (wall, door)(1)
* monsters that man move on the map (this is a list or array?)

draw it to screen

Assuming the monster are an array of 10 and are at unique map positions that are free - their starting positions

draw them (in a different colour) to the map

* get a primitive ai working that treat anything above 0 at a wall and get them to move

****
avoidance

****
* in the monster update (the move routine) a monster will either be on a tile, or on 2 tiles - so reflect this in your map. make the tile(or tiles) 2
* as we now have the map updated, when you move the monster it will leave a trail where other monster can go (because the map is above 0 - it will also block the source monster too)
* so when you update each monster - remove the previous 2 (or 2 2s) from the map

Not sure if i'm clear here? But give it a go


Takis76(Posted 2015) [#9]
I made one prototype and it seems worked.
Whole code of the prototype all monsters are moving and none of them overlap.

AdamStrange you gave me a good idea and I changed whole monster AI functions and it worked.


The code:

SuperStrict
'Eye of the Beholder IV - monster AI prototype

Import brl.bank
Import pub.zlib
Import brl.filesystem
Import brl.standardio
Import brl.bankstream
Import brl.filesystem
Import BRL.Retro


Import klepto.klpacker
Import maxgui.drivers

Import brl.Audio
Import brl.Map

Import brl.FreeTypeFont
Import brl.PNGLoader
Import brl.timer
Import brl.eventqueue
Import brl.textstream
Import brl.oggloader
Import brl.wavloader
Import brl.jpgloader
Import brl.bmploader
Import brl.stream


Global map_grid:TImage
Global spider_icon_n:TImage
Global spider_icon_s:TImage
Global spider_icon_w:TImage
Global spider_icon_e:TImage

Global ghost_icon_n:TImage
Global ghost_icon_s:TImage
Global ghost_icon_w:TImage
Global ghost_icon_e:TImage
Global locked_square:TImage


Global Current_time:Int
Global Game_Loaded:Byte


Const Max_Monsters:Byte = 100
Const Max_Levels:Byte = 51 '(+1 for developer mechanics)

Global All_Monsters:Byte
Global LVL:Byte


'Global Monster_Table:Byte[52, 39, 38]
Global Monster_Table:Int[52, 21, 21]
Global MTable_x:Int
Global MTable_y:Int



Type My_Monster
	
	Field X:Byte 'position X on the map
	Field Y:Byte 'position Y on the map
	Field Direction:Byte 'The direction the monster goes
	Field Graphic:String 'the graphic of the monster "01"=Skeleton
	Field State:Byte '1=Walk 2=Attack 3=Guard 4=Patrol
	Field Walking:Byte
	Field Square_transition_frame:Int
	Field Deadline_time:Int 'Timer Delay
	Field Time_delay:Int 'animation speed
	Field Not_Allow_to_move:Byte
	Field square_locked:Byte
	Field Choose_Direction:Byte
	Field Steps:Byte
	
	
End Type
Global Monster:My_Monster[max_levels + 1, Max_Monsters + 1]
For LVL = 1 To Max_Levels
	For All_Monsters = 0 To Max_Monsters
		Monster[LVL, All_Monsters] = New My_Monster
	Next
Next


Type My_Party

	Field x:Byte
	Field y:Byte
	Field Level:Byte
	Field Level_temp:Byte
	Field Direction:Byte
	Field Mana_Points:Float
	Field Max_Mana_Points:Float
	Field Mana_Percentage:Float
	Field Collision:Byte
	Field Immobilized:Byte
	Field Dest_Level:Byte
	
	
EndType
Global Party:My_Party = New My_Party


	
	

'Windowed ======================
'Global graph:TGraphics = Graphics(640, 480, 0, 120)

'Full
Global graph:TGraphics = Graphics(640, 480, 32, 120, 0)


SetMaskColor(255, 0, 255)
'SetBlend(MASKBLEND)
SetBlend(ALPHABLEND | MASKBLEND)


Load_Data()

While Not AppTerminate()

	Current_time = MilliSecs()
	If Game_Loaded = 0 Then Load_Data()
	
	Draw_Interface()

	If KeyHit(KEY_ESCAPE) Then Ending()
	
	Flip -1
	'Windows 8 - Flip 1
Wend

Function Ending()
End
End Function

	
Function Draw_Interface()


	Monster_Mechanics()
	Draw_Game()
	Keyboard_commands()
	DrawText "Current Time:" + String(Current_time), 350, 10
	DrawText "M1 Deadline time:" + String(Monster[1, 1].Deadline_time), 350, 20
	DrawText "M1 X:" + String(Monster[1, 1].X), 350, 40
	DrawText "M1 Y:" + String(Monster[1, 1].Y), 350, 50
	DrawText "M1 STF:" + String(Monster[1, 1].Square_transition_frame), 350, 60
	DrawText "M1 Walking:" + String(Monster[1, 1].Walking), 350, 70
	
End Function

Function Load_Data()

	'skel_icon = LoadImage("skel_icon.png", MASKEDIMAGE)
	ghost_icon_n = LoadImage("ghost_icon_n.png", MASKEDIMAGE)
	ghost_icon_s = LoadImage("ghost_icon_s.png", MASKEDIMAGE)
	ghost_icon_w = LoadImage("ghost_icon_w.png", MASKEDIMAGE)
	ghost_icon_e = LoadImage("ghost_icon_e.png", MASKEDIMAGE)
	spider_icon_n = LoadImage("spider_icon_n.png", MASKEDIMAGE)
	spider_icon_s = LoadImage("spider_icon_s.png", MASKEDIMAGE)
	spider_icon_w = LoadImage("spider_icon_w.png", MASKEDIMAGE)
	spider_icon_e = LoadImage("spider_icon_e.png", MASKEDIMAGE)
	locked_square = LoadImage("locked_square.png", MASKEDIMAGE)
	map_grid = LoadImage("Map_grid.png", MASKEDIMAGE)
	Inits()
	'Delay(2000)
	
Game_Loaded = 1
End Function


Function Monster_Mechanics()
	
	For All_Monsters = 1 To Max_Monsters

			If Monster[Party.Level, All_Monsters].Direction = 1 Then
				If Monster[Party.Level, All_Monsters].Square_transition_frame = 0 Then
					Monster_Table[Party.Level, Monster[Party.Level, All_Monsters].X, Monster[Party.Level, All_Monsters].Y + 1] = 0
					Monster_Table[Party.Level, Monster[Party.Level, All_Monsters].X, Monster[Party.Level, All_Monsters].Y] = 1
					Monster[Party.Level, All_Monsters].Not_Allow_to_move = 1
					'Monster[Party.Level, All_Monsters].square_locked = 0
				End If
				If Monster[Party.Level, All_Monsters].Square_transition_frame > 0 Then
					Monster_Table[Party.Level, Monster[Party.Level, All_Monsters].X, Monster[Party.Level, All_Monsters].Y] = 2
					Monster_Table[Party.Level, Monster[Party.Level, All_Monsters].X, Monster[Party.Level, All_Monsters].Y - 1] = 2
					Monster[Party.Level, All_Monsters].Not_Allow_to_move = 0
				End If
			EndIf
			If Monster[Party.Level, All_Monsters].Direction = 2 Then
				If Monster[Party.Level, All_Monsters].Square_transition_frame = 0 Then
					Monster_Table[Party.Level, Monster[Party.Level, All_Monsters].X, Monster[Party.Level, All_Monsters].Y - 1] = 0
					Monster_Table[Party.Level, Monster[Party.Level, All_Monsters].X, Monster[Party.Level, All_Monsters].Y] = 1
					Monster[Party.Level, All_Monsters].Not_Allow_to_move = 1
					'Monster[Party.Level, All_Monsters].square_locked = 0
				End If
				If Monster[Party.Level, All_Monsters].Square_transition_frame > 0 Then
					Monster_Table[Party.Level, Monster[Party.Level, All_Monsters].X, Monster[Party.Level, All_Monsters].Y] = 2
					Monster_Table[Party.Level, Monster[Party.Level, All_Monsters].X, Monster[Party.Level, All_Monsters].Y + 1] = 2
					Monster[Party.Level, All_Monsters].Not_Allow_to_move = 0
				End If
			EndIf
			If Monster[Party.Level, All_Monsters].Direction = 3 Then
				If Monster[Party.Level, All_Monsters].Square_transition_frame = 0 Then
					Monster_Table[Party.Level, Monster[Party.Level, All_Monsters].X + 1, Monster[Party.Level, All_Monsters].Y] = 0
					Monster_Table[Party.Level, Monster[Party.Level, All_Monsters].X, Monster[Party.Level, All_Monsters].Y] = 1
					Monster[Party.Level, All_Monsters].Not_Allow_to_move = 1
					'Monster[Party.Level, All_Monsters].square_locked = 0
				End If
				If Monster[Party.Level, All_Monsters].Square_transition_frame > 0 Then
					Monster_Table[Party.Level, Monster[Party.Level, All_Monsters].X, Monster[Party.Level, All_Monsters].Y] = 2
					Monster_Table[Party.Level, Monster[Party.Level, All_Monsters].X - 1, Monster[Party.Level, All_Monsters].Y] = 2
					Monster[Party.Level, All_Monsters].Not_Allow_to_move = 0
				End If
			EndIf
			If Monster[Party.Level, All_Monsters].Direction = 4 Then
				If Monster[Party.Level, All_Monsters].Square_transition_frame = 0 Then
					Monster_Table[Party.Level, Monster[Party.Level, All_Monsters].X - 1, Monster[Party.Level, All_Monsters].Y] = 0
					Monster_Table[Party.Level, Monster[Party.Level, All_Monsters].X, Monster[Party.Level, All_Monsters].Y] = 1
					Monster[Party.Level, All_Monsters].Not_Allow_to_move = 1
					'Monster[Party.Level, All_Monsters].square_locked = 0
				End If
				If Monster[Party.Level, All_Monsters].Square_transition_frame > 0 Then
					Monster_Table[Party.Level, Monster[Party.Level, All_Monsters].X, Monster[Party.Level, All_Monsters].Y] = 2
					Monster_Table[Party.Level, Monster[Party.Level, All_Monsters].X + 1, Monster[Party.Level, All_Monsters].Y] = 2
					Monster[Party.Level, All_Monsters].Not_Allow_to_move = 0
				End If
			EndIf
			
		
			
		If Current_time >= Monster[Party.Level, All_Monsters].Deadline_time Then
		Monster[Party.Level, All_Monsters].Time_Delay = 500
		Monster[Party.Level, All_Monsters].Deadline_time = Current_time + Monster[Party.Level, All_Monsters].Time_Delay
		
				If Monster[Party.Level, All_Monsters].X = 0 And Monster[Party.Level, All_Monsters].Y = 0 Then Continue
				If Monster[Party.Level, All_Monsters].X > 0 And Monster[Party.Level, All_Monsters].Y > 0 Then

					If Monster[Party.Level, All_Monsters].Square_transition_frame = 0 Then
						Monster[Party.Level, All_Monsters].Steps:+1
						If Monster[Party.Level, All_Monsters].Steps > Rand(5, 9) Then
						Monster[Party.Level, All_Monsters].Steps = 0
							If Monster[Party.Level, All_Monsters].Walking = 0 Then Monster[Party.Level, All_Monsters].Walking = 1
							If Monster[Party.Level, All_Monsters].Direction = 1
								Monster[Party.Level, All_Monsters].choose_direction = Rand(1, 2)
								If Monster[Party.Level, All_Monsters].choose_direction = 1 Then Monster[Party.Level, All_Monsters].Direction = 3 Return
								If Monster[Party.Level, All_Monsters].choose_direction = 2 Then Monster[Party.Level, All_Monsters].Direction = 4 Return
							EndIf
							If Monster[Party.Level, All_Monsters].Direction = 2
								Monster[Party.Level, All_Monsters].choose_direction = Rand(1, 2)
								If Monster[Party.Level, All_Monsters].choose_direction = 1 Then Monster[Party.Level, All_Monsters].Direction = 4 Return
								If Monster[Party.Level, All_Monsters].choose_direction = 2 Then Monster[Party.Level, All_Monsters].Direction = 3 Return
							EndIf
							If Monster[Party.Level, All_Monsters].Direction = 3
								Monster[Party.Level, All_Monsters].choose_direction = Rand(1, 2)
								If Monster[Party.Level, All_Monsters].choose_direction = 1 Then Monster[Party.Level, All_Monsters].Direction = 1 Return
								If Monster[Party.Level, All_Monsters].choose_direction = 2 Then Monster[Party.Level, All_Monsters].Direction = 2 Return
							EndIf
							If Monster[Party.Level, All_Monsters].Direction = 4
								Monster[Party.Level, All_Monsters].choose_direction = Rand(1, 2)
								If Monster[Party.Level, All_Monsters].choose_direction = 1 Then Monster[Party.Level, All_Monsters].Direction = 2 Return
								If Monster[Party.Level, All_Monsters].choose_direction = 2 Then Monster[Party.Level, All_Monsters].Direction = 1 Return
							EndIf
						EndIf
						
					EndIf
					
			
					If Monster[Party.Level, All_Monsters].Walking = 1 Then
						'Monster goes NORTH
						If Monster[Party.Level, All_Monsters].Direction = 1

							'The monster is on the first row							
							If Monster[Party.Level, All_Monsters].Y = 1 Then
							Monster[Party.Level, All_Monsters].choose_direction = Rand(1, 2)
								If Monster[Party.Level, All_Monsters].choose_direction = 1 Then Monster[Party.Level, All_Monsters].Direction = 3 Return
								If Monster[Party.Level, All_Monsters].choose_direction = 2 Then Monster[Party.Level, All_Monsters].Direction = 4 Return
							EndIf
							
							'If the monster found other monster in front
							If Monster_Table[Party.Level, Monster[Party.Level, All_Monsters].X, Monster[Party.Level, All_Monsters].Y - 1] <> 0 And Monster[Party.Level, All_Monsters].Not_Allow_to_move = 1 Then
							Monster[Party.Level, All_Monsters].choose_direction = Rand(1, 2)
								If Monster[Party.Level, All_Monsters].choose_direction = 1 Then Monster[Party.Level, All_Monsters].Direction = 3 Return
								If Monster[Party.Level, All_Monsters].choose_direction = 2 Then Monster[Party.Level, All_Monsters].Direction = 4 Return
							EndIf
							Monster[Party.Level, All_Monsters].Square_transition_frame:+1
							If Monster[Party.Level, All_Monsters].Square_transition_frame > 3 Then
							Monster[Party.Level, All_Monsters].Square_transition_frame = 0
							Monster[Party.Level, All_Monsters].Y:-1
							EndIf
							
						EndIf
						'Monster goes SOUTH
						If Monster[Party.Level, All_Monsters].Direction = 2
							
							'The monster is on the last row							
							If Monster[Party.Level, All_Monsters].Y = 20 Then
							Monster[Party.Level, All_Monsters].choose_direction = Rand(1, 2)
								If Monster[Party.Level, All_Monsters].choose_direction = 1 Then Monster[Party.Level, All_Monsters].Direction = 4 Return
								If Monster[Party.Level, All_Monsters].choose_direction = 2 Then Monster[Party.Level, All_Monsters].Direction = 3 Return
							EndIf
							
							'If the monster found other monster in front
							If Monster_Table[Party.Level, Monster[Party.Level, All_Monsters].X, Monster[Party.Level, All_Monsters].Y + 1] <> 0 And Monster[Party.Level, All_Monsters].Not_Allow_to_move = 1 Then
							Monster[Party.Level, All_Monsters].choose_direction = Rand(1, 2)
								If Monster[Party.Level, All_Monsters].choose_direction = 1 Then Monster[Party.Level, All_Monsters].Direction = 4 Return
								If Monster[Party.Level, All_Monsters].choose_direction = 2 Then Monster[Party.Level, All_Monsters].Direction = 3 Return
							EndIf
							Monster[Party.Level, All_Monsters].Square_transition_frame:+1
							If Monster[Party.Level, All_Monsters].Square_transition_frame > 3 Then
							Monster[Party.Level, All_Monsters].Square_transition_frame = 0
							Monster[Party.Level, All_Monsters].Y:+1
							EndIf
							
						EndIf
						'Monster goes WEST
						If Monster[Party.Level, All_Monsters].Direction = 3
							
							'The monster is on the first column							
							If Monster[Party.Level, All_Monsters].X = 1 Then
							Monster[Party.Level, All_Monsters].choose_direction = Rand(1, 2)
								If Monster[Party.Level, All_Monsters].choose_direction = 1 Then Monster[Party.Level, All_Monsters].Direction = 1 Return
								If Monster[Party.Level, All_Monsters].choose_direction = 2 Then Monster[Party.Level, All_Monsters].Direction = 2 Return
							EndIf

							'If the monster found other monster in front
							If Monster_Table[Party.Level, Monster[Party.Level, All_Monsters].X - 1, Monster[Party.Level, All_Monsters].Y] <> 0 And Monster[Party.Level, All_Monsters].Not_Allow_to_move = 1 Then
							Monster[Party.Level, All_Monsters].choose_direction = Rand(1, 2)
								If Monster[Party.Level, All_Monsters].choose_direction = 1 Then Monster[Party.Level, All_Monsters].Direction = 1 Return
								If Monster[Party.Level, All_Monsters].choose_direction = 2 Then Monster[Party.Level, All_Monsters].Direction = 2 Return
							EndIf
							Monster[Party.Level, All_Monsters].Square_transition_frame:+1
							If Monster[Party.Level, All_Monsters].Square_transition_frame > 3 Then
							Monster[Party.Level, All_Monsters].Square_transition_frame = 0
							Monster[Party.Level, All_Monsters].X:-1
							EndIf
							
						EndIf
						'Monster goes EAST
						If Monster[Party.Level, All_Monsters].Direction = 4
							
							'The monster is on the last column							
							If Monster[Party.Level, All_Monsters].X = 20 Then
							Monster[Party.Level, All_Monsters].choose_direction = Rand(1, 2)
								If Monster[Party.Level, All_Monsters].choose_direction = 1 Then Monster[Party.Level, All_Monsters].Direction = 2 Return
								If Monster[Party.Level, All_Monsters].choose_direction = 2 Then Monster[Party.Level, All_Monsters].Direction = 1 Return
							EndIf

							'If the monster found other monster in front
							If Monster_Table[Party.Level, Monster[Party.Level, All_Monsters].X + 1, Monster[Party.Level, All_Monsters].Y] <> 0 And Monster[Party.Level, All_Monsters].Not_Allow_to_move = 1 Then
							Monster[Party.Level, All_Monsters].choose_direction = Rand(1, 2)
								If Monster[Party.Level, All_Monsters].choose_direction = 1 Then Monster[Party.Level, All_Monsters].Direction = 2 Return
								If Monster[Party.Level, All_Monsters].choose_direction = 2 Then Monster[Party.Level, All_Monsters].Direction = 1 Return
							EndIf
							Monster[Party.Level, All_Monsters].Square_transition_frame:+1
							If Monster[Party.Level, All_Monsters].Square_transition_frame > 3 Then
							Monster[Party.Level, All_Monsters].Square_transition_frame = 0
							Monster[Party.Level, All_Monsters].X:+1
							EndIf
							
						EndIf
					EndIf
					
					'If the monster is walking
					If Monster[Party.Level, All_Monsters].Walking = 0 Then
					'Monster[Party.Level, All_Monsters].Walking = 1

					EndIf

				EndIf
				'end of monsters XY > 0
		
		EndIf
		'End of monster deadline time
		
		
		If Monster[Party.Level, All_Monsters].X <= 1 Then Monster[Party.Level, All_Monsters].X = 1
		If Monster[Party.Level, All_Monsters].X >= 20 Then Monster[Party.Level, All_Monsters].X = 20
		If Monster[Party.Level, All_Monsters].Y <= 1 Then Monster[Party.Level, All_Monsters].Y = 1
		If Monster[Party.Level, All_Monsters].Y >= 20 Then Monster[Party.Level, All_Monsters].Y = 20
		
	Next

	
	
End Function


Function Draw_Game()

	SetColor(0, 0, 0)
	DrawRect(0, 0, 640, 480)
	SetColor(255, 255, 255)

	
	For MTable_y = 1 To 20
		For MTable_x = 1 To 20
			If Monster_Table[Party.Level, MTable_x, MTable_y] = 2 Then DrawImage(locked_square, -16 + (MTable_x * 16), -16 + (MTable_y * 16), 0)
		Next
	Next

			
	For All_Monsters = 1 To Max_Monsters
		
		If Monster[Party.Level, All_Monsters].X <= 0 And Monster[Party.Level, All_Monsters].Y <= 0 Then Continue
		If Monster[Party.Level, All_Monsters].X > 0 And Monster[Party.Level, All_Monsters].Y > 0 Then
			
			If Monster[Party.Level, All_Monsters].Direction = 1 Then
				
				If Monster[Party.Level, All_Monsters].Graphic = "01" Then
					
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 3 Then DrawImage(spider_icon_n, -16 + (Monster[Party.Level, All_Monsters].X * 16), -16 + (Monster[Party.Level, All_Monsters].Y * 16) - 12, 0)
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 2 Then DrawImage(spider_icon_n, -16 + (Monster[Party.Level, All_Monsters].X * 16), -16 + (Monster[Party.Level, All_Monsters].Y * 16) - 8, 0)
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 1 Then DrawImage(spider_icon_n, -16 + (Monster[Party.Level, All_Monsters].X * 16), -16 + (Monster[Party.Level, All_Monsters].Y * 16) - 4, 0)
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 0 Then DrawImage(spider_icon_n, -16 + (Monster[Party.Level, All_Monsters].X * 16), -16 + (Monster[Party.Level, All_Monsters].Y * 16), 0)
				EndIf

				If Monster[Party.Level, All_Monsters].Graphic = "02" Then
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 3 Then DrawImage(ghost_icon_n, -16 + (Monster[Party.Level, All_Monsters].X * 16), -16 + (Monster[Party.Level, All_Monsters].Y * 16) - 12, 0)
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 2 Then DrawImage(ghost_icon_n, -16 + (Monster[Party.Level, All_Monsters].X * 16), -16 + (Monster[Party.Level, All_Monsters].Y * 16) - 8, 0)
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 1 Then DrawImage(ghost_icon_n, -16 + (Monster[Party.Level, All_Monsters].X * 16), -16 + (Monster[Party.Level, All_Monsters].Y * 16) - 4, 0)
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 0 Then DrawImage(ghost_icon_n, -16 + (Monster[Party.Level, All_Monsters].X * 16), -16 + (Monster[Party.Level, All_Monsters].Y * 16), 0)
				EndIf

							
			End If
						
			If Monster[Party.Level, All_Monsters].Direction = 2 Then
						
				If Monster[Party.Level, All_Monsters].Graphic = "01" Then
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 3 Then DrawImage(spider_icon_s, -16 + (Monster[Party.Level, All_Monsters].X * 16), -16 + (Monster[Party.Level, All_Monsters].Y * 16) + 12, 0)
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 2 Then DrawImage(spider_icon_s, -16 + (Monster[Party.Level, All_Monsters].X * 16), -16 + (Monster[Party.Level, All_Monsters].Y * 16) + 8, 0)
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 1 Then DrawImage(spider_icon_s, -16 + (Monster[Party.Level, All_Monsters].X * 16), -16 + (Monster[Party.Level, All_Monsters].Y * 16) + 4, 0)
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 0 Then DrawImage(spider_icon_s, -16 + (Monster[Party.Level, All_Monsters].X * 16), -16 + (Monster[Party.Level, All_Monsters].Y * 16), 0)
				EndIf
				If Monster[Party.Level, All_Monsters].Graphic = "02" Then
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 3 Then DrawImage(ghost_icon_s, -16 + (Monster[Party.Level, All_Monsters].X * 16), -16 + (Monster[Party.Level, All_Monsters].Y * 16) + 12, 0)
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 2 Then DrawImage(ghost_icon_s, -16 + (Monster[Party.Level, All_Monsters].X * 16), -16 + (Monster[Party.Level, All_Monsters].Y * 16) + 8, 0)
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 1 Then DrawImage(ghost_icon_s, -16 + (Monster[Party.Level, All_Monsters].X * 16), -16 + (Monster[Party.Level, All_Monsters].Y * 16) + 4, 0)
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 0 Then DrawImage(ghost_icon_s, -16 + (Monster[Party.Level, All_Monsters].X * 16), -16 + (Monster[Party.Level, All_Monsters].Y * 16), 0)
				EndIf
			
			End If
		
			If Monster[Party.Level, All_Monsters].Direction = 3 Then
						
				If Monster[Party.Level, All_Monsters].Graphic = "01" Then
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 3 Then DrawImage(spider_icon_w, -16 + (Monster[Party.Level, All_Monsters].X * 16) - 12, -16 + (Monster[Party.Level, All_Monsters].Y * 16) , 0)
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 2 Then DrawImage(spider_icon_w, -16 + (Monster[Party.Level, All_Monsters].X * 16) - 8, -16 + (Monster[Party.Level, All_Monsters].Y * 16) , 0)
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 1 Then DrawImage(spider_icon_w, -16 + (Monster[Party.Level, All_Monsters].X * 16) - 4, -16 + (Monster[Party.Level, All_Monsters].Y * 16) , 0)
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 0 Then DrawImage(spider_icon_w, -16 + (Monster[Party.Level, All_Monsters].X * 16), -16 + (Monster[Party.Level, All_Monsters].Y * 16), 0)
				EndIf
				If Monster[Party.Level, All_Monsters].Graphic = "02" Then
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 3 Then DrawImage(ghost_icon_w, -16 + (Monster[Party.Level, All_Monsters].X * 16) - 12, -16 + (Monster[Party.Level, All_Monsters].Y * 16) , 0)
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 2 Then DrawImage(ghost_icon_w, -16 + (Monster[Party.Level, All_Monsters].X * 16) - 8, -16 + (Monster[Party.Level, All_Monsters].Y * 16) , 0)
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 1 Then DrawImage(ghost_icon_w, -16 + (Monster[Party.Level, All_Monsters].X * 16) - 4, -16 + (Monster[Party.Level, All_Monsters].Y * 16) , 0)
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 0 Then DrawImage(ghost_icon_w, -16 + (Monster[Party.Level, All_Monsters].X * 16), -16 + (Monster[Party.Level, All_Monsters].Y * 16), 0)
				EndIf
				
			End If
		
			If Monster[Party.Level, All_Monsters].Direction = 4 Then
						
				If Monster[Party.Level, All_Monsters].Graphic = "01" Then
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 3 Then DrawImage(spider_icon_e, -16 + (Monster[Party.Level, All_Monsters].X * 16) + 12, -16 + (Monster[Party.Level, All_Monsters].Y * 16) , 0)
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 2 Then DrawImage(spider_icon_e, -16 + (Monster[Party.Level, All_Monsters].X * 16) + 8, -16 + (Monster[Party.Level, All_Monsters].Y * 16) , 0)
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 1 Then DrawImage(spider_icon_e, -16 + (Monster[Party.Level, All_Monsters].X * 16) + 4, -16 + (Monster[Party.Level, All_Monsters].Y * 16) , 0)
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 0 Then DrawImage(spider_icon_e, -16 + (Monster[Party.Level, All_Monsters].X * 16), -16 + (Monster[Party.Level, All_Monsters].Y * 16), 0)
				EndIf
				If Monster[Party.Level, All_Monsters].Graphic = "02" Then
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 3 Then DrawImage(ghost_icon_e, -16 + (Monster[Party.Level, All_Monsters].X * 16) + 12, -16 + (Monster[Party.Level, All_Monsters].Y * 16) , 0)
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 2 Then DrawImage(ghost_icon_e, -16 + (Monster[Party.Level, All_Monsters].X * 16) + 8, -16 + (Monster[Party.Level, All_Monsters].Y * 16) , 0)
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 1 Then DrawImage(ghost_icon_e, -16 + (Monster[Party.Level, All_Monsters].X * 16) + 4, -16 + (Monster[Party.Level, All_Monsters].Y * 16) , 0)
					If Monster[Party.Level, All_Monsters].Square_transition_frame = 0 Then DrawImage(ghost_icon_e, -16 + (Monster[Party.Level, All_Monsters].X * 16), -16 + (Monster[Party.Level, All_Monsters].Y * 16), 0)
				EndIf
				
			End If

		End If
		'End of monster XY>0

	Next
	
		
	
	DrawImage(map_grid, 0, 0, 0)

End Function


Function Inits()
	
	Local all_spiders:Byte
	
	For all_spiders = 1 To 30
	Monster[1, all_spiders].X = Rand(1, 20)
	Monster[1, all_spiders].Y = Rand(1, 20)
	Monster[1, all_spiders].Graphic = "01"
	Monster[1, all_spiders].Direction = Rand(1, 4)
	Monster[1, all_spiders].Square_transition_frame = 0
	Monster[1, all_spiders].Not_Allow_to_move = 0
	Monster[1, all_spiders].Walking = 1
	Next
	
	
	
	
	Monster[1, 1].X = 3
	Monster[1, 1].Y = 7
	Monster[1, 1].Graphic = "01"
	Monster[1, 1].Direction = 1
	Monster[1, 1].Square_transition_frame = 0
	Monster[1, 1].Not_Allow_to_move = 0
	Monster[1, 1].Walking = 1

	Monster[1, 2].X = 3
	Monster[1, 2].Y = 4
	Monster[1, 2].Graphic = "02"
	Monster[1, 2].Direction = 1
	Monster[1, 2].Not_Allow_to_move = 0
	Monster[1, 3].Walking = 0
	
		
	Party.Level = 1

	
End Function


Function Keyboard_commands()
	
	If KeyHit(KEY_W) Then
		
		If Monster[1, 2].Direction = 1 Then
			If Monster[1, 2].Y > 1 Then Monster[1, 2].Square_transition_frame:+1
		EndIf
		If Monster[1, 2].Direction = 2 Then
			If Monster[1, 2].Y < 20 Then Monster[1, 2].Square_transition_frame:+1
		EndIf
		If Monster[1, 2].Direction = 3 Then
			If Monster[1, 2].X > 1 Then Monster[1, 2].Square_transition_frame:+1
		EndIf
		If Monster[1, 2].Direction = 4 Then
			If Monster[1, 2].X < 20 Then Monster[1, 2].Square_transition_frame:+1
		EndIf
		If Monster[1, 2].Direction = 1 Then
			If Monster[1, 2].Square_transition_frame > 3 Then
			Monster[1, 2].Square_transition_frame = 0
			Monster[1, 2].Y:-1
			Return
			EndIf
		EndIf
		If Monster[1, 2].Direction = 2 Then
			If Monster[1, 2].Square_transition_frame > 3 Then
			Monster[1, 2].Square_transition_frame = 0
			Monster[1, 2].Y:+1
			Return
			EndIf
		EndIf
		If Monster[1, 2].Direction = 3 Then
			If Monster[1, 2].Square_transition_frame > 3 Then
			Monster[1, 2].Square_transition_frame = 0
			Monster[1, 2].X:-1
			Return
			EndIf
		EndIf
		If Monster[1, 2].Direction = 4 Then
			If Monster[1, 2].Square_transition_frame > 3 Then
			Monster[1, 2].Square_transition_frame = 0
			Monster[1, 2].X:+1
			Return
			EndIf
		EndIf
	EndIf
		
	
	
	If KeyHit(KEY_Q) Then
		If Monster[1, 2].Direction = 1 Then
			If Monster[1, 2].Square_transition_frame <> 0 Then Return
		Monster[1, 2].Direction = 3
		Return
		EndIf
		If Monster[1, 2].Direction = 2 Then
			If Monster[1, 2].Square_transition_frame <> 0 Then Return
		Monster[1, 2].Direction = 4
		Return
		EndIf
		If Monster[1, 2].Direction = 3 Then
			If Monster[1, 2].Square_transition_frame <> 0 Then Return
		Monster[1, 2].Direction = 2
		Return
		EndIf
		If Monster[1, 2].Direction = 4 Then
			If Monster[1, 2].Square_transition_frame <> 0 Then Return
		Monster[1, 2].Direction = 1
		Return
		EndIf
	EndIf
	If KeyHit(KEY_E) Then
		If Monster[1, 2].Direction = 1 Then
			If Monster[1, 2].Square_transition_frame <> 0 Then Return
		Monster[1, 2].Direction = 4
		Return
		EndIf
		If Monster[1, 2].Direction = 2 Then
			If Monster[1, 2].Square_transition_frame <> 0 Then Return
		Monster[1, 2].Direction = 3
		Return
		EndIf
		If Monster[1, 2].Direction = 3 Then
			If Monster[1, 2].Square_transition_frame <> 0 Then Return
		Monster[1, 2].Direction = 1
		Return
		EndIf
		If Monster[1, 2].Direction = 4 Then
			If Monster[1, 2].Square_transition_frame <> 0 Then Return
		Monster[1, 2].Direction = 2
		Return
		EndIf
	EndIf
	
	
	
	

End Function


The files you will need are here:
Click for sprites

So far so good for now , I will try to adopt this in my game.
I will tell you what I managed.


Matty(Posted 2015) [#10]
Glad youve managed to sort this out.


Takis76(Posted 2015) [#11]
Thank you , now I will adjust this code in the main game engine with new battle system.
And when the next version will be finished I will show a demo.


AdamStrange(Posted 2015) [#12]
Excellent - Sometimes you need to either step away from the code, or come at it from a different view. starting a small project to deal with a single issue is a great way of clearing your mind :)


Takis76(Posted 2015) [#13]
My monsters now are perfect and my new code was updated in my game core engine. No overlaps and smarter Artificial Intelligence , now my monsters can open doors , avoid other obstacles do attacks...
Everything goes very nice and now I am programming the battle system.


AdamStrange(Posted 2015) [#14]
It's great when it starts to come together :)