Monday, July 31, 2017

Monkey-X - Diffusion Limited Aggregation - Map Generator - code example


'diffusion limited aggregation (dla)

' This basically works like this.
' You create random walkers(dots) They move into a random direction.
' On the map there is a solid part and if the walker touches it he turns
' into a solid part. This is it.


Import mojo

' The random walker class. We only use it to store
' the x and y coordinates
Class walker
    Field x:Int,y:Int
    Field deleteme:Bool
    Method New(x:Int,y:Int)
        Self.x = x
        Self.y = y
    End Method
End Class

Class dla
    ' map width/height screen width/height
    ' tile width/height
    ' map containing the solids parts. 
    ' The walker list containing the walkers
    Field mw:Int,mh:Int,sw:Int,sh:Int
    Field tw:Float,th:Float
    Field map:Int[][]
    Field mywalker:List<walker>
    Field walkerradius:Int=1
    Field finished:Bool=False
    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)
        ' create our map array
        map = New Int[mw][]
        For Local i:=0 Until mw
            map[i] = New Int[mh]
        Next
        ' create the solid part
        mapcircle(mw/2,mh/2)
        ' create the walker list
        mywalker = New List<walker>
        'add some walkers to the list
        For Local i:=0 Until mw*mh/20
            addwalker(Rnd(4))
        Next
    End Method    
    ' Here we update the walkers. We keep adding them to 
    ' the screen until they have created a solid part 
    ' on the most part of the screen.
    Method update()
        ' speed it up    
        For Local speed:Int=0 Until 20
            ' loop through each walker on the list
            For Local i:=Eachin mywalker
                ' step in one of 8 directions    
                Local x:Int=Rnd(-2,2)
                Local y:Int=Rnd(-2,2)
                ' if we are not moving outside of the map 
                ' then update position
                If i.x+x >= 0 And i.x+x < mw Then i.x += x
                If i.y+y >= 0 And i.y+y < mh Then i.y += y
                ' if we touch a solid then
                If map[i.x][i.y] = 1 Then
                    ' flag for deletion
                    i.deleteme = True
                    ' if the last position was not near the edge of the screen
                    If i.x > 10 And i.x < mw-10 And i.y > 10 And i.y < mh-10
                        ' add new walker to the border of screen
                        addwalker(Rnd(4))
                    End If
                    ' Draw the new solid
                    mapcircle(i.x,i.y)
                End If
            Next
            ' Go through the walker list and see if
            ' we can delete any walkers from the list
            For Local i:=Eachin mywalker
                If i.deleteme = True Then mywalker.Remove(i)
            Next
        Next
        If mywalker.IsEmpty Then finished = True
    End Method
    ' Add a walker to the list at a random edge location
    Method addwalker(loc:Int)
        Select loc
            Case 0'top
                mywalker.AddLast(New walker(Rnd(mw),0))
            Case 1'bottom
                mywalker.AddLast(New walker(Rnd(mw),mh-1))
            Case 2'left
                mywalker.AddLast(New walker(0,Rnd(mh)))
            Case 3'"right"
                mywalker.AddLast(New walker(mw-1,Rnd(mh)))
        End Select
    End Method
    Method mapcircle(x:Int,y:Int)
        For Local y2:=-walkerradius To walkerradius
        For Local x2:=-walkerradius To walkerradius
            Local x3:Int=x+x2
            Local y3:Int=y+y2
            If x3<0 Or y3<0 Or x3>=mw Or y3>=mh Then Continue
            map[x3][y3] = 1
        Next
        Next
    End Method
    ' Draw the solid and walkers
    Method draw()
        For Local y:=0 Until mh
        For Local x:=0 Until mw
            If map[x][y] = 1 Then
                SetColor 255,255,255
                DrawRect x*tw,y*th,tw+1,th+1
            End If
        Next
        Next
        For Local i:=Eachin mywalker
            DrawCircle i.x*tw,i.y*th,walkerradius*th
        Next
        
    End Method
End Class

Class MyGame Extends App
    Field mydla:dla
    Field count:Int=200
    Method OnCreate()
        SetUpdateRate(60)
        Seed = GetDate[4] + GetDate[5]
        mydla = New dla(DeviceWidth(),DeviceHeight(),100,100)
    End Method
    Method OnUpdate()
        mydla.update()        
        If MouseHit(MOUSE_LEFT) Then mydla.finished = True ; count=0
        If mydla.finished = True Then
            count-=1
            If count<0 
            Local s:Int=Rnd(50,256)
            mydla = New dla(DeviceWidth(),DeviceHeight(),s,s)
            count=200
            End If
        End If
    End Method
    Method OnRender()
        Cls 0,0,0 
        mydla.draw()
        SetColor 255,255,255
        DrawText "Diffusion Limited Aggregation map generator - click = new",0,0
    End Method
End Class


Function Main()
    New MyGame()
End Function

Sunday, July 23, 2017

Monkey-X - Circle Line Segment Collision - code example


'
' Circle Line Segment Collision 
'
Import mojo

Class MyGame Extends App

    Method OnCreate()
        SetUpdateRate(30)        
    End Method
    Method OnUpdate()        
    End Method
    Method OnRender()
        Cls 0,0,0 
        SetColor 255,255,255        
        ' the mouse location for circle to line collision
        Local cx:Float=MouseX(),cy:Float=MouseY()
        Local cr:Int=20 'circle radius
        'draw the circle
        DrawCircle cx,cy,cr
        ' line coordinates
        Local l1:Int[]=[100,100,200,200] ' line x1,y1,x2,y2
        Local l2:Int[]=[250,100,290,300]
        ' draw the lines
        SetColor 100,100,100
        DrawLine l1[0],l1[1],l1[2],l1[3] 'x1,y1,x2,y2
        DrawLine l2[0],l2[1],l2[2],l2[3] ',,
     
         SetColor 255,255,255
         ' check the collisions
         If circlelinecollide(l1[0],l1[1],l1[2],l1[3],cx,cy,cr)
             DrawText "Collide line 1",0,20
         Else
             DrawText "No Collide line 1",0,20
         End If
         If circlelinecollide(l2[0],l2[1],l2[2],l2[3],cx,cy,cr)
             DrawText "Collide line 2",0,40
         Else
             DrawText "No Collide line 2",0,40
         End If
         
         DrawText "Move Mouse around to check collisions with lines.",0,0
         
    End Method
End Class

'
' Line(segment) to Circle Collision
'
Function circlelinecollide:Bool(sx1:Int, sy1:Int, sx2:Int, sy2:Int, cx:Int, cy:Int,cr:Float)
    Local xDelta:Float = sx2 - sx1
    Local yDelta:Float = sy2 - sy1
    Local px:Int,py:Int
    Local u:Float

    If ((xDelta = 0) And (yDelta = 0))    
      Error("Segment start equals segment end")
    End If

    u = ((cx - sx1) * xDelta + (cy - sy1) * yDelta) / (xDelta * xDelta + yDelta * yDelta)

    If (u < 0)
         px = sx1
          py = sy1
    Else If (u > 1)
        px = sx2
        py = sy2
    Else
        px = Int(Floor(sx1 + u * xDelta))
        py = Int(Floor(sy1 + u * yDelta))
    End If
    ' If the distance of the circle to the closest point in the line
    ' is less then the radius then there is a collision
    Local d:Int= Sqrt( Pow(px - cx,2) + Pow(py - cy,2))
    If d<=(cr) Then Return True
    Return False
End Function


Function Main()
    New MyGame()
End Function

Friday, July 21, 2017

Monkey-X - 4 Way Flood Fill Pathfinding - code example


Import mojo

' This is a class that holds x and y variables.
Class pathnode
    Field x:Int,y:Int
    Method New(x:Int,y:Int)
        Self.x = x
        Self.y = y
    End Method
End Class

Class MyGame Extends App
    'Our map. it holds the walls(-1) and the distance
    'from the start to end position.
    Field cmap:Int[][]
    ' map width and height
    Field mw:Int=20,mh:Int=20
    ' tile width and height
    Field tw:Float=22,th:Float=18
    ' our start and end position
    Field sx:Int,sy:Int,ex:Int,ey:Int
    ' this holds our path
    Field path:List<pathnode>
    Method OnCreate()
        SetUpdateRate(1)
        ' create a new map array
        cmap = New Int[mw][]
        For Local i:=0 Until mw
            cmap[i] = New Int[mh]
        Next
        ' set the tile width and height.
        tw = DeviceWidth() / Float(mw)
        th = DeviceHeight() / Float(mh)
        ' get a random seed so every time we run
        ' this program we get a different result.
        Seed = GetDate[4] + GetDate[5]
    End Method
    Method OnUpdate()
        'erase the map
        For Local y:=0 Until mh
        For Local x:=0 Until mw
            cmap[x][y] = 0
        Next
        Next        
 
        'draw some contents on the map
        For Local x:=0 Until mw Step 5
        For Local y:=2 Until mh-2
            cmap[x][y] = -1
        Next
        Next
        For Local i:=0 Until 5
            Local x1:Int=Rnd(0,mw-4)
            Local y1:Int=Rnd(0,mh-4)
            For Local x2:=0 Until 4
            For Local y2:=0 Until 4
                cmap[x1+x2][y1+y2] = -1
            Next
            Next
        Next
 
        For Local y:=0 Until mh Step 5
            For Local x:=0 Until mw
                cmap[x][y] = 0
            Next
        Next
 
 
 
         'find start and end position
         Repeat
             sx = Rnd(0,mw)
             sy = Rnd(0,mh)
             ex = Rnd(0,mw)
             ey = Rnd(0,mh)
             If cmap[sx][sy] = 0 And cmap[ex][ey] = 0 
                 If sx<>ex And sy<>ey
                     Exit
                 End If
             End If
         Forever
 
         'flood the map with distance from
         'sx and sy
         ' Create a list with a class inside it (the class has
         ' the x and y variables)
         Local ol:List<pathnode> = New List<pathnode>
         ' Add the start position on the list
         ol.AddLast(New pathnode(sx,sy))
         ' set the cloes map at the start position to distance 1
         cmap[sx][sy] = 1
         ' some helper arrays. We can determine the top,right,and bottom
         ' and left position cells with these numbers.
         Local dx:Int[] = [0,1,0,-1]
         Local dy:Int[] = [-1,0,1,0]
         ' While there is contents in the list
         While ol.Count <> 0
             ' Get the current location
             Local x1:Int=ol.First.x
             Local y1:Int=ol.First.y
             ' Remove the current location from the list
             ol.RemoveFirst
             ' Get 4 new positions around the current positions
            For Local i:=0 Until 4
                ' Set new x and y
                Local nx:Int=x1+dx[i]
                Local ny:Int=y1+dy[i]
                ' If the coordinates are inside the map
                If nx>=0 And ny>=0 And nx<mw And ny<mh
                ' If the closedmap is not written to yet
                 If cmap[nx][ny] = 0 
                     ' Set the new distance based on the current distance
                     cmap[nx][ny] = cmap[x1][y1] + 1
                     ' Add new position to the list
                     ol.AddLast(New pathnode(nx,ny))
                 End If
                 End If
            Next
         Wend
         
         ' Make the path. Here we start at the end position
         ' and find the lowest value around our current position
         ' and so on until we are at the start position.
         ' Get the current position
         Local x1:Int=ex,y1:Int=ey
         ' Reset our path list
         path = New List<pathnode>
         ' Set the first path contents(end x and y)
         path.AddLast(New pathnode(x1,y1))
         ' Little counter for if we enter an infinite loop
         Local cnt:Int=0
         ' While we are not near the map distance of 1
        While cmap[x1][y1] > 1        
            ' Get tge current poditions distance    
            Local lowest:Int=cmap[x1][y1]
            ' Create 2 new variables that hold the new location
            Local nx:Int,ny:Int
            ' If the position left of our current position is on the map
            ' and is not a wall and has a distance smaller that of our
            ' current position.
            If x1-1>=0 And cmap[x1-1][y1]>-1 And cmap[x1-1][y1] < lowest Then 
                ' set new position
                nx=x1-1
                ny=y1
                ' set new lowest variable
                lowest = cmap[nx][ny]
            End If
            ' If the position right of our current position is on the map
            ' and is not a wall and has a distance smaller that of our
            ' current position.
            If x1+1<mw And cmap[x1+1][y1]>-1 And cmap[x1+1][y1] < lowest
                nx=x1+1
                ny=y1
                lowest = cmap[nx][ny]
            End If
            ' If the position top of our current position is on the map
            ' and is not a wall and has a distance smaller that of our
            ' current position.
            If y1-1>=0 And cmap[x1][y1-1]>-1 And cmap[x1][y1-1] < lowest
                nx=x1
                ny=y1-1
                lowest = cmap[nx][ny]
            End If
            ' If the position at the bottom of our current position is on the map
            ' and is not a wall and has a distance smaller that of our
            ' current position.
            If y1+1 < mh And cmap[x1][y1+1]>-1 And cmap[x1][y1+1] < lowest
                nx=x1
                ny=y1+1
                lowest = cmap[nx][ny]
            End If
            ' Here we should have a new position so we put it into 
            ' our current position
            x1 = nx
            y1 = ny
            ' Add the next position to the bottom of our path list
            path.AddLast(New pathnode(x1,y1))
            ' If we can not go anywhere then exit this loop if we have done
            ' a thousand loops.
            cnt+=1
            If cnt>1000 Then Exit
        Wend
    End Method
    Method OnRender()
        Cls 0,0,0 
        SetColor 255,255,255
        'draw the map
        For Local y:=0 Until mh
        For Local x:=0 Until mw
            If cmap[x][y] = -1 Then
                DrawRect x*tw,y*th,tw,th
            End If
        Next
        Next
        'draw the path
        If path
        For Local i:=Eachin path
            SetColor 255,255,0
            DrawRect i.x*tw,i.y*th,tw,th
            SetColor 255,255,255
            DrawText cmap[i.x][i.y],i.x*tw,i.y*th
        Next
        End If
        'Draw the end and start position
        SetColor 255,0,0
        DrawRect sx*tw,sy*th,tw,th
        DrawRect ex*tw,ey*th,tw,th
        
        SetColor 255,255,255
        DrawText "FloodFill pathfinding",0,0
    End Method
End Class


Function Main()
    New MyGame()
End Function

Thursday, July 13, 2017

Monkey-X - Flow Fields Multiple Agents - rts - code example


Around each agent flow fields are placed direction agents who want to travel 
in their path around them. On the map itself random flowfields are placed.

The code does not work 100% correctly yet since sometimes agents go through 
each other.

Import mojo

Class flowfield
    Field mapwidth:Int,mapheight:Int
    Field tilewidth:Float,tileheight:Float
    Field screenwidth:Int,screenheight:Int
    Field map:Int[][]
    Field tempmap:Int[][]
    Field flowlinestartx:Int
    Field flowlinestarty:Int

    Method New(screenwidth:Int,screenheight:Int,mapwidth:Int,mapheight:Int)
        Self.screenwidth = screenwidth
        Self.screenheight = screenheight
        Self.tilewidth = Float(screenwidth)/Float(mapwidth)
        Self.tileheight = Float(screenheight)/Float(mapheight)
        Self.mapwidth = mapwidth
        Self.mapheight = mapheight
        ' make a array
        map = New Int[mapwidth][]
        tempmap = New Int[mapwidth][]        
        For Local i = 0 Until mapwidth
            map[i] = New Int[mapheight]
            tempmap[i] = New Int[mapheight]
        Next    
        ' -1 if no direction
        For Local y:=0 Until mapheight
        For Local x:=0 Until mapheight
            map[x][y] = -1
        Next
        Next

        '
        ' Here we create a number of points
        ' with which we draw the lines in between.
        '
        Seed = GetDate[5]
        Local lastx:Int=Rnd(2,mapwidth-2)
        Local lasty:Int=Rnd(2,mapheight-2)
        flowlinestartx = lastx
        flowlinestarty = lasty
        For Local i:=0 Until mapwidth*mapheight/10
            Local newx:Int=Rnd(2,mapwidth-2)
            Local newy:Int=Rnd(2,mapheight-2)
            flowline(lastx,lasty,newx,newy)
            lastx=newx
            lasty=newy
        Next
    End Method

    'copy temp into map
    Method refreshmap()
        For Local y:=0 Until mapheight
        For Local x:=0 Until mapwidth
            map[x][y] = tempmap[x][y]
        Next
        Next
    End Method
    
    ' Make a flowfield(line) between two points
    Method flowline(x1:Int,y1:Int,x2:Int,y2:Int)
        Local dx:Int, dy:Int, sx:Int, sy:Int, e:Int
          dx = Abs(x2 - x1)
          sx = -1
          If x1 < x2 Then sx = 1      
          dy = Abs(y2 - y1)
          sy = -1
          If y1 < y2 Then sy = 1
          If dx < dy Then 
             e = dx / 2 
          Else 
             e = dy / 2          
          End If
          Local exitloop:Bool=False
          While exitloop = False
            'SetColor 255,255,255
            'DrawPoint x1,y1
            If x1 = x2 
                If y1 = y2
                    exitloop = True
                End If
            End If
            For Local y:=-6 To 6
            For Local x:=-6 To 6
                If x1+x<0 Or x1+x>=mapwidth Or y1+y<0 Or y1+y>=mapheight Then continue
                map[x1+x][y1+y] = pointto(x1+x,y1+y,x1,y1)
            Next
            Next
            map[x1][y1] = pointto(x1,y1,x2,y2)
            If dx > dy Then
                x1 += sx ; e -= dy 
                  If e < 0 Then e += dx ; y1 += sy
            Else
                y1 += sy ; e -= dx 
                If e < 0 Then e += dy ; x1 += sx
            Endif
          Wend
          ' put contents in tempmap
        For Local y:=0 Until mapheight
        For Local x:=0 Until mapwidth
            tempmap[x][y] = map[x][y]
        Next
        Next
     End Method
    ' point the flow field direction to the x2,y2 from x1,y1
     Function pointto:Int(x1:Int,y1:Int,x2:Int,y2:Int)
        Local nd:Int=-1
        If x1<x2 Then nd=0
        If x1>x2 Then nd=4
        If y1<y2 Then nd=2
        If y1>y2 Then nd=6                    
        If x1<x2 And y1<y2 Then nd=1
        If x1>x2 And y1<y2 Then nd=3
        If x1<x2 And y1>y2 Then nd=7
        If x1>x2 And y1>y2 Then nd=5
        Return nd
     End Function

    Method draw()
        SetColor 255,255,255
        For Local y:=0 Until mapheight
        For Local x:=0 Until mapwidth
            Local direction:Int = map[x][y]
            If direction=-1 Then Continue
            Local x1:Float=Float(x)*tilewidth+tilewidth/2
            Local y1:Float=Float(y)*tileheight+tileheight/2
            Local ang:Int= (360/8*direction)
            Local x2:Float=x1+(Cos(ang)*tilewidth/2)
            Local y2:Float=y1+(Sin(ang)*tileheight/2)        
            Local x3:Float=x1+(Cos(ang+150)*tilewidth/4)
            Local y3:Float=y1+(Sin(ang+150)*tileheight/4)        
            Local x4:Float=x1+(Cos(ang-150)*tilewidth/4)
            Local y4:Float=y1+(Sin(ang-150)*tileheight/4)        
            
            DrawPoly([x2,y2,x3,y3,x4,y4])
        Next
        Next
    End Method
End Class

Class alien
    Field alienx:Float,alieny:Float
    Field speed:Float
    Method New(x:Int,y:Int)
        Self.alienx = x
        Self.alieny = y
        Self.speed = Rnd(0.1,1)
    End Method
    '
    ' here we modify the flowfield so that other units can move around the
    ' unit. We do this by placing arrows around the unit based on his direction.
    Method modifyflowmap()
        Local x:Int=alienx / myflowfield.tilewidth
        Local y:Int=alieny / myflowfield.tileheight
        If x-1<0 Or x+1>=myflowfield.mapwidth Or y-1<0 Or y+1>=myflowfield.mapheight Then Return
        Local d:Int=myflowfield.map[x][y]
        Local a:Int[8]
        Select d
            Case 2 'moving down
            a[0]=2;a[1]=1;a[2]=2;a[3]=7
            a[4]=0;a[5]=5;a[6]=2;a[7]=3
            Case 3 'moving left down
            a[0]=4;a[1]=3;a[2]=2;a[3]=3
            a[4]=4;a[5]=1;a[6]=2;a[7]=3
            Case 4 'moving left
            a[0]=4;a[1]=5;a[2]=4;a[3]=3
            a[4]=4;a[5]=5;a[6]=6;a[7]=3
            Case 5 'moving left up
            a[0]=4;a[1]=5;a[2]=6;a[3]=5
            a[4]=4;a[5]=5;a[6]=6;a[7]=3
            Case 6 ' moving up
            a[0]=4;a[1]=5;a[2]=6;a[3]=7
            a[4]=6;a[5]=5;a[6]=6;a[7]=7
            Case 7 ' moving right up
            a[0]=0;a[1]=5;a[2]=6;a[3]=7
            a[4]=0;a[5]=7;a[6]=6;a[7]=7
            Case 0 ' moving right
            a[0]=0;a[1]=1;a[2]=6;a[3]=7
            a[4]=0;a[5]=1;a[6]=0;a[7]=7
            Case 1 ' moving right down
            a[0]=0;a[1]=1;a[2]=2;a[3]=3
            a[4]=0;a[5]=1;a[6]=2;a[7]=7
            
            
            
            
            
        End Select
        
        myflowfield.map[x][y+1] = a[0]
        myflowfield.map[x-1][y+1] = a[1]
        myflowfield.map[x-1][y] = a[2]
        myflowfield.map[x-1][y-1] = a[3]
        myflowfield.map[x][y-1] = a[4]
        myflowfield.map[x+1][y-1] = a[5]
        myflowfield.map[x+1][y] = a[6]
        myflowfield.map[x+1][y+1] = a[7]
        
    End Method
    Method move()
        Local x2:Int=alienx/myflowfield.tilewidth
        Local y2:Int=alieny/myflowfield.tileheight
        
        Local d:Int=myflowfield.map[x2][y2]

        ' Move the alien based on the flowfield array's direction 0=right 1=rightdown 7=rightup
        Select d
            Case 0
            alienx+=speed
            Case 1
            alienx+=speed;alieny+=speed
            Case 2
            alieny+=speed
            Case 3
            alienx-=speed
            alieny+=speed
            Case 4
            alienx-=speed
            Case 5
            alienx-=speed
            alieny-=speed
            Case 6
            alieny-=speed
            Case 7
            alieny-=speed
            alienx+=speed
        End Select
        ' stay inside array(screen)
        If alienx+10>myflowfield.screenwidth Then alienx = myflowfield.screenwidth-10
        If alienx-10<0 Then alienx = 10
        If alieny+10>myflowfield.screenheight Then alieny = myflowfield.screenheight-10
        If alieny-10<0 Then alieny = 10
        
    End Method
    Method draw()
        SetColor 255,0,0
        DrawCircle(alienx,alieny,myflowfield.tilewidth/2)
    End Method
End Class

Global myflowfield:flowfield
Global myalien:List<alien> = New List<alien>


Class MyGame Extends App
    Field mapwidth:Int,mapheight:Int
    Method OnCreate()
        Seed = GetDate[5] + GetDate[4]
        SetUpdateRate(60)        
        myflowfield = New flowfield(DeviceWidth(),DeviceHeight(),20,20)
        For Local i:=0 Until 10
            myalien.AddLast(New alien(Rnd(DeviceWidth),Rnd(DeviceHeight)))
        Next
    End Method
    Method OnUpdate()    

        myflowfield.refreshmap()        
        For Local i:=Eachin myalien
            i.modifyflowmap
            i.move()
        Next            
           
        ' if pressed space or no move by alien then new flowfield
        If KeyHit(KEY_SPACE) Or MouseHit(MOUSE_LEFT)
            Seed = Millisecs()
            mapwidth  = Rnd(20,80)
            mapheight = mapwidth
            myflowfield = New flowfield(DeviceWidth(),DeviceHeight(),mapwidth,mapheight)
            myalien.Clear()
            For Local i:=0 Until mapwidth*mapheight/20
                Local x:Int=Rnd(DeviceWidth)
                Local y:Int=Rnd(DeviceHeight)
                myalien.AddLast(New alien(x,y))
            Next
        End If    
        
    End Method
    Method OnRender()
        Cls 0,0,0 
        SetColor 255,255,255
        
        myflowfield.draw()
        
        For Local i:=Eachin myalien
            i.draw
        Next

        SetColor 255,255,255
        DrawText("Flow Fields multiple agents - space(touch/lmb) new map.",0,0)
    End Method
End Class

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

Wednesday, July 12, 2017

Monkey-X - Flow Fields - Lines - code example


Import mojo

Class flowfield
    Field mapwidth:Int,mapheight:Int
    Field tilewidth:Float,tileheight:Float
    Field screenwidth:Int,screenheight:Int
    Field map:Int[][]
    Field flowlinestartx:Int
    Field flowlinestarty:Int

    Method New(screenwidth:Int,screenheight:Int,mapwidth:Int,mapheight:Int)
        Self.screenwidth = screenwidth
        Self.screenheight = screenheight
        Self.tilewidth = Float(screenwidth)/Float(mapwidth)
        Self.tileheight = Float(screenheight)/Float(mapheight)
        Self.mapwidth = mapwidth
        Self.mapheight = mapheight
        ' make a array
        map = New Int[mapwidth][]
        For Local i = 0 Until mapwidth
            map[i] = New Int[mapheight]
        Next    
        ' -1 if no direction
        For Local y:=0 Until mapheight
        For Local x:=0 Until mapheight
            map[x][y] = -1
        Next
        Next
        '
        ' Here we create a number of points
        ' with which we draw the lines in between.
        '
        Seed = GetDate[5]
        Local lastx:Int=Rnd(2,mapwidth-2)
        Local lasty:Int=Rnd(2,mapheight-2)
        flowlinestartx = lastx
        flowlinestarty = lasty
        For Local i:=0 Until 5
            Local newx:Int=Rnd(2,mapwidth-2)
            Local newy:Int=Rnd(2,mapheight-2)
            flowline(lastx,lasty,newx,newy)
            lastx=newx
            lasty=newy
        Next
    End Method
    ' Make a flowfield(line) between two points
    Method flowline(x1:Int,y1:Int,x2:Int,y2:Int)
        Local dx:Int, dy:Int, sx:Int, sy:Int, e:Int
          dx = Abs(x2 - x1)
          sx = -1
          If x1 < x2 Then sx = 1      
          dy = Abs(y2 - y1)
          sy = -1
          If y1 < y2 Then sy = 1
          If dx < dy Then 
             e = dx / 2 
          Else 
             e = dy / 2          
          End If
          Local exitloop:Bool=False
          While exitloop = False
            'SetColor 255,255,255
            'DrawPoint x1,y1
            If x1 = x2 
                If y1 = y2
                    exitloop = True
                End If
            End If
            For Local y:=-1 To 1
            For Local x:=-1 To 1
                map[x1+x][y1+y] = pointto(x1+x,y1+y,x1,y1)
            Next
            Next
            map[x1][y1] = pointto(x1,y1,x2,y2)
            If dx > dy Then
                x1 += sx ; e -= dy 
                  If e < 0 Then e += dx ; y1 += sy
            Else
                y1 += sy ; e -= dx 
                If e < 0 Then e += dy ; x1 += sx
            Endif
          Wend
     End Method
    ' point the flow field direction to the x2,y2 from x1,y1
     Function pointto:Int(x1:Int,y1:Int,x2:Int,y2:Int)
        Local nd:Int=-1
        If x1<x2 Then nd=0
        If x1>x2 Then nd=4
        If y1<y2 Then nd=2
        If y1>y2 Then nd=6                    
        If x1<x2 And y1<y2 Then nd=1
        If x1>x2 And y1<y2 Then nd=3
        If x1<x2 And y1>y2 Then nd=7
        If x1>x2 And y1>y2 Then nd=5
        Return nd
     End Function

    Method draw()
        SetColor 255,255,255
        For Local y:=0 Until mapheight
        For Local x:=0 Until mapwidth
            Local direction:Int = map[x][y]
            If direction=-1 Then Continue
            Local x1:Float=Float(x)*tilewidth+tilewidth/2
            Local y1:Float=Float(y)*tileheight+tileheight/2
            Local ang:Int= (360/8*direction)
            Local x2:Float=x1+(Cos(ang)*tilewidth/2)
            Local y2:Float=y1+(Sin(ang)*tileheight/2)        
            Local x3:Float=x1+(Cos(ang+150)*tilewidth/4)
            Local y3:Float=y1+(Sin(ang+150)*tileheight/4)        
            Local x4:Float=x1+(Cos(ang-150)*tilewidth/4)
            Local y4:Float=y1+(Sin(ang-150)*tileheight/4)        
            
            DrawPoly([x2,y2,x3,y3,x4,y4])
        Next
        Next
    End Method
End Class

Class MyGame Extends App
    Field myflowfield:flowfield
    Field alienx:Int=200,alieny:Int=200
    Field lastx:Int,lasty:Int
    Method OnCreate()
        SetUpdateRate(60)
        myflowfield = New flowfield(DeviceWidth(),DeviceHeight(),50,50)
        alienx = myflowfield.flowlinestartx * myflowfield.tilewidth
        alieny = myflowfield.flowlinestarty * myflowfield.tileheight
    End Method
    Method OnUpdate()    
        Local d:Int=myflowfield.map[alienx/myflowfield.tilewidth][alieny/myflowfield.tileheight]
        ' Move the alien based on the flowfield array's direction 0=right 1=rightdown 7=rightup
        Select d
            Case 0
            alienx+=1
            Case 1
            alienx+=1;alieny+=1
            Case 2
            alieny+=1
            Case 3
            alienx-=1
            alieny+=1
            Case 4
            alienx-=1
            Case 5
            alienx-=1
            alieny-=1
            Case 6
            alieny-=1
            Case 7
            alieny-=1
            alienx+=1
        End Select
    
        ' stay inside array(screen)
        If alienx+10>DeviceWidth() Then alienx = DeviceWidth()-10
        If alienx-10<0 Then alienx = 10
        If alieny+10>DeviceHeight() Then alieny = DeviceHeight()-10
        If alieny-10<0 Then alieny = 10

        ' if we press the left mouse then move the alien to mouse position
        If MouseHit(MOUSE_LEFT) Then
            alienx = MouseX
            alieny = MouseY
        End If
        
        ' if pressed space or no move by alien then new flowfield
        If KeyHit(KEY_SPACE) Or lastx = alienx And lasty = alieny
            myflowfield = New flowfield(DeviceWidth(),DeviceHeight(),50,50)
            alienx = myflowfield.flowlinestartx * myflowfield.tilewidth
            alieny = myflowfield.flowlinestarty * myflowfield.tileheight
        End If    
        
        lastx = alienx
        lasty = alieny
    End Method
    Method OnRender()
        Cls 0,0,0 
        SetColor 255,255,255
        myflowfield.draw()
        SetColor 255,0,0
        DrawCircle(alienx,alieny,20)
        SetColor 255,255,255
        DrawText("Flow Fields (lines)- Press lmb to place alien - space new map.",0,0)
    End Method
End Class

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

Monkey-X - Flow Fields - code example

Import mojo

Class flowfield
    Field mapwidth:Int,mapheight:Int
    Field tilewidth:Float,tileheight:Float
    Field screenwidth:Int,screenheight:Int
    Field map:Int[][]
    Method New(screenwidth:Int,screenheight:Int,mapwidth:Int,mapheight:Int)
        Self.screenwidth = screenwidth
        Self.screenheight = screenheight
        Self.tilewidth = Float(screenwidth)/Float(mapwidth)
        Self.tileheight = Float(screenheight)/Float(mapheight)
        Self.mapwidth = mapwidth
        Self.mapheight = mapheight
        map = New Int[mapwidth][]
        For Local i = 0 Until mapwidth
            map[i] = New Int[mapheight]
        Next    
        pointtocenter
    End Method
    '
    ' In the flowfield array there are 8 directions. 
    ' Here we point the directions towards the center
    '
    Method pointtocenter()
        For Local y:Int=0 Until mapheight
        For Local x:Int=0 Until mapwidth
            Local x2:Int=mapwidth/2
            Local y2:Int=mapheight/2
            Local nd:Int=0
            If x<x2 Then nd=0
            If x>x2 Then nd=4
            If y<y2 Then nd=2
            If y>y2 Then nd=6
            
            If x<x2 And y<y2 Then nd=1
            If x>x2 And y<y2 Then nd=3
            If x<x2 And y>y2 Then nd=7
            If x>x2 And y>y2 Then nd=5

            map[x][y] = nd
        Next
        Next
    End Method
    Method draw()
        SetColor 255,255,255
        For Local y:=0 Until mapheight
        For Local x:=0 Until mapwidth
            Local direction:Int = map[x][y]
            Local x1:Float=Float(x)*tilewidth+tilewidth/2
            Local y1:Float=Float(y)*tileheight+tileheight/2
            Local ang:Int= (360/8*direction)
            Local x2:Float=x1+(Cos(ang)*tilewidth/2)
            Local y2:Float=y1+(Sin(ang)*tileheight/2)        
            Local x3:Float=x1+(Cos(ang+150)*tilewidth/4)
            Local y3:Float=y1+(Sin(ang+150)*tileheight/4)        
            Local x4:Float=x1+(Cos(ang-150)*tilewidth/4)
            Local y4:Float=y1+(Sin(ang-150)*tileheight/4)        
            
            DrawPoly([x2,y2,x3,y3,x4,y4])
        Next
        Next
    End Method
End Class

Class MyGame Extends App
    Field myflowfield:flowfield
    Field alienx:Int=200,alieny:Int=200
    Method OnCreate()
        SetUpdateRate(60)
        myflowfield = New flowfield(DeviceWidth(),DeviceHeight(),20,20)
    End Method
    Method OnUpdate()        
        Local d:Int=myflowfield.map[alienx/myflowfield.tilewidth][alieny/myflowfield.tileheight]
        Select d
            Case 0
            alienx+=1
            Case 1
            alienx+=1;alieny+=1
            Case 2
            alieny+=1
            Case 3
            alienx-=1
            alieny+=1
            Case 4
            alienx-=1
            Case 5
            alienx-=1
            alieny-=1
            Case 6
            alieny-=1
            Case 7
            alieny-=1
            alienx+=1
        End Select

        If rectsoverlap(alienx-10,alieny-10,20,20,DeviceWidth/2-30,DeviceHeight/2-30,60,60)
            alienx = Rnd(DeviceWidth)
            alieny = Rnd(DeviceHeight)
        End If

        If MouseHit(MOUSE_LEFT) Then
            alienx = MouseX
            alieny = MouseY
        End If

    End Method
    Method OnRender()
        Cls 0,0,0 
        SetColor 255,255,255
        myflowfield.draw()
        SetColor 255,0,0
        DrawCircle(alienx,alieny,20)
        SetColor 255,255,255
        DrawText("Flow Fields - Press Left Mouse to place alien",0,0)
    End Method
End Class

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

Sunday, July 9, 2017

Monkey-X - Example - Boids Flocking - code example


' Converted to Monkey-X - from Blitzmax ( coded originally by Flanker)
'

Import mojo

Class obstacle
    Field x:Float,y:Float
    Field radius:Int
    Method New(x:Int,y:Int)
        Self.x = x
        Self.y = y
        Self.radius = 20    
    End Method
    Method draw()
        SetColor 255,255,0
        DrawCircle(x,y,radius)
    End Method    
End Class

Class boid
    Field friendlist:List<boid> = New List<boid>
    Field x:Float,y:Float,angle:Float
    Field vx:Float,vy:Float
    Field alignspeed:Float = 8
    Field speed:Float = 3
    Field smoothturn:Float = 25
    Field radius:Float = 10
    Field cohesionfactor:Float = 100
    Field friendradius:Float = 75
    Field frienddistance:Float = 30
    Field friendsqradius:Float
    Field friendsqrdistance:Float
    Field obstaclemargin:Float = 2
    Method New()
        friendsqradius = friendradius*friendradius
        friendsqrdistance = frienddistance*frienddistance
    End Method
    Method create(count:Int)
        For Local i:=0 Until count
            Local newboid:boid = New boid()
            newboid.x = Rnd(640)
            newboid.y = Rnd(480)
            newboid.angle = Rnd(360)
            boidlist.AddLast(newboid)
        Next
    End Method
    Method update()
        getfriends()
        If friendlist.Count > 0
            vx = 0
            vy = 0
            cohesion()
            obstacle()
            distance()
            align()
        Else
            obstacle()
        End If        
        ' Clamp the movement speed
         If vx < -2 Then vx = -2 
         If vy < -2 Then vy = -2
         If vx > 2 Then vx = 2
         If vy > 2 Then vy = 2
        move()
    End Method
    Method obstacle()
        For Local i:=Eachin myobstacle
            Local diffx:Float = x - i.x
            Local diffy:Float = y - i.y
            Local sqrdistance:Float=diffx*diffx+diffy*diffy
            If diffx*diffx+diffy*diffy < i.radius*i.radius*i.radius/obstaclemargin
                vx -= (i.x - x) / Sqrt(sqrdistance) 
                vy -= (i.y - y) / Sqrt(sqrdistance)
            End If
        Next
    End Method
    Method cohesion()
        Local centerx:Float
        Local centery:Float
        For Local i:= Eachin friendlist
            centerx += i.x
            centery += i.y
        Next
        centerx /= friendlist.Count
        centery /= friendlist.Count
        vx += (centerx-x) / cohesionfactor
        vy += (centery-y) / cohesionfactor
    End Method
    Method distance()
        For Local i:=Eachin friendlist
            Local diffx:Float=x-i.x
            Local diffy:Float=y-i.y
            Local sqrdistance:Float=diffx*diffx+diffy*diffy
            If diffx*diffx+diffy*diffy < friendsqrdistance
                vx -= (i.x - x) / Sqrt(sqrdistance)
                vy -= (i.y - y) / Sqrt(sqrdistance)
            End If
        Next
    End Method
    Method align()
        Local sumvx:Float
        Local sumvy:Float
        For Local i:=Eachin friendlist
            sumvx += i.vx
            sumvy += i.vy
        Next
        sumvx /= friendlist.Count
        sumvy /= friendlist.Count
        vx += (sumvx - vx) / alignspeed
        vy += (sumvy - vy) / alignspeed
    End Method
    Method move()
         x += vx
        y += vy
        angle = smoothrotate(x,y,angle,x+vx,y+vy,smoothturn)
        x += Cos(angle) * speed
        y += Sin(angle) * speed
        If x<0 Then x = 640
        If y<0 Then y = 480
        If x>640 Then x = 0
        If y>480 Then y = 0
    End Method
    Method getfriends()
        friendlist.Clear()
        For Local i:=Eachin boidlist
            Local diffx:Float=x-i.x
            Local diffy:Float=y-i.y            
            If diffx*diffx+diffy*diffy < friendsqradius
                If i <> Self Then 
                    friendlist.AddLast(i)
                End If
            End If
        Next
    End Method
    Method updateall()
        For Local i:=Eachin boidlist
            i.update
        Next
    End Method
    Method drawall()
        For Local i:=Eachin boidlist
            i.draw
        Next
    End Method
    Method draw()
        SetColor 255,255,255
        DrawCircle(x,y,10)
    End Method
    Function smoothrotate:Float(sourceX:Float,sourceY:Float,sourceAngle:Float,destX:Float,destY:Float,smooth:Float)
        ' Thanks to BlackSp1der on BB forums for this piece of code ! <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
        Local targetAngle:Float = ATan2(sourceY-destY,sourceX-destX)
        Local tempAngle:Float = targetAngle - Sgn(targetAngle-sourceAngle) * 360
        If Abs(targetAngle-sourceAngle) > Abs(tempAngle-sourceAngle) Then targetAngle = tempAngle
        If sourceAngle <> targetAngle Then sourceAngle = sourceAngle - Sgn(targetAngle-sourceAngle) * (180-Abs(targetAngle-sourceAngle)) / (1+smooth)
        If sourceAngle >= 360 Then sourceAngle -= 360 Else If sourceAngle < 0 Then sourceAngle += 360
        Return sourceAngle
    End Function    
End Class

Global myobstacle:List<obstacle>
Global boidlist:List<boid> = New List<boid>

Class MyGame Extends App
    Field myboid:boid
    Method OnCreate()
        SetUpdateRate(60)
        myobstacle = New List<obstacle>
        For Local i:Int = 0 Until 10
            myobstacle.AddLast(New obstacle(Rnd(640),Rnd(480)))
        Next
        myboid = New boid()
        myboid.create(20)
    End Method
    Method OnUpdate()
        myboid.updateall()     
        If MouseDown(MOUSE_LEFT) Then myobstacle.AddLast(New obstacle(MouseX,MouseY))
        If KeyHit(KEY_SPACE) Then 
            For Local i:Int = 0 Until 10
                myobstacle.AddLast(New obstacle(Rnd(640),Rnd(480)))
            Next
        End If
    End Method
    Method OnRender()
        Cls 0,0,0 
        SetColor 255,255,255
        myboid.drawall()
        For Local i:=Eachin myobstacle
            i.draw
        Next
        SetColor 255,255,255
        DrawText "Hold LMB to draw obstacles.",0,0
        DrawText "Press space for random obstacles.",0,20
    End Method
End Class

Function Main()
    New MyGame()
End Function