Monday, January 26, 2015

Monkey-X - 2d Dungeon Generator (small to large maps) code example




Here is a new version of my Dungeon Generator. I have now used open and closed lists for the doors. The code can generate larger maps. Hundreds of rooms.

Code below :

This basically is how it works :

It creates one door in the open list.
It creates a room on the door and 3 new doors on the walls
the doors are added to the open list
it selects a random door and tries to create a new room there
if that fails then the door is removed from the open list. if it succeeds then it is removed from the open list and added to the closed list.
this continues until the number of rooms has been reached or the open list is empty (check for bounds to)
The door locations are in the closed list (x,y) and can be drawn to the map.


Import mojo

Const screenwidth:Int=640
Const screenheight:Int=480
Global tilewidth:Int=6
Global tileheight:Int=4
Global mapwidth:Int=100
Global mapheight:Int=100
Const isnothing:Int=0
Const iswall:Int=1
Const isfloor:Int=2
Const isdoor:Int=3
Const minroomw:Int=5
Const minroomh:Int=5
Const maxroomw:Int=10
Const maxroomh:Int=10

Global map:Int[mapwidth][]

Global rcount:Int=0

Class debug
    Field x:Int
    Field y:Int
    Method New(_x:Int,_y:Int)
        x=_x
        y=_y
    End Method
End Class
Class dooropenlist
    Field x:Int
    Field y:Int
    Method New(_x:Int,_y:Int)
        x=_x
        y=_y
    End Method
End Class
Class doorclosedlist
    Field x:Int
    Field y:Int
    Method New(_x:Int,_y:Int)
        x=_x
        y=_y
    End Method
End Class

Global dol:List<dooropenlist> = New List<dooropenlist>
Global dcl:List<doorclosedlist> = New List<doorclosedlist>
Global d:List<debug> = New List<debug>

Class MyGame Extends App

    Method OnCreate()
        SetUpdateRate(1)
        Seed = Millisecs()
        For Local i = 0 Until mapwidth
            map[i] = New Int[mapheight]
        Next
        createmap(25,mapwidth/2,mapheight/2)
    End Method
    Method OnUpdate()  
        createmap(Rnd(10,125),mapwidth/2,mapheight/2)
    End Method
    Method OnRender()
        Cls 0,0,0
        SetColor 255,255,255
        drawmap
        
    End Method
End Class


Function createmap:Bool(numrooms:Int,sx:Int,sy:Int)
    For Local y=0 Until mapheight
    For Local x=0 Until mapwidth
        map[x][y] = isnothing
    Next
    Next
    d.Clear
    dol.Clear
    dcl.Clear
    dol.AddLast(New dooropenlist(sx,sy))
    Local roomcount:Int=0
    Local tx:Int
    Local ty:Int
    While roomcount<numrooms And dol.IsEmpty() = False
        Local founddoor:Bool=False
        While founddoor=False
            For Local i:=Eachin dol
                If Rnd(100)<2
                    founddoor = True
                    tx = i.x
                    ty = i.y
                    Exit
                End If
            Next
        Wend
        If makeroomondoor(tx,ty) = True Then 
            roomcount+=1
            removedoorfromopenlist(tx,ty)
            dcl.AddLast(New doorclosedlist(tx,ty))
        Else
            removedoorfromopenlist(tx,ty)
        End If
    Wend
    For Local i:=Eachin dcl
        If i.x = sx And i.y = sy Then dcl.Remove i
    Next
    rcount = roomcount
End Function


Function makeroomondoor:Bool(x:Int,y:Int)
    Local makeroom:Bool=False
    Local cnt:Int=0
    Local x1:Int
    Local y1:Int
    Local w1:Int
    Local h1:Int
    Local facing:String
    If x+maxroomw > mapwidth Then Return False
    If y+maxroomh > mapheight Then Return False
    If x-maxroomw < 0 Then Return False
    If y-maxroomh < 0 Then Return False    
    If map[x+1][y]=isnothing Then facing = "right"
    If map[x][y-1]=isnothing Then facing = "up"
    If map[x][y+1]=isnothing Then facing = "down"
    If map[x-1][y]=isnothing Then facing = "left"
    If facing="" Then Return False
    While cnt<100
        w1 = Rnd(minroomw,maxroomw)
        h1 = Rnd(minroomh,maxroomh) 
        Select facing
            Case "left"
                x1=x-w1
                y1=y-Rnd(h1/2)
            Case "right"
                x1=x+1
                y1=y-Rnd(h1/2)
            Case "up"
                x1=x-Rnd(w1/2)
                y1=y-h1
            Case "down"
                x1=x-Rnd(w1/2)
                y1=y+1
        End Select
        If spaceisempty(x1,y1,w1,h1) = True Then
            For Local y2=0 Until h1
            For Local x2=0 Until w1
                map[x2+x1][y2+y1] = isfloor
                If y2 = 0 Or x2 = 0 Or y2 = h1-1 Or x2 = w1-1 Then map[x2+x1][y2+y1] = 1  ' wall
            Next
            Next    
            ' shift map
            Select facing
                Case "left"        
                    For Local y2=0 Until h1
                    For Local x2=w1 Until 0 Step -1
                        map[x2+x1][y2+y1] = map[x2+x1-1][y2+y1]
                    Next
                    Next
                    For Local y2=0 Until h1
                        map[x1][y2+y1] = isnothing
                    Next
                    'make doors
                    makedoors(x1,y1,w1,h1,True,False,True,True)
                Case "right"        
                    For Local y2=0 Until h1
                    For Local x2=0 Until w1
                        map[x2+x1-1][y2+y1] = map[x2+x1][y2+y1]
                    Next
                    Next
                    For Local y2=0 Until h1
                        map[x1+w1-1][y2+y1] = isnothing
                    Next
                    'make doors
                    makedoors(x1-1,y1,w1,h1,False,True,True,True)                        
                Case "up"        
                    For Local y2=h1 Until 0 Step -1
                    For Local x2=0 Until w1
                        map[x2+x1][y2+y1] = map[x2+x1][y2+y1-1]
                    Next
                    Next
                    For Local x2=0 Until w1
                        map[x1+x2][y1] = isnothing
                    Next
                    'make doors
                    makedoors(x1,y1+1,w1,h1,True,True,True,False)
                Case "down"        
                    For Local y2=0 Until h1
                    For Local x2=0 Until w1
                        map[x2+x1][y2+y1-1] = map[x2+x1][y2+y1]
                    Next
                    Next
                    For Local x2=0 Until w1
                        map[x1+x2][y1+h1-1] = isnothing
                    Next
                    'make doors
                    makedoors(x1-1,y1-1,w1+1,h1,True,True,False,True)                        
            End Select            
            Return True                
        End If
        cnt+=1
    Wend
    Return False
End Function

Function makedoors:Void(x:Int,y:Int,w:Int,h:Int,l:Bool,r:Bool,u:Bool,d:Bool)
    Local dx:Int
    Local dy:Int
    If l=True Then 'left side
        dx = x+1
        dy = y+Rnd(h-4)+2
        dol.AddLast(New dooropenlist(dx,dy))
    End If
    If r=True Then 'right side
        dx = x+w-1
        dy = y+Rnd(h-4)+2
        dol.AddLast(New dooropenlist(dx,dy))
    End If
    If u=True Then 'up side
        dx = x+Rnd(w-4)+2
        dy = y
        dol.AddLast(New dooropenlist(dx,dy))
       End If
    If d=True Then ' down side
        dx = x+Rnd(w-4)+2
        dy = y+h-1
        dol.AddLast(New dooropenlist(dx,dy))
    End If
End Function

Function spaceisempty:Bool(x:Int,y:Int,w:Int,h:Int)
    For Local y1=0 Until h
    For Local x1=0 Until w
        If map[x1+x][y1+y] <> isnothing Then Return False
    Next
    Next
    Return True
End Function

Function makeroom(x:Int,y:Int,w:Int,h:Int)
    For Local y1=0 Until h
    For Local x1=0 Until w
        map[x1+x][y1+y] = 2  ' floor
        If y1 = 0 Or x1 = 0 Or y1 = h-1 Or x1 = w-1 Then map[x1+x][y1+y] = 1  ' wall
    Next
    Next
End Function

Function drawmap:Bool()
    For Local y=0 Until mapheight
    For Local x=0 Until mapwidth
        Select map[x][y]
            Case isnothing ; 
            Case iswall ; SetColor 150,150,150 ' wall
            Case isfloor ; SetColor 50,50,50 ' floor
            Case isdoor ; SetColor 200,200,0 ' door
        End Select
        If map[x][y]<>isnothing Then DrawRect x*tilewidth,y*tileheight,tilewidth,tileheight
    Next
    Next
    SetColor 255,255,0
    For Local i:=Eachin dcl
        DrawRect i.x*tilewidth,i.y*tileheight,tilewidth,tileheight
    Next
    SetColor 255,255,255
    DrawText "Number of rooms :"+rcount,0,0
    #rem
    SetColor 255,0,0
    For Local i:=Eachin d
        DrawRect i.x*tilewidth,i.y*tileheight,tilewidth,tileheight
    Next
    #End
End Function

Function removedoorfromopenlist:Void(x:Int,y:Int)
    For Local i:=Eachin dol
        If i.x = x And i.y = y Then 
            dol.Remove i 
            d.AddLast(New debug(x,y))
            Return
        End If
    Next
End Function

Function Main()
    New MyGame()
End Function

Sunday, January 25, 2015

Monkey-X - 1 to 5 room 2d Dungeon generator - code example





This is code for a 2d dungeon generator. It makes maps with 1 to 5 rooms connected with doors. It is the first time I tried to make it so the code might be somewhat long for what it does.
Code below :

Import mojo

Const screenwidth:Int=640
Const screenheight:Int=480
Global tilewidth:Int=16
Global tileheight:Int=16
Global mapwidth:Int=40
Global mapheight:Int=30
Const isnothing:Int=0
Const iswall:Int=1
Const isfloor:Int=2
Const isdoor:Int=3
Const minroomw:Int=5
Const minroomh:Int=5
Const maxroomw:Int=10
Const maxroomh:Int=10

Global map:Int[mapwidth][]

Global rcount:Int=0

Class MyGame Extends App

    Method OnCreate()
        SetUpdateRate(60)
        Seed = Millisecs()
        For Local i = 0 Until mapwidth
            map[i] = New Int[mapheight]
        Next
        createmap(Rnd(1,6))
    End Method
    Method OnUpdate()        
        rcount+=1
        Local exitloop=False
           If KeyHit(KEY_SPACE) Or rcount > 140 Then 
               createmap(Rnd(1,6))
               rcount=0
           End If
    End Method
    Method OnRender()
        Cls 0,0,0
        SetColor 255,255,255
        DrawText "Generates dungeons with 1 to 5 rooms",0,0
        drawmap
        
    End Method
End Class


Function createmap:Bool(numrooms:Int)
    Local succes:Bool=False
    Seed = Millisecs()
    If numrooms<1 Then numrooms=1
    If numrooms>5 Then numrooms=4
    While succes = False
        For Local y=0 Until mapheight
        For Local x=0 Until mapwidth
            map[x][y] = isnothing
        Next
        Next
        Local startx:Int=mapwidth/2-5
        Local starty:Int=mapheight/2-5
        Local roomw:Int=Rnd(minroomw,maxroomw)
        Local roomh:Int=Rnd(minroomh,maxroomh)
        makeroom(startx,starty,roomw,roomh)
        If numrooms = 1 Then Return True
        Local roomcount:Int=1
        Local l1:Bool=False
        Local r1:Bool=False
        Local u1:Bool=False
        Local d1:Bool=False
        While roomcount < numrooms
            If Rnd(10)<2 And roomcount<numrooms And r1=False Then makedoor("right",startx,starty,roomw,roomh) ; roomcount+=1 ; r1=True
            If Rnd(10)<2 And roomcount<numrooms And l1=False Then makedoor("left",startx,starty,roomw,roomh) ; roomcount+=1;l1=True
            If Rnd(10)<2 And roomcount<numrooms And u1=False Then makedoor("up",startx,starty,roomw,roomh) ; roomcount+=1 ; u1=True
            If Rnd(10)<2 And roomcount<numrooms And d1=False Then makedoor("down",startx,starty,roomw,roomh) ; roomcount+=1;d1=True
        Wend

        Local doorfound=False
        Local x1:Int
        Local y1:Int
        roomcount=1
        Local cnt:Int=0
        While roomcount<numrooms        
            x1=Rnd(mapwidth)
            y1=Rnd(mapheight)
            If map[x1][y1] = isdoor Then 
                If makeroomondoor(x1,y1) = True Then roomcount+=1
            End If
            cnt+=1
            If cnt>1000 Then Exit
        Wend
    If cnt>1000 Then succes=False Else succes = true
Wend
End Function


Function makeroomondoor:Bool(x:Int,y:Int)
    Local makeroom:Bool=False
    Local cnt:Int=0
    Local x1:Int
    Local y1:Int
    Local w1:Int
    Local h1:Int
    Local facing:String
    If map[x-1][y]=isnothing Then facing = "left"
    If map[x+1][y]=isnothing Then facing = "right"
    If map[x][y-1]=isnothing Then facing = "up"
    If map[x][y+1]=isnothing Then facing = "down"
    While cnt<100
        w1 = Rnd(minroomw,maxroomw)
        h1 = Rnd(minroomh,maxroomh)
        x1=-1
        Select facing
            Case "left"
                x1=x-w1
                y1=y-Rnd(h1/2)
            Case "right"
                x1=x+1
                y1=y-Rnd(h1/2)
            Case "up"
                x1=x-Rnd(w1/2)
                y1=y-h1
            Case "down"
                x1=x-Rnd(w1/2)
                y1=y+1
        End Select
        If x1<>-1
            If spaceisempty(x1,y1,w1,h1) = True Then
                For Local y2=0 Until h1
                For Local x2=0 Until w1
                    map[x2+x1][y2+y1] = isfloor
                    If y2 = 0 Or x2 = 0 Or y2 = h1-1 Or x2 = w1-1 Then map[x2+x1][y2+y1] = 1  ' wall
                Next
                Next
                
            ' shift map
                Select facing
                    Case "left"        
                        For Local y2=0 Until h1
                        For Local x2=w1 Until 0 Step -1
                            If map[x2+x1][y2+y1] <> isdoor
                                map[x2+x1][y2+y1] = map[x2+x1-1][y2+y1]
                            End If                            
                        Next
                        Next
                        For Local y2=0 Until h1
                            map[x1][y2+y1] = isnothing
                        Next
                    Case "right"        
                        For Local y2=0 Until h1
                        For Local x2=0 Until w1
                            If map[x2+x1-1][y2+y1] <> isdoor
                                map[x2+x1-1][y2+y1] = map[x2+x1][y2+y1]
                            End If
                        Next
                        Next
                        For Local y2=0 Until h1
                            map[x1+w1-1][y2+y1] = isnothing
                        Next
                    Case "up"        
                        For Local y2=h1 Until 0 Step -1
                        For Local x2=0 Until w1
                            If map[x2+x1][y2+y1] <> isdoor
                                map[x2+x1][y2+y1] = map[x2+x1][y2+y1-1]
                            End If
                        Next
                        Next
                        For Local x2=0 Until w1
                            map[x1+x2][y1] = isnothing
                        Next
                    Case "down"        
                        For Local y2=0 Until h1
                        For Local x2=0 Until w1
                            If map[x2+x1][y2+y1-1] <> isdoor
                                map[x2+x1][y2+y1-1] = map[x2+x1][y2+y1]
                            End If
                        Next
                        Next
                        For Local x2=0 Until w1
                            map[x1+x2][y1+h1-1] = isnothing
                        Next
                End Select            
                Return True                
            End If            
        End If
        cnt+=1
    Wend
    Return False
End Function

Function spaceisempty:Bool(x:Int,y:Int,w:Int,h:Int)
    For Local y1=0 Until h
    For Local x1=0 Until w
        If map[x1+x][y1+y] <> isnothing Then Return False
    Next
    Next
    Return True
End Function

Function makedoor(side:String,x:Int,y:Int,w:Int,h:Int)
    Local x1:Int
    Local y1:Int
    Select side
        Case "left"
            x1=x
            y1=y+Rnd(h-6)+3    
        Case "right"
            x1=x+w-1
            y1=y+Rnd(h-6)+3        
        Case "up"
            x1=x+Rnd(w-6)+3
            y1=y
        Case "down"
            x1=x+Rnd(w-6)+3
            y1=y+h-1
    End Select
    map[x1][y1] = isdoor
End Function

Function makeroom(x:Int,y:Int,w:Int,h:Int)
    For Local y1=0 Until h
    For Local x1=0 Until w
        map[x1+x][y1+y] = 2  ' floor
        If y1 = 0 Or x1 = 0 Or y1 = h-1 Or x1 = w-1 Then map[x1+x][y1+y] = 1  ' wall
    Next
    Next
End Function

Function drawmap:Bool()
    For Local y=0 Until mapheight
    For Local x=0 Until mapwidth
        Select map[x][y]
            Case isnothing ; 
            Case iswall ; SetColor 150,150,150 ' wall
            Case isfloor ; SetColor 50,50,50 ' floor
            Case isdoor ; SetColor 200,200,0 ' door
        End Select
        If map[x][y]<>isnothing Then DrawRect x*tilewidth,y*tileheight,tilewidth,tileheight
    Next
    Next
End Function

Function Main()
    New MyGame()
End Function

Saturday, January 24, 2015

Monkey-X - RTS grouped units out of the way pushing - code example


It took me more then 6 hours to get this example working. It still has a small bug sometimes that I have not been able to find yet. But for the most it works good.
Here in this example you can move a red unit with the mouse through a huge group of units and they will move out of the way. Handy for rts games and such.

It works like this: When moving into a direction it checks if the destination is taken. If so then this unit is moved into a random position. When this is not possible then the next location in that direction is handled this way. The units in that line are pushed into that direction until a free position is found.


Code below :

Import mojo

Const screenwidth:Int=640
Const screenheight:Int=480
Const tilewidth:Int=16
Const tileheight:Int=16

Class player
    Field x:Int
    Field y:Int
    Field dx:Int
    Field dy:Int
    Field ox:Int
    Field oy:Int
    Field w:Int=16
    Field h:Int=16
    Method New(_x:Int,_y:Int)
        x=_x
        y=_y
        dx=x
        dy=y
    End Method
    Method update()
        Local x1:Int=MouseX()/tilewidth
        Local y1:Int=MouseY()/tileheight
        Local moved:Bool=False
        If MouseHit(MOUSE_LEFT)
            If rectsoverlap(x1,y1,1,1,x-1,y-1,3,3) = True Then moved=True
        End If
        If moved=True
            moveunit(x1,y1)
            dx = x1
            dy = y1
        End If
        If x<> dx Or y<>dy
            If x<dx Then ox+=1
            If x>dx Then ox-=1
            If y<dy Then oy+=1
            If y>dy Then oy-=1
            If ox=16 Then x+=1 ; dx=x ; ox=0
            If oy=16 Then y+=1 ; dy=y ; oy=0
            If ox=-16 Then x-=1 ; dx=x ; ox = 0
            If oy=-16 Then y-=1 ; dy=y ; oy = 0            
        End If
    End Method
    Method moveunit(_x,_y)
        Local occupied:Bool=False
        For Local i:=Eachin u
            If i.x = _x And i.y=_y Then occupied = True
        Next
        If occupied = False Then
            dx=_x
            dy=_y
            Else
            dx=_x
            dy=_y
            moveunitoutoftheway(_x,_y)
        End If
    End Method
    Method moveunitoutoftheway(x1:Int,y1:Int)
        Local xdir:Int = x1-x
        Local ydir:Int = y1-y
        Local eloop:Bool=False
        Local mx:Int=0
        Local my:Int=0
        While eloop=False
            eloop=True
            For Local i:=Eachin u
                If i.x = x1+mx And i.y=y1+my
                    If moveintofreepos(i.x,i.y) = False Then
                        i.dx+=xdir
                        i.dy+=ydir
                        eloop=False
                    Else
                        Return True
                    End If
                End If
            Next
            mx+=xdir
            my+=ydir
        Wend
    End Method
    Method moveintofreepos:Bool(x1:Int,y1:Int)
        Local eloop:Bool=False
        Local mx:Int
        Local my:Int
        Local cnt:Int=0
        For Local i:=Eachin u
        If i.x = x1 And i.y=y1
        While eloop = False
            eloop=True
            mx=Rnd(-2,2)
            my=Rnd(-2,2)
            If x1+mx = p.x And y1+my = p.y Then eloop = False
            If x1+mx = p.dx And y1+my = p.dy Then eloop = False
            If x1+mx = x1 And y1+my = y1 Then eloop = False
            For Local ii:=Eachin u
                If ii.x = x1+mx And ii.y = y1+my Then eloop = False
                If ii.dx = x1+mx And ii.dy = y1+my Then eloop = False
            Next
            cnt+=1
            If cnt>100 Then Return False
        Wend
        i.dx = x1+mx
        i.dy = y1+my
        End If
        Next
        Return True
    End Method
    Method draw()
        SetColor 255,0,0
        DrawOval x*tilewidth+ox,y*tileheight+oy,tilewidth,tileheight
    End Method
End Class

Class unit
    Field x:Int
    Field y:Int
    Field w:Int=16
    Field h:Int=16
    Field dx:Float
    Field dy:Float
    Field ox:Int
    Field oy:Int
    Field moved:Bool=False
    Field hasmoved:Bool=False
    Method New(_x:Int,_y:Int)
        x=_x
        y=_y
        dx=x
        dy=y
    End Method
    Method update()
        If x<>dx Or y<>dy
            If x<dx Then ox+=1
            If x>dx Then ox-=1
            If y<dy Then oy+=1
            If y>dy Then oy-=1
            If ox=16 Then x=dx ; ox=0
            If oy=16 Then y=dy ; oy=0
            If ox=-16 Then x=dx ; ox = 0
            If oy=-16 Then y=dy ; oy = 0
        End If
        If hasmoved = True And x=dx And y=dy Then hasmoved=False
    End Method
    Method draw()
        SetColor 255,255,0
        DrawOval (x*tilewidth)+ox,(y*tileheight)+oy,tilewidth,tileheight
    End Method
End Class

Global u:List<unit> = New List<unit>
Global p:player = New player(10,10)

Class MyGame Extends App

    Method OnCreate()
        SetUpdateRate(60)
        Local x1:Int=20
        Local y1:Int=15
        Local x3:Int
        Local y3:Int
        For Local i=0 Until 300
            Local taken:Bool=True
            While taken=True
            Local x2:Int=(Rnd(-10,10))
            Local y2:Int=(Rnd(-10,10))
            x3=x1+x2
            y3=y1+y2
            taken=False
            For Local ii:=Eachin u
                If ii.x = x3 And ii.y = y3 Then taken = True ; Exit
            Next
            Wend
            u.AddLast(New unit(x3,y3))
        Next
    End Method
    Method OnUpdate()        
        p.update
        For Local i:=Eachin u
            i.update
        Next
    End Method
    Method OnRender()
        Cls 0,0,0 
        SetColor 255,255,255
        DrawText "Press the mouse near the red oval to move it into that direction.",0,0
        DrawText "The yellow ovals move out of the way.",0,16
        For Local i:=Eachin u
            i.draw
        Next
        p.draw
    End Method
End Class

Function distance:Int(x1:Int,y1:Int,x2:Int,y2:Int)
    Return Abs(x2-x1) + Abs(y2-y1)
End Function

Function getangle:Int(x1:Int,y1:Int,x2:Int,y2:Int)
    Return ATan2(y1-y2, x1-x2)
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    

Function Main()
    New MyGame()
End Function

Friday, January 23, 2015

Monkey-X - simple 2d top down space asteroid mining ai - code example


#monkey-X #gamedev
Here a small example of how ai flies to asteroids to mine them. They fly back to their base planet and continue to mine until all asteroids are gone. The map resets to a random new map at random intervals. In this example there can be up to 50 ai mining ships per map.
Code below :

Import mojo

Const screenwidth:Int=640
Const screenheight:Int=480
Global numships:Int=45
Global minerals:Int=0
Global resettime:Int=Millisecs()+15000

Class ship
    Field x:Float
    Field y:Float
    Field angle:Int
    Field destangle:Int
    Field incx:Float
    Field incy:Float
    Field homex:Int
    Field homey:Int
    Field destx:Int
    Field desty:Int
    Field state:String="evaluate"
    Field laststate:String
    Field cargo:Int=0
    Field minetime:Int
    Method New(_x:Float,_y:Float,_angle:Int)
        x = _x
        y = _y
        homex = x
        homey = y
        angle = _angle
    End Method    
    Method update()
        Select state
            Case "evaluate" ; eval
            Case "findasteroidtomine" ; findasteroid
            Case "flytotarget" ; flytot
            Case "mineasteroid" ; mineasteroid
        End Select
    End Method
    Method mineasteroid()
        If minetime < Millisecs() Then
            For Local i:=Eachin a
                If i.x = destx And i.y = desty
                    i.s -=1
                    If i.s < 1 Then i.delete = True
                    Exit
                End If
            Next
            cargo = 1
            laststate=state
            destx = homex
            desty = homey
            state="flytotarget"
        End If
    End Method
    Method flytot()
        destangle = getangle(x,y,destx,desty)
        If leftangle(angle,destangle) = True Then angle-=5 Else angle+=5
        If angle<-180 Then angle=180
        If angle>180 Then angle = -180
        incx = Cos(angle)
        incy = Sin(angle)
        x+=incx
        y+=incy
        If distance(x,y,destx,desty) < 5 Then 
            If laststate = "findasteroidtomine" Then
                laststate = state ; state = "mineasteroid"
                minetime = Millisecs()+2000
            End If
            If laststate = "mineasteroid"
                cargo = 0
                minerals+=1
                laststate = state ; state = "evaluate"
            End If
        End If
    End Method
    Method findasteroid()
        Local sd:Int = 100000
        Local afound:Bool=False
        Local taken:Bool=False
        For Local i:=Eachin a
            Local d:Int = distance(i.x,i.y,x,y) 
            If d<sd Then
                Local taken = False
                For Local ii:=Eachin sh
                    If ii.destx = i.x And ii.desty=i.y
                        taken = True
                    End If
                Next
                If taken=False
                    sd=d
                    destx=i.x
                    desty=i.y
                    afound=True
                End If
            End If
        Next        
        If afound = True Then
            laststate= state ; state="flytotarget"
            'Print "found asteroid to mine"+Millisecs()
        Else
            'Print "Nothing state ship"+Millisecs
            state="evaluate"
            
        End If
    End Method
    Method eval()
        If Rnd(200)<2 And cargo = 0 Then laststate=state ; state="findasteroidtomine"
    End Method
    Method draw()
        SetColor 255,255,0
        DrawPoint x,y
    End Method
End Class

Class star
    Field x:Int
    Field y:Int
    Method New(_x:Int,_y:Int)
        x=_x
        y=_y
    End Method
    Method draw()
        SetColor 255,255,255
        DrawPoint x,y
    End Method
End Class

Class asteroid
    Field x:Float
    Field y:Float
    Field s:Float
    Field rotangle:Float
    Field rotangles:Float
    Field delete:Bool=False
    Method New(_x:Float,_y:Float,_s:Float)
        x = _x
        y = _y
        s = _s
        rotangle = 0.0
        rotangles = Rnd(-1,1)
    End Method
    Method update()
        rotangle += rotangles
        If rotangle <-180 Then rotangle = 180
        If rotangle >180 Then rotangle = -180
        For Local i:=Eachin a
            If i.delete = True Then a.Remove i
        Next
    End Method
    Method draw()
        PushMatrix()
        Translate x,y
        Rotate(-rotangle)
        Translate -x,-y
        SetColor 100,100,100
        DrawRect x-s/2,y-s/2,s,s
        PopMatrix()
    End Method
End Class

Class planet
    Field x:Float
    Field y:Float
    Field s:Float
    Field t:Int ' planet type
    Method New(_x:Float,_y:Float,_s:Float,_t:Int)
        x = _x
        y = _y
        s = _s
        t = _t
    End Method
    Method update()
    End Method
    Method draw()
        Select t
            Case 1 'earthlike
                SetColor 0,200,0
            Case 2 'gas
                SetColor 255,0,0
        End Select
        DrawOval x-s/2,y-s/2,s,s        
    End Method
End Class

Global p:List<planet> = New List<planet>
Global a:List<asteroid> = New List<asteroid>
Global s:List<star> = New List<star>
Global sh:List<ship> = New List<ship>

Class MyGame Extends App

    Method OnCreate()
        SetUpdateRate(60)
        createmap
    End Method
    Method OnUpdate()        
        If Rnd(1600)<2 And resettime<Millisecs() Then createmap ; resettime=Millisecs()+15000
        For Local i:=Eachin p 'update planets
            i.update
        Next
        For Local i:=Eachin a 'update asteroids
            i.update
        Next
        For Local i:=Eachin sh
            i.update
        Next
    End Method
    Method OnRender()
        Cls 0,0,0 
        SetColor 255,255,255
        DrawText "Minerals Mined : " + minerals,0,0
        For Local i:=Eachin s 'draw stars
            i.draw
        Next
        For Local i:=Eachin p 'draw planets
            i.draw
        Next
        For Local i:=Eachin a 'draw asteroids
            i.draw
        Next
        For Local i:=Eachin sh 'draw ships
            i.draw
        Next
    End Method
End Class


Function createmap()        
        a.Clear()
        sh.Clear()
        s.Clear()
        p.Clear()
        minerals = 0
        numships = Rnd(10,300)
        Seed = Millisecs()
        'Make planets
        For Local i=0 Until 3
            Local x1 = Rnd(25,screenwidth-50)
            Local y1 = Rnd(25,screenheight-50)
            p.AddLast(New planet(x1,y1,Rnd(5,10),2))
        Next
           Local x1 = 320
           Local y1 = 240
        p.AddLast(New planet(x1,y1,Rnd(5,25),1))
        ' Make asteroids
        Local numfields:Int=Int(Rnd(3,6))
        For Local i=0 Until numfields
            Local x1 = Rnd(50,screenwidth-50)
            Local y1 = Rnd(50,screenheight-50)
            Local numa:Int=Rnd(3,45)
            For Local ii=0 Until numa
                Local x2 = Rnd(-50,50)
                Local y2 = Rnd(-50,50)
                a.AddLast(New asteroid(x1+x2,y1+y2,Rnd(2,8)))
            Next
        Next
        'Make stars
        For Local i=0 Until 100
            s.AddLast(New star(Rnd(screenwidth),Rnd(screenheight)))
        Next
        'Make ships
        For Local i=0 Until numships
            Local v:Int=Rnd(360)
            Local x1:Float
            Local y1:Float
            x1=320+(Cos(v)*32)+Rnd(-2,2)
            y1=240+(Sin(v)*32)+Rnd(-2,2)
            sh.AddLast(New ship(x1,y1,Rnd(-180,180)))
        Next
End Function
Function distance:Int(x1:Int,y1:Int,x2:Int,y2:Int)
    Return Abs(x2-x1) + Abs(y2-y1)
End Function

Function getangle:Int(x1:Int,y1:Int,x2:Int,y2:Int)
    Return ATan2(y1-y2, x1-x2)
End Function

Function leftangle:Bool(_angle:Int,_destinationangle:Int)
    Local cnt1 = 0    
    Local a1 = _angle
    While a1<>_destinationangle    
        a1+=1
        If a1>180 Then a1=-180
        cnt1+=1
    Wend
    If cnt1<180 Then Return True Else Return False
End Function

Function Main()
    New MyGame()
End Function

Monkey-X - Cybernoid 2, 3 screens remade - code example


Here 3 screens playable of the game Cybernoid 2. Most of the things are remade. There is no collision so you can not die. (use cursors to control ship)

Code below :

Import mojo

Const screenwidth:Int=640
Const screenheight:Int=480
Const tilewidth:Int=32
Const tileheight:Int=32
Const mapwidth:Int=16
Const mapheight:Int=10
Global currentscreen:Int=0

'1 = block
'2 = circle turn around ship thingy
Global screen1:Int[][] =  [ [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
                            [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
                            [1,1,0,0,0,0,0,0,1,1,0,0,0,0,1,1],
                            [1,1,0,0,0,0,0,0,1,1,0,0,0,0,1,1],
                            [1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1],
                            [1,1,0,1,0,1,0,0,0,0,0,0,0,0,1,1],
                            [1,1,0,1,0,1,0,0,0,0,0,0,0,0,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,0,0,0,1,1,1,1,1,1],
                            [1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1]]
            
Global screen2:Int[][] = [  [1,1,1,1,1,1,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,0,0,0,0,0,0,0,0,0,0],
                            [0,0,0,1,1,1,0,0,0,0,1,1,1,1,1,1],
                            [0,0,0,1,1,1,1,1,0,0,1,1,1,1,1,1],
                            [0,0,0,1,1,1,1,1,0,0,1,1,1,1,1,1],
                            [0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1],
                            [0,0,0,0,2,0,0,0,0,0,0,0,1,1,1,1],
                            [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
                            [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]]
                            
Global screen3:Int[][] = [  [1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1],
                            [1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1],
                            [1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0],
                            [1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0],
                            [1,1,1,1,1,1,1,0,0,1,1,1,0,0,0,0],
                            [1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0],
                            [1,1,1,1,1,0,0,1,1,1,1,1,0,0,0,0],
                            [1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
                            [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
                            [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]]

Class effect
    Field x:Int
    Field y:Int
    Field w:Float
    Field h:Float
    Field delete:Bool=False
    Method New(_x:Int,_y:Int)
        x=_x
        y=_y
        w=10
        h=10
    End Method
    Method update()
        w-=.2
        h-=.2
        If w<0 Or h<0 Then delete = True
        For Local i:=Eachin e
            If i.delete = True Then e.Remove i
        Next
    End Method
    Method draw()
        SetColor 255,255,0
        DrawOval x+w/2,y+h/2,w,h
    End Method
End Class

Class powerup
    Field x:Int
    Field y:Int
    Field w:Int=32
    Field h:Int=32
    Field delete:Bool=False
    Method New(_x:Int,_y:Int)
        x=_x
        y=_y
    End Method
    Method update()
        If rectsoverlap(p.x,p.y,p.w,p.h,x,y,w,h) = True
            delete = True
            p.cweapon=True
        End If
        For Local i:=Eachin pu
            If i.delete=True Then pu.Remove i
        Next
    End Method
    Method draw()
        SetColor 255,255,0
        DrawOval x,y,w,h
        SetColor 255,255,255
        DrawText "W",x+w/2,y+h/2,0.5,0.5
    End Method
End Class

Class dualslider
    Field x1:Int
    Field y1:Int
    Field w:Int=32
    Field h:Int=32
    Field x2:Int
    Field y2:Int
    Field dir:String="right"
    Method New(_x1:Int,_y1:Int,_x2:Int,_y2:Int)
        x1 = _x1
        y1 = _y1
        x2 = _x2
        y2 = _y2
    End Method
    Method update()
        If dir="right"
            Local s:Bool=False
            If tc(x1+1,y1,w,h) = True Then s=True
            If tc(x2+1,y1,w,h) = True Then s=True
            If s = False
                x1+=1
                x2+=1
            Else
                dir="left"
            End If
        End If
        If dir="left"
            Local s:Bool=False
            If tc(x1-1,y1,w,h) = True Then s=True
            If tc(x2-1,y1,w,h) = True Then s=True
            If s = False
                x1-=1
                x2-=1
            Else
                dir="right"
            End If            
        End If
    End Method
    Method draw()
        SetColor 255,0,0
        DrawOval x1,y1,w,h
        DrawOval x2,y2,w,h
    End Method
End Class

Class laser
    Field x:Float
    Field y:Float
    Field w:Int=8
    Field h:Int=8
    Field incx:Float
    Field incy:Float
    Field delete:Bool=False
    Method New(_x:Float,_y:Float,_incx:Float,_incy:Float)
        x = _x
        y = _y
        incx = _incx
        incy = _incy
    End Method
    Method update()
        x+=incx
        y+=incy
        If tc(x,y,w,h) = True
            delete=True
        End If
        For Local i:=Eachin l
            If i.delete = True Then l.Remove i
        Next
    End Method
    Method draw()
        SetColor 255,255,0
        DrawOval x-w/2,y-h/2,w,h
    End Method
End Class

Class turret
    Field x:Int
    Field y:Int
    Field w:Int=64
    Field h:Int=64
    Field shottimer:Int
    Method New(_x,_y)
        x = _x
        y = _y
    End Method
    Method update()    
        If shottimer < Millisecs()
        If p.x < x
            Local a:Int=getangle(p.x+p.w/2,p.y+p.h/2,x,y+h/2)
            Local ix:Float=Cos(a)
            Local iy:Float=Sin(a)
            l.AddLast(New laser(x,y+h/2,ix,iy))
            shottimer = Millisecs() + 1000
        End If
        End If
    End Method
    Method draw()
        SetColor 255,0,0
        DrawRect x,y,w,h
    End Method
End Class
                          
Class player
    Field x:Float
    Field dir:String="right"
    Field y:Float
    Field w:Int=32
    Field h:Int=32
    Field shield:Int=100
    Field msl:Int=1
    Field msr:Int=1
    Field cweapon:Bool=False
    Field cweapona:Int=0
    Field cwd:Int=0
    Method New()
    End Method
    Method update()
        If cweapon = True 
            cwd+=1
            If cwd>10 Then
                e.AddLast(New effect(x+Cos(cweapona)*50,y+Sin(cweapona)*50))
                cwd=0
            End If
            If dir="left" Then cweapona-=5 Else cweapona+=5
            If cweapona < -180 Then cweapona = 180
            If cweapona > 180 Then cweapona = -180
            
        End If
        Select currentscreen
            Case 0
                If p.y+p.h > mapheight*tileheight+96 Then 
                    currentscreen = 1
                    t.AddLast(New turret(12*tilewidth,9*tileheight))
                    pu.AddLast(New powerup(6*tilewidth,10*tileheight))
                    p.y=96                    
                End If
            Case 1
                If p.x < 64 Then
                    currentscreen = 2
                    t.Clear()
                    l.Clear()
                    pu.Clear()
                    Local x1:Int=5*tilewidth
                    Local y1:Int=8*tileheight
                    ds.AddLast(New dualslider(x1,y1,x1+4*tilewidth,y1))
                    p.x = 32+mapwidth*tilewidth                    
                End If
            Case 2
                If p.y < 96 Then
                    currentscreen = 0
                    ds.Clear()
                    e.Clear()
                    p.cweapon=False                    
                    p.x = 6*tilewidth
                    p.y = 8*tileheight
                End If
        End Select
        If msl>0 Then msl-=1
        If msr>0 Then msr-=1
        'gravity for ship
        If p.y+p.h < mapheight*tileheight+97
        For Local i=0 Until 2
        If tc(p.x,p.y+1,p.w,p.h) = False
            p.y+=1
        End If
        Next
        End If
        If KeyDown(KEY_UP)
            If p.y-1 > 96
            For Local i=0 Until 4
            If tc(p.x,p.y-1,p.w,p.h) = False
                p.y-=1
            End If
            Next
            End If
        End If
        If KeyDown(KEY_LEFT)
            dir="left"
            If msl<3 Then msl+=2
            For Local i=0 Until msl
            If tc(p.x-1,p.y,p.w,p.h) = False
                p.x-=1
            End If
            Next            
        End If
        If KeyDown(KEY_RIGHT)
            dir="right"                         
            If p.x+p.w+1 < mapwidth*tilewidth+64
            If msr<3 Then msr+=2            
            For Local i=0 Until msr
            If tc(p.x+1,p.y,p.w,p.h) = False
                p.x+=1
            End If
            Next
            End If            
        End If
    End Method
    Method draw()
        SetColor 0,0,255
        DrawRect x,y,w,h
        If cweapon = True Then            
            DrawOval x+Cos(cweapona)*50,y+Sin(cweapona)*50,20,20
        End If
    End Method
End Class

Global p:player = New player
Global t:List<turret> = New List<turret>
Global l:List<laser> = New List<laser>
Global ds:List<dualslider> = New List<dualslider>
Global pu:List<powerup> = New List<powerup>
Global e:List<effect> = New List<effect>
                                                                                                                
Class MyGame Extends App

    Method OnCreate()
        SetUpdateRate(60)
        ' player start position screen 1
        p.x = 6*tilewidth 
        p.y = 8*tileheight
    End Method
    Method OnUpdate()                
           p.update  ' update player
           For Local i:=Eachin l 'update lasers
               i.update
           Next
           For Local i:=Eachin t 'update turrets
               i.update
           Next
        For Local i:=Eachin ds 'update dual sliders
            i.update
        Next
        For Local i:=Eachin pu 'update powerups
            i.update
        Next
        For Local i:=Eachin e ' update effects
            i.update
        Next
    End Method
    Method OnRender()
        Cls 0,0,0
        SetColor 100,100,100
        DrawRect 64,32,screenwidth-128,64
        SetColor 255,255,255
        drawboxedrect 64,32,screenwidth-1-128,64
        drawmap
        drawboxedrect 64,96,screenwidth-1-128,mapheight*tileheight
        For Local i:=Eachin t' draw turrets
            i.draw
        Next
        For Local i:=Eachin ds 'draw dual sliders
            i.draw
        Next
        For Local i:=Eachin l' 'draw lasers
            i.draw
        Next        
        For Local i:=Eachin pu 'draw powerups
            i.draw
        Next
        For Local i:=Eachin e 'draw effects
            i.draw
        Next
        p.draw 'draw player
    End Method
End Class

Function drawmap:Void()
    SetColor 255,255,255
    For Local y=0 Until mapheight
    For Local x=0 Until mapwidth
        Local t:Int=0
        Select currentscreen
            Case 0
                t = screen1[y][x]
            Case 1    
                t = screen2[y][x]
            Case 2
                t = screen3[y][x]
        End Select
        If t=1 Then
            DrawRect x*tilewidth+64,y*tileheight+96,tilewidth,tileheight
        End If
    Next
    Next
End Function

'coords collide with map solid blocks true/false
Function tc:Bool(x:Int,y:Int,w:Int,h:Int)
    Local cx = (x-64)/tilewidth
    Local cy = (y-96)/tileheight
    For Local y2=cy-1 Until cy+4
    For Local x2=cx-1 Until cx+4
        If x2>=0 And x2<mapwidth And y2>=0 And y2<mapheight
            Local t:Int=0
            Select currentscreen
                Case 0;t = screen1[y2][x2]
                Case 1;t = screen2[y2][x2]
                Case 2;t = screen3[y2][x2]
            End Select
            If t = 1
                Local x3 = (x2)*tilewidth
                Local y3 = (y2)*tileheight
                If rectsoverlap(x-64,y-96,w,h,x3,y3,tilewidth,tileheight) = True
                    Return True
                End If
            End If
        End If
    Next
    Next
    Return False
End Function

Function drawboxedrect:Void(x:Int,y:Int,w:Int,h:Int)
    DrawLine x,y,x+w,y
    DrawLine x,y,x,y+h
    DrawLine x,y+h,x+w,y+h
    DrawLine x+w,y,x+w,y+h
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

Function getangle:Int(x1:Int,y1:Int,x2:Int,y2:Int)
    Return ATan2(y1-y2, x1-x2)
End Function

Function Main()
    New MyGame()
End Function

Wednesday, January 21, 2015

Monkey-X - Simple 2d Beat em Up - code example


This is the first time I made something like this. You control a player with the cursor left and right. Space is hitting. 2 baddies move towards you and try to hit you. When you hit them 3 times they dissapear. When both baddies are gone then 2 new one appear. You can not die. The hitcount is shown on the characters.

Code below :

Import mojo

Const screenwidth:Int=640
Const screenheight:Int=480

Class baddie
    Field x:Float
    Field y:Float
    Field w:Int=32
    Field h:Int=48
    Field state:String="movein"
    Field laststate:String
    Field frame:String
    Field lastframe:String
    Field delay:Int
    Field fight:Bool
    Field hitcount:Int=3
    Field delete:Bool=False
    Field hashit:Bool
    Method update()
        Select state            
            Case "fight"
                If delay<Millisecs() And distance(x+w/2,y+h/2,p.x+p.w/2,p.y+p.h/2) < 40 Then state="verticalalign"
                If frame="right" And x>p.x Then frame="left"            
                If frame="left" And x<p.x Then frame="right"
                If delay< Millisecs() And distance(x+w/2,y+h/2,p.x+p.w/2,p.y+p.h/2) > 80 Then state = "movein" ; fight=False
                If delay < Millisecs() And (frame="hitright" Or frame="hitleft")                
                    frame=lastframe
                    hashit=False
                End If
                If delay < Millisecs() And (frame="left" Or frame="right")
                    If Rnd(100)<2
                        lastframe=frame
                        delay = Millisecs()+400
                        If frame="left" Then 
                            frame="hitleft" 
                            hashit = True
                            If rectsoverlap(x-w,y,w*2,h,p.x,p.y,p.w,p.h) = True Then p.hitcount-=1
                        Else
                            frame="hitright"                            
                            hashit=True
                            If rectsoverlap(x,y,w*2,h,p.x,p.y,p.w,p.h) = True Then p.hitcount-=1
                        End If
                    End If
                End If
            Case "verticalalign"                
                keepaidistance()        
                If x<p.x And distance(x+w/2,y+h/2,p.x+p.w/2,p.y+p.h/2) < 40 Then x-=1
                If x+w>p.x And distance(x+w/2,y+h/2,p.x+p.w/2,p.y+p.h/2) < 40 Then x+=1
                If y<p.y Then y+=1
                If y>p.y Then y-=1
                If y=p.y Then state="fight"
            Case "evaluate"
                Local otherfighting:Bool=False
                For Local i:=Eachin b
                    If i.fight=True Then otherfighting=True
                Next
                If otherfighting = False
                    fight=True
                    state="verticalalign"
                    Else
                    state="movein"
                End If
            Case "movein"
                keepaidistance()
                If distance(x+w/2,y+w/2,p.x+p.w/2,p.y+p.h/2) > 60
                    If x>p.x Then x-=1 ; frame="left"
                    If x<p.x Then x+=1 ; frame="right"
                Else
                    state="evaluate"
                End If
                If x=p.x Then state="evaluate"
        End Select
        For Local i:=Eachin b
            If i.delete = True Then b.Remove i
        Next
    End Method
    Method keepaidistance()
        For Local i:=Eachin b
            If x<>i.x And y<>i.y
                If distance(x+16,y+16,i.x+16,i.y+16) < 64 Then
                    If x<=i.x Then x-=1 Else x+=1
                    If y<=i.y Then y-=1 Else y+=1
                End If
            End If
        Next        
    End Method
    Method New(_x:Float,_y:Float)
        x=_x
        y=_y
    End Method
    Method draw()
        SetColor 255,255,255
        Select frame
            Case "right"
                DrawRect x,y,w,h
            Case "left"
                DrawRect x,y,w,h
            Case "hitright"
                DrawRect x,y,w,h
                DrawRect x,y+10,w*2,10
            Case "hitleft"
                DrawRect x,y,w,h
                DrawRect x-w,y+10,w,10
        End Select        
        DrawText "HC:"+hitcount,x+w/2,y+h/2,0.5,0.5
    End Method    
End Class

Class game
    Method update()
    End Method
End Class

Class player
    Field x:Float=100
    Field y:Float=240
    Field w:Int=32
    Field h:Int=48
    Field frame:String="right"
    Field lastframe:String
    Field delay:Int
    Field hitcount:Int=3
    Field hashit:Bool=False
    Method update()
        If frame="left" Or frame="right"
            If KeyDown(KEY_DOWN)
                y+=3
                If y>400 Then y=400
            End If
            If KeyDown(KEY_UP)
                y-=3
                If y<200 Then y=200
            End If
            If KeyDown(KEY_RIGHT)    
                frame="right"
                x+=3
                If x+w>screenwidth Then x=screenwidth-w                
            End If
            If KeyDown(KEY_LEFT)
                frame="left"
                x-=3
                If x<0 Then x=0
            End If
        End If
        If KeyDown(KEY_SPACE)    
            If frame="right" Or frame="left"
                hashit=False
                delay = Millisecs()+200
                If frame="right" Then            
                    frame = "hitright"
                    lastframe="right"
                End If
                If frame="left" Then
                    frame = "hitleft"
                    lastframe="left"
                End If
            End If
        End If
        Select frame
            Case "hitright"
                If hashit = False
                    For Local i:=Eachin b
                        If rectsoverlap(x,y,w*2,h,i.x,i.y,i.w,i.h) = True
                            i.hitcount -=1
                            If i.hitcount = 0 Then i.delete = True
                        End If
                    Next
                    hashit=True
                End If
                If delay < Millisecs()
                    frame=lastframe
                End If
            Case "hitleft"
                If hashit = False
                    For Local i:=Eachin b
                        If rectsoverlap(x-w,y,w*2,h,i.x,i.y,i.w,i.h) = true
                            i.hitcount -=1
                            If i.hitcount = 0 Then i.delete = True
                        End If
                    Next
                    hashit=True
                End If
                If delay < Millisecs()
                    frame=lastframe
                End If
        End Select
    End Method
    Method draw()
        SetColor 255,255,255
        Select frame
            Case "right"
                DrawRect x,y,w,h
            Case "left"
                DrawRect x,y,w,h
            Case "hitright"
                DrawRect x,y,w,h
                DrawRect x,y+10,w*2,10
            Case "hitleft"
                DrawRect x,y,w,h
                DrawRect x-w,y+10,w,10
        End Select
        DrawText "HC:"+hitcount,x+w/2,y+h/2,0.5,0.5
    End Method
End Class

Global g:game = New game
Global p:player = New player
Global b:List<baddie> = New List<baddie>

Class MyGame Extends App

    Method OnCreate()
        SetUpdateRate(60)
        b.AddLast(New baddie(screenwidth+32,200))
        b.AddLast(New baddie(screenwidth+32,350))
    End Method
    Method OnUpdate()
        If b.IsEmpty() = True Then
            b.AddLast(New baddie(screenwidth+32,200))
            b.AddLast(New baddie(screenwidth+32,350))
        End If
        For Local i:=Eachin b
            i.update
        Next
        p.update
    End Method
    Method OnRender()
        Cls 0,0,0 
        SetColor 255,255,255
        DrawText "Use cursor left/right to move, space to hit.",0,0
        For Local i:=Eachin b
            i.draw
        Next
        p.draw
    End Method
End Class

Function distance:Int(x1:Int,y1:Int,x2:Int,y2:Int)
    Return Abs(x2-x1) + Abs(y2-y1)
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

Monkey-X - 2d Platformer/tilemap jumping on ai - code example


Here a example of how to let the player jump on the ai to kill them. Use the cursor keys to ove the player, space to jump. When the player lands on a baddie he bounces back up again.

Code below :

Import mojo

Const screenwidth:Int=640
Const screenheight:Int=480
Const tilewidth:Int=32
Const tileheight:Int=32
Const mapwidth:Int=20
Const mapheight:Int=15
Const numbaddies:Int=10
Global map:Int[][] = [       [1,1,1,1,1,1,1,1,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,1],
                            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
                            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
                            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
                            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
                            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
                            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
                            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
                            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
                            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
                            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
                            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
                            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
                            [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] ]

Class baddie
    Field x:Float
    Field y:Float
    Field w:Int
    Field h:Float
    Field delete:Bool=False
    Field state:String = "wait"
    Method New(_x:Float,_y:Float,_w:Int,_h:Int)
        x=_x
        y=_y
        w=_w
        h=_h
    End Method
    Method update()
        Select state
            Case "sink"
                h-=1
                If h<=0 Then delete = true
            Case "moveright"
                For Local i=0 Until 1
                    If tc(x,y-h,w,h,1,0) = False Then x+=1 Else state="moveleft"
                Next
                If Rnd(100)<2 Then state="wait"
            Case "moveleft"
                For Local i=0 Until 1
                    If tc(x,y-h,w,h,-1,0) = False Then x-=1 Else state="moveright"
                Next
                If Rnd(100)<2 Then state="wait"
            Case "wait"
                If Rnd(100)<2
                    If Int(Rnd(1,3)) = 1
                        state="moveleft"
                    Else
                        state="moveright"
                    End If
                End If
        End Select
        For Local i:=Eachin b
            If i.delete = True Then b.Remove i
        Next
    End Method
    Method draw()
        SetColor 255,0,0
        DrawRect x,y-h,w,h
    End Method
End Class

Class player
    Field x:Float=3*tilewidth
    Field y:Float=3*tileheight
    Field w:Int=tilewidth
    Field h:Int=tileheight
    Field isjumping:Bool=False
    Field incy:Float=0
    Method update()
        If KeyDown(KEY_SPACE)
            If isjumping = False
                isjumping = True
                incy=-9
            End If
        End If
        If KeyDown(KEY_RIGHT)
            For Local i=0 Until 5
                If ptc(1,0) = False
                        x+=1
                Else
                    Exit
                End If 
            Next
        End If
        If KeyDown(KEY_LEFT)
            For Local i=0 Until 5
                If ptc(-1,0) = False
                    x-=1
                Else
                    Exit
                End If
            Next
        End If
        If isjumping = False
            If ptc(0,1) = False
                incy = 0
                isjumping = True
            End If
        End If
        If isjumping = True
            If incy>=0
                If incy<5 Then incy+=.3
                For Local i=0 Until incy
                    If ptc(0,1) = False    
                        y+=1
                    Else
                        incy = 0
                        isjumping = False
                        Exit
                    End If
                Next
            End If
            If incy<0
                incy+=.3
                For Local i=0 Until Abs(incy)
                    If ptc(0,-1) = False
                        y-=1
                    Else
                        incy=0
                        Exit
                    End If
                Next
            End If
        End If
        jumponbaddie
    End Method
    Method jumponbaddie()
        For Local i:=Eachin b
            If isjumping=True
            If incy>0
                If rectsoverlap(x,y,w,h,i.x,i.y-i.h,i.w,i.h) = True Then
                    i.state = "sink"
                    incy=-9
                End If
            End If
            End If
        Next
    End Method
    Method draw()
        SetColor 255,255,0
        DrawRect x,y,w,h
        SetColor 255,255,255
        DrawText "P",x+w/2,y+h/2,0.5,0.5
    End Method
End Class

Global p:player = New player
Global b:List<baddie> = New List<baddie>
    
Class MyGame Extends App

    Method OnCreate()
        SetUpdateRate(60)
        For Local i=0 Until numbaddies
            Local w:Int=Rnd(10,32)
            Local h:Int=Rnd(10,32)
            b.AddLast(New baddie(Rnd(100,screenwidth-64),mapheight*tileheight-32,w,h))
        Next
    End Method
    Method OnUpdate()       
        For Local i:=Eachin b
            i.update
        Next
        p.update 
    End Method
    Method OnRender()
        Cls 0,0,0 
        SetColor 255,255,255
        drawmap
        For Local i:=Eachin b
            i.draw
        Next
        p.draw
        SetColor 255,255,255
        DrawText "Use cursor left/right to move, space to jump.",0,0
        DrawText "Jump on ai to kill them.",0,16
    End Method
End Class

Function drawmap:Void()
    SetColor 255,255,255
    For Local y=0 Until mapheight
    For Local x=0 Until mapwidth
        If map[y][x] = 1
            DrawRect x*tilewidth,y*tileheight,tilewidth,tileheight
        End If
    Next
    Next
End Function

'tile collision
Function tc:Bool(x:Int,y:Int,w:Int,h:Int,offsetx:Int=0,offsety:Int=0)
    Local cx = (x+offsetx)/tilewidth
    Local cy = (y+offsety)/tileheight
    For Local y2=cy-1 Until cy+4
    For Local x2=cx-1 Until cx+4
        If x2>=0 And x2<mapwidth And y2>=0 And y2<mapheight
            If map[y2][x2] = 1
                Local x3 = (x2)*tilewidth
                Local y3 = y2*tileheight
                If rectsoverlap(x+offsetx,y+offsety,w,h,x3,y3,tilewidth,tileheight) = True
                    Return True
                End If
            End If
        End If
    Next
    Next
    Return False
End Function

'player tile collision
Function ptc:Bool(offsetx:Int=0,offsety:Int=0)
    Local cx = (p.x+offsetx)/tilewidth
    Local cy = (p.y+offsety)/tileheight
    For Local y2=cy-1 Until cy+4
    For Local x2=cx-1 Until cx+4
        If x2>=0 And x2<mapwidth And y2>=0 And y2<mapheight
            If map[y2][x2] = 1
                Local x3 = (x2)*tilewidth
                Local y3 = y2*tileheight
                If rectsoverlap(p.x+offsetx,p.y+offsety,p.w,p.h,x3,y3,tilewidth,tileheight) = True
                    Return True
                End If
            End If
        End If
    Next
    Next
    Return False
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


Function Main()
    New MyGame()
End Function

Tuesday, January 20, 2015

Monkey-X - Matrix/SetAlpha/Rotate/Translate Effect - code example


Here a short example of how to make effects with Monkey. In this example rectangles are shot up from the center of the screen, they rotate and then they fall down. The SetAlpha is used so they are transparent.

Code below :

Import mojo

Const screenwidth:Int=640
Const screenheight:Int=480

Class effect
    Field x:Float
    Field y:Float
    Field incx:Float
    Field incy:Float
    Field w:Int
    Field h:Int
    Field col:Int
    Field angle:Int=Rnd(-180,180)
    Field angleturn:Int
    Field delete:Bool = False
    Method New(_x:Float,_y:Float,_w:Int,_h:Int)
        x = _x
        y = _y
        w = _w
        h = _h
        col = Rnd(50,150)
        incx = Rnd(-2,2)
        incy = Rnd(-10,-5)
        angleturn = Rnd(-5,5)
    End Method
    Method update()
        angle+=angleturn
        If angle>180 Then angle=-180
        If angle<-180 Then angle=180
        x+=incx
        y+=incy
        incy+=.1
        If y>screenheight Then delete = True
        For Local i:=Eachin e
            If i.delete = True Then e.Remove i
        Next
    End Method
    Method draw()
        PushMatrix()
        Translate x,y
        Rotate(-angle)
        Translate -x,-y
        SetColor 10+col,0,0
        SetAlpha 0.5 'part transparent setting
        DrawRect x,y,w,h
        PopMatrix()
        SetAlpha 1 'restore transparent setting to none transparent
    End Method
End Class

Global e:List<effect> = New List<effect>

Class MyGame Extends App

    Method OnCreate()
        SetUpdateRate(60)
    End Method
    Method OnUpdate()        
        If Rnd(10) < 2 Then e.AddLast(New effect(320,240,Rnd(5,25),Rnd(5,25)))
        For Local i:=Eachin e
            i.update
        Next
    End Method
    Method OnRender()
        Cls 0,0,0 
        For Local i:=Eachin e
            i.draw
        Next
        SetColor 255,255,255
    End Method
End Class


Function Main()
    New MyGame()
End Function

Monkey-X - Basic space ship controls and scrolling (Matrix/Translate/Rotate) - code example


Here is begin part of a space game. There is a ship that can rotate and increase/decrease speed. The map moves and the spaceship stays in the center of the screen. The rotation of the ship is done using the Pushmatrix and popmatrix commands. 

Code below :

Import mojo

Const screenwidth:Int=640
Const screenheight:Int=480
Const numstars:Int=150


Class player
    Field x:Float=screenwidth/2
    Field y:Float=screenheight/2
    Field incx:Float=0
    Field incy:Float=0
    Field w:Int=16
    Field h:Int=16
    Field angle:Int=0
    Method draw()
        PushMatrix()
        Translate 320,240
        Rotate(-angle)
        DrawPoly([Float(0-8),0 ,8,0-8 ,8,8])
        DrawLine -10,0,8,0        
        PopMatrix()
        

    End Method    
    Method update()

        If KeyDown(KEY_UP)
            incx = incx+Cos(angle)/10
            incy = incy+Sin(angle)/10
        End If
        If KeyDown(KEY_DOWN)
            incx = incx-Cos(angle)/30
            incy = incy-Sin(angle)/30
        End If
        If incx>3 Then incx = 3
        If incy>3 Then incy = 3
        If incx<-3 Then incx = -3
        If incy<-3 Then incy = -3

        If KeyDown(KEY_LEFT)
            angle-=1
            If angle<-180 Then angle = 180
        End If
        If KeyDown(KEY_RIGHT)
            angle+=1
            If angle>180 Then angle=-180
        End If
        For Local i:=Eachin s
            i.x+=incx
            i.y+=incy
            If i.x>screenwidth Then i.x = 0 ; i.y=Rnd(screenheight)
            If i.y>screenheight Then i.y=0 ; i.x = Rnd(screenwidth)
            If i.x<0 Then i.x=screenwidth ; i.y=Rnd(screenheight)
            If i.y<0 Then i.y=screenheight ; i.x=Rnd(screenwidth)
        Next
    End Method
End Class

Class star
    Field x:Float
    Field y:Float
    Method New()
        x = Rnd(screenwidth)
        y = Rnd(screenheight)
    End Method
    Method draw()
        DrawPoint x,y
    End Method
End Class

Global s:List<star> = New List<star>
Global p:player = New player

Class MyGame Extends App

    Method OnCreate()
        SetUpdateRate(60)
        For Local i=0 Until numstars
            s.AddLast(New star())
        Next
    End Method
    Method OnUpdate()  
        p.update      
    End Method
    Method OnRender()
        Cls 0,0,0 
        SetColor 255,255,255
        DrawText "Use cursor keys to control the ship.",0,0
        For Local i:=Eachin s
            i.draw
        Next
        p.draw
    End Method
End Class


Function Main()
    New MyGame()
End Function

Monkey-X - Minimap and Map code example





Here a minimap example. Press the mouse in the minimap(right top) and the map will move to that part.

Code below :

Import mojo

Const screenwidth:Int=640
Const screenheight:Int=480
Const tilewidth:Int=16
Const tileheight:Int=16
Const mapwidth=100
Const mapheight=100
Global numland:Int = 0
Global percland:Float = 1.7
Global map:Int[mapwidth][]
Global mapx:Int=0
Global mapy:Int=0

Class minimap
    Field image:Image
    Field pixels:Int[100*100]
    Method makeminimap()
        For Local y=0 Until 100
        For Local x=0 Until 100
            'Local pc = y*mapheight+x
            Local val = map[x][y]
            Local val2:Int
             ' tile 1,2,3,4 is sea
            If val<5 Then val2 = argb(0,0,val*10+100)
               ' tile 5 6 7 8 is grasslands/trees
               If val>=5 And val <9 Then val2 = argb(0,val*15,0)
            'tiles 9 10 11 12 13 is mountains
            If val>=9 Then val2 = argb(val*15,val*4,0)
            drawr(x,y,1,1,val2)
        Next
        Next
        image.WritePixels(mm.pixels, 0, 0, 100, 100, 0)
    End Method
    Method drawr(x1,y1,w1,h1,col)
        For Local y2=y1 Until y1+h1
        For Local x2=x1 Until x1+w1
            Local pc = y2*100+x2
            If pc >= 0 And pc < 100*100
                pixels[pc] = col
            End If
        Next
        Next
    End Method 
    Method draw(_x:Int,_y:Int)
        DrawImage image,_x,_y
    End Method
End Class    

Global mm:minimap = New minimap


Class MyGame Extends App

    Method OnCreate()
        'make the map array
        For Local i = 0 Until mapwidth
            map[i] = New Int[mapheight]
        Next        
        makemap
        'create the minimap image
        mm.image = CreateImage(100, 100)        
        'draw the minimap image
        mm.makeminimap        
        SetUpdateRate(60)
    End Method
    Method OnUpdate()
        'here we move the map from the minimap
        If MouseDown(MOUSE_LEFT)
            If MouseX()>screenwidth-105
            If MouseY()>5
            If MouseX()<screenwidth-5
            If MouseY()<110
                mapx = 100-(screenwidth-MouseX())+5
                mapy = MouseY()-5
                mapx = mapx - (screenwidth/tilewidth)/2
                mapy = mapy - (screenheight/tileheight)/2
                'Print "mapx:"+mapx+"mapy:"+mapy
                If mapx<0 Then mapx=0
                If mapy<0 Then mapy=0
                If mapx+screenwidth/tilewidth>mapwidth Then mapx=mapwidth-screenwidth/tilewidth
                If mapy+screenheight/tileheight>mapheight Then mapy=mapheight-screenheight/tileheight
            End If
            End If
            End If
            End If
        End If
    End Method
    Method OnRender()
        Cls 0,0,0 
        SetColor 255,255,255
        drawmap
        SetColor 0,0,0
        DrawRect screenwidth-110,0,110,110
        SetColor 255,255,255
        'here we draw the minimap image
        mm.draw(screenwidth-105,5)
        'here we draw the white box in the minimap
        drawboxedrect(mapx+screenwidth-105,5+mapy,screenwidth/tilewidth,screenheight/tileheight)
        DrawText "Press the mouse in the minimap to move the big map",0,0
'        drawminimap
    End Method
End Class

Function makemap:Void()
    numland=0
    ' exit loop if conditions on land percentage is good
       While numland<(mapwidth*mapheight/percland)
        ' erase the old data
        For Local y=0 Until mapheight
           For Local x=0 Until mapwidth
               map[x][y] = 0
        Next
        Next
        'lowest hold the highest tile value
        Local lowest = 0
        ' while land height is below 13
        While lowest < 13
            Local x1 = Rnd(mapwidth)
               Local y1 = Rnd(mapheight)
               ' create a radius for draw oval
            Local radius = Rnd(3,6)
            ' loop and create oval
            For Local y2=-radius To radius
            For Local x2=-radius To radius
                If ((x2*x2)+(y2*y2)) <= radius*radius+radius*0.8
                    Local x3 = x1+x2
                    Local y3 = y1+y2
                    If x3>=0 And y3>=0 And x3<mapwidth And y3<mapheight
                        ' add current position with added older tile value 
                        map[x3][y3]=map[x3][y3]+1
                        ' if current value is higher then lowest loop value
                        ' then store it in the loop exit variable
                        If map[x3][y3] > lowest Then lowest = map[x3][y3]
                    End If
                End If
            Next
            Next
        Wend
        'Count the number of land tiles
        numland=0
        For Local y=0 Until mapheight
        For Local x=0 Until mapwidth
            ' if the value is above 4 then add landtile counter
            If map[x][y] >= 5 Then numland+=1
        Next
        Next
    Wend
End Function

Function drawmap:Void()
    For Local y=0 Until screenheight/tileheight
    For Local x=0 Until screenwidth/tilewidth
        Local val:Int=map[x+mapx][y+mapy]
        ' tile 1,2,3,4 is sea
        If val<5 Then SetColor 0,0,val*10+100
        ' tile 5 6 7 8 is grasslands/trees
        If val>=5 And val <9 Then SetColor 0,val*15,0
        'tiles 9 10 11 12 13 is mountains
        If val>=9 Then SetColor val*15,val*4,0
        ' draw the tile
        DrawRect x*tilewidth,y*tileheight,tilewidth,tileheight
    Next
    Next
End Function

Function argb:Int(r:Int, g:Int, b:Int ,alpha:Int=255)
        Return (alpha Shl 24) | (r Shl 16) | (g Shl 8) | b          
End Function

Function drawboxedrect:Void(x:Int,y:Int,w:Int,h:Int)
    DrawLine x,y,x+w,y
    DrawLine x,y,x,y+h
    DrawLine x,y+h,x+w,y+h
    DrawLine x+w,y,x+w,y+h
End Function

Function Main()
    New MyGame()
End Function

Monkey-X - Simple 2d top down shooter with simple evading/following ai - code example


I made a simple shooter. The ai follows/homes towards the player. on a random point he moves away and then when he if far enough he return into the follow state. The ai can not shoot. You can shoot. The ai can take 4 hits before they are removed from the list.

Code below :

Import mojo

Class _ai
    Field x:Float
    Field y:Float
    Field angle:Int
    Field speed:Float=2
    Field destinationangle:Int
    Field dist:Int
    Field state:String="follow"
    Field hitpoints:Int=4
    Method New(_x:Float,_y:Float,_angle:Int)
        x = _x
        y = _y
        angle=_angle
    End Method
    Method update()
        dist = distance(x,y,p.x,p.y)
        Select state
            Case "follow"
                x+=Cos(angle)*speed
                y+=Sin(angle)*speed                
                destinationangle = getangle(p.x,p.y,x,y)
                If dist>50 Then 'turn towards player
                    If leftangle(angle,destinationangle) = True Then angle+=3 Else angle-=3
                    If angle>180 Then angle=-180
                    If angle<-180 Then angle=180
                End If
                If Rnd(100) < 2 Then state="evade"
            Case "evade"
                destinationangle = getangle(x,y,p.x,p.y)
                If leftangle(angle,destinationangle) = True Then angle+=3 Else angle-=3
                If angle>180 Then angle=-180
                If angle<-180 Then angle=180                
                x+=Cos(angle)*speed
                y+=Sin(angle)*speed
                If dist > 250 Then state="follow"
        End Select            
    End Method
    Method draw()
        SetColor 255,0,0
        DrawOval x,y,10,10
    End Method
End Class

Class playerlaser
    Field x:Float
    Field y:Float
    Field angle:Int
    Field speed:Float=4
    Field delete:Bool=False
    Method New(_x:Float,_y:Float,_angle:Int)
        x = _x
        y = _y
        angle=_angle
    End Method
    Method update()
        x+=Cos(angle)*speed
        y+=Sin(angle)*speed
        If x<-100 Then delete=True
        If x>DeviceWidth()+100 Then delete = True
        If y<-100 Then delete = True
        If y>DeviceHeight+100 Then delete = True
        ' laser ai collision
        For Local i:=Eachin ai
            If rectsoverlap(i.x,i.y,10,10,x,y,10,10) = True
                delete = True
                i.hitpoints-=1
                If i.hitpoints=0 Then ai.Remove(i)
            End If
        Next
        '
        For Local i:=Eachin pl
            If i.delete = True Then pl.Remove(i)
        Next
    End Method
    Method draw()
        SetColor 255,255,0
        DrawOval x,y,3,3
    End Method
End Class

Class player
    Field angle:Int
    Field x:Float = 100
    Field y:Float = 100
    Field mx:Float = 2 'movement speed x
    Field my:Float = 2 'movement speed y    
    Method update()
        ' player controls
        If KeyHit(KEY_SPACE)
            pl.AddLast(New playerlaser(x,y,angle))
        End If
        x+=Cos(angle)*mx
        y+=Sin(angle)*my
        Local destinationangle = getangle(MouseX(),MouseY(),x,y)
        If leftangle(angle,destinationangle) = True Then angle+=3 Else angle-=3
        If angle>180 Then angle=-180
        If angle<-180 Then angle=180
    End Method
    Method draw()
        SetColor 255,255,255
        DrawOval x,y,10,10
    End Method
End Class

Global p:player = New player
Global pl:List<playerlaser> = New List<playerlaser>
Global ai:List<_ai> = New List<_ai>

Class MyGame Extends App

    Method OnCreate()
        SetUpdateRate(60)
        ai.AddLast(New _ai(-100,100,0))
        ai.AddLast(New _ai(-100,200,0))
        ai.AddLast(New _ai(-100,300,0))
        ai.AddLast(New _ai(-100,400,0))
    End Method
    Method OnUpdate()        
        p.update
        For Local i:=Eachin pl
            i.update
        Next
        For Local i:=Eachin ai
            i.update
        Next
    End Method
    Method OnRender()
        Cls 0,0,0 
        SetColor 255,255,255
        For Local i:=Eachin ai
            i.draw
        Next
        For Local i:=Eachin pl
            i.draw
        Next
        p.draw
        SetColor 255,255,255
        DrawText "Player (white) moves towards mouse, space = shoot",0,0
    End Method
End Class

Function distance:Int(x1:Int,y1:Int,x2:Int,y2:Int)
    Return Abs(x2-x1) + Abs(y2-y1)
End Function

Function getangle:Int(x1:Int,y1:Int,x2:Int,y2:Int)
    Return ATan2(y1-y2, x1-x2)
End Function

Function leftangle:Bool(_angle:Int,_destinationangle:Int)
    Local cnt1 = 0    
    Local a1 = _angle
    While a1<>_destinationangle    
        a1+=1
        If a1>180 Then a1=-180
        cnt1+=1
    Wend
    If cnt1<180 Then Return True Else Return False
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

Monkey-X beginners- Moving/turning towards mouse cos/sin/atan2 - code example


Here is code that shows how to make a player move towards the mouse. Not directly but it will slowly turn towards the destination angle. It checks which direction is closest. This method is nice for homing missiles and such and space ship thrust movement.

I was able to shorten the code for the turning. Now I only have one loop to count up to the closest turn.

I hope to create a space shooter with some smart ai and turrets this morning and post it online. Hope I can get it working.

Code below :

Import mojo

Class player
    Field angle:Int
    Field x:Float = 100
    Field y:Float = 100
    Field mx:Float = 2 'movement speed x
    Field my:Float = 2 'movement speed y    
    Method update()
        ' here we move the player with the current angle
        x+=Cos(angle)*mx
        y+=Sin(angle)*my
        'get the real angle between the mouse and player
        Local destinationangle = getangle(MouseX(),MouseY(),x,y)
        ' cnt1 is used to see which direction is closer towards the destination angle
        Local cnt1 = 0
        ' put the angle value in a1
        Local a1 = angle
        ' we exit the loop if the a1 value is the destination angle
        While a1<>destinationangle
            'increase the a1 value
            a1+=1
            ' valid angles range from -180 to 180
            If a1>180 Then a1=-180
            'increase the cnt1 counter with one
            cnt1+=1
        Wend
        ' if going left is shorter 
        If cnt1<180 Then angle+=3 Else angle-=3
        ' Keep the angle in the valid angle range
        If angle>180 Then angle=-180
        If angle<-180 Then angle=180
    End Method
    Method draw()
        SetColor 255,255,255
        DrawOval x,y,10,10
    End Method
End Class

Global p:player = New player

Class MyGame Extends App

    Method OnCreate()
        SetUpdateRate(60)
    End Method
    Method OnUpdate()        
        p.update
    End Method
    Method OnRender()
        Cls 0,0,0 
        SetColor 255,255,255
        p.draw
    End Method
End Class

Function getangle:Int(x1:Int,y1:Int,x2:Int,y2:Int)
    Return ATan2(y1-y2, x1-x2)
End Function

Function Main()
    New MyGame()
End Function

Monkey-X - Beginners - Moving a oval towards the mouse pointer Cos/Sin - code example


I still have problems with angles and cos and sin and atan2 and commands like that. Below here is a simple example of how to move a oval automatically towards te mouse.

Code below :

Import mojo

Global angle:Int=0
Global x1:Float=100
Global y1:Float=100

Class MyGame Extends App

    Method OnCreate()
        SetUpdateRate(60)
    End Method
    Method OnUpdate()        
        angle = getangle(MouseX(),MouseY(),x1,y1)
        x1+=Cos(angle)*1
        y1+=Sin(angle)*1
    End Method
    Method OnRender()
        Cls 0,0,0 
        SetColor 255,255,255
        DrawOval x1,y1,10,10
    End Method
End Class

Function getangle:Int(x1:Int,y1:Int,x2:Int,y2:Int)
    Return ATan2(y1-y2, x1-x2)
End Function

Function Main()
    New MyGame()
End Function

Monday, January 19, 2015

Monkey-X - 2d Sidescrolling platformer with ai and bumpblocks - code example


Here is a example where you have a sidescrolling map on which the player can move. There is simple ai and there are blocks that you can bump into. they dissapear. On the map in the code the 3 characters is the ai. The 2 character is the bump tiles. And the 1 characters is the solid blocks.

Code below :

Import mojo


' The most left and most right tiles do not get drawn.
Global map:Int[][] =     [   [1,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
                            [1,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
                            [1,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
                            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,2,1,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1],
                            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,3,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,0,0,0,0,0,0,0,0,1,2,2,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,2,2,2,1,0,0,0,0,0,0,0,1,1,1,1],
                            [1,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,1,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,3,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,3,0,3,0,0,0,0,0,1,1,1,1,1,1],
                            [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]]

Const mapwidth:Int=50
Const mapheight:Int=10
Const tilewidth:Int=32
Const tileheight:Int=32
' Level array x location (left of the screen)
Global mapx:Int = 0
' Scrolling offset
Global mapsx:Int = 0

Class baddie
    Field d:String = "left"
    Field x:Float
    Field y:Float
    Field w:Int=20
    Field h:Int=12
    Field isjumping:Bool=False
    Field incy:Float=0
    Method update()
        If isjumping = True
            If incy < 5 Then incy+=.3
            For Local i=0 Until incy
                If btc(x,y,0,1) = False
                    y+=1
                Else
                    isjumping=False
                    Exit                
                End If
            Next
        End If
        Select d
            Case "left"
                For Local i = 0 Until 1                    
                    If x-1<0 Then 
                        d = "right"
                        Exit
                    End If
                    If isjumping = False And btc(x,y,0,1) = False
                        incy = 0
                        isjumping = True
                    End If
                    If btc(x,y,-1,0) = True
                        d="right"
                        Exit
                    End If
                    x-=1
                Next
            Case "right"
                For Local i=0 Until 1
                    If x+w+1>mapwidth*tilewidth Then
                        d="left"
                        Exit
                    End If
                    If btc(x,y,1,0) = True
                        d="left"
                        Exit
                    End If
                    x+=1
                Next
        End Select
    End Method
    Method New(_x:Float,_y:Float)
        x=_x
        y=_y
    End Method
    Method draw()
        SetColor 255,0,0
        Local x1 = x-(mapx*tilewidth)+mapsx
        Local y1 = y+tileheight-h
        DrawRect x1,y1,w,h
    End Method
End Class

Class player
    Field x:Float=32*3
    Field y:Float=32*3
    Field w:Int=32
    Field h:Int=32
    Field incy:Float=0
    Field isjumping:Bool=False
    Method update()
        If KeyDown(KEY_SPACE)
            If isjumping = False
                isjumping = True
                incy=-9
            End If
        End If
        If KeyDown(KEY_RIGHT)
            For Local i=0 Until 5
                If ptc(1,0) = False
                        x+=1
                Else
                    Exit
                End If 
            Next
        End If
        If KeyDown(KEY_LEFT)
            For Local i=0 Until 5
                If ptc(-1,0) = False
                    x-=1
                Else
                    Exit
                End If
            Next
        End If
        If isjumping = False
            If ptc(0,1) = False
                incy = 0
                isjumping = True
            End If
        End If
        If isjumping = True
            If incy>=0
                If incy<5 Then incy+=.3
                For Local i=0 Until incy
                    If ptc(0,1) = False    
                        y+=1
                    Else
                        incy = 0
                        isjumping = False
                        Exit
                    End If
                Next
            End If
            If incy<0
                incy+=.3
                For Local i=0 Until Abs(incy)
                    If ptc(0,-1) = False
                        y-=1
                    Else
                        If psc(0,-1) = True
                        End If
                        incy=0
                        Exit
                    End If
                Next
            End If
        End If
    End Method
    Method draw()
        SetColor 255,255,0
        DrawRect x,y,w,h
        SetColor 255,255,255
        DrawText "P",x+w/2,y+h/2,0.5,0.5
    End Method
End Class

Global b:List<baddie> = New List<baddie>
Global p:player = New player

Class MyGame Extends App
    Method OnCreate()
        SetUpdateRate(60)
        For Local y=0 Until mapheight
        For Local x=0 Until mapwidth
            If map[y][x] = 3
                b.AddLast(New baddie(x*tilewidth,y*tileheight))
            End If
        Next
        Next
    End Method
    Method OnUpdate()        
        p.update
        For Local i:=Eachin b
            i.update
        Next
        alignmap
    End Method
    Method OnRender()
        Cls 0,0,0
        SetColor 255,255,255
        For Local y=0 Until 10
        For Local x=0 Until 21
                Local x1=x*tilewidth+mapsx-tilewidth
                Local y1 = y*tileheight
                Select map[y][x+mapx]
                    Case 1
                    SetColor 255,255,255
                    DrawRect x1,y1,tilewidth,tileheight
                    Case 2
                    SetColor 255,155,22
                    DrawRect x1,y1,tilewidth,tileheight
                    SetColor 255,255,255
                    DrawText "B",x1+tilewidth/2,y1+tileheight/2,0.5,0.5
                End Select
        Next
        Next
        For Local i:=Eachin b
            i.draw
        Next
        p.draw
        DrawText "Use cursor Left and Right and space to control player.",10,20
    End
End

Function alignmap:Bool()
        For Local i=0 Until 5
        If p.x > DeviceWidth / 2
            If mapx+20 < mapwidth-1
                mapsx-=1
                If mapsx < 0 Then 
                    mapsx = 31
                    mapx += 1
                Endif
                p.x-=1
            End If
        End If
        Next

        For Local i=0 Until 5
        If p.x < DeviceWidth / 2
            If mapx > 0
                mapsx+=1
                If mapsx > 32 Then 
                    mapsx = 0
                    mapx -= 1
                Endif
                p.x+=1
            End If
        End If
        Next
End Function


'player collide with special blocks true/false
Function psc:Bool(offsetx:Int=0,offsety:Int=0)
    Local cx = (p.x+offsetx)/tilewidth+mapx
    Local cy = (p.y+offsety)/tileheight
    For Local y2=cy-1 Until cy+4
    For Local x2=cx-1 Until cx+4
        If x2>=0 And x2<mapwidth And y2>=0 And y2<mapheight
            If map[y2][x2] = 2
                Local x3 = (x2-mapx)*tilewidth-32+mapsx
                Local y3 = y2*tileheight
                If rectsoverlap(p.x+offsetx,p.y+offsety,p.w,p.h,x3,y3,tilewidth,tileheight) = True
                    map[y2][x2] = 1
                    Return True
                End If
            End If
        End If
    Next
    Next
    Return False
End Function



'player collide with solid blocks true/false
Function ptc:Bool(offsetx:Int=0,offsety:Int=0)
    Local cx = (p.x+offsetx)/tilewidth+mapx
    Local cy = (p.y+offsety)/tileheight
    For Local y2=cy-1 Until cy+4
    For Local x2=cx-1 Until cx+4
        If x2>=0 And x2<mapwidth And y2>=0 And y2<mapheight
            If map[y2][x2] = 1 Or map[y2][x2] = 2
                Local x3 = (x2-mapx)*tilewidth-32+mapsx
                Local y3 = y2*tileheight
                If rectsoverlap(p.x+offsetx,p.y+offsety,p.w,p.h,x3,y3,tilewidth,tileheight) = True
                    Return True
                End If
            End If
        End If
    Next
    Next
    Return False
End Function

'baddie collide with solid blocks true/false
Function btc:Bool(x1:Int,y1:Int,offsetx:Int=0,offsety:Int=0)
    Local cx = (x1+offsetx)/tilewidth
    Local cy = (y1+offsety)/tileheight
    For Local y2=cy-1 Until cy+4
    For Local x2=cx-1 Until cx+4
        If x2>=0 And x2<mapwidth And y2>=0 And y2<mapheight
            If map[y2][x2] = 1 Or map[y2][x2] = 2
                Local x3 = (x2)*tilewidth-32
                Local y3 = y2*tileheight
                If rectsoverlap(x1+offsetx,y1+offsety+tileheight-12,20,12,x3,y3,tilewidth,tileheight) = True
                    Return True
                End If
            End If
        End If
    Next
    Next
    Return False
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

Function Main()
    New MyGame()
End