Monday, December 7, 2015

Monkey-X - RPG - Scrolling platformer - Mining.- Game Template


' version 7-12-2015.
' Template.
' Scrolling platformer with RPG features.
' Based on the scrolling platformer with vines
' template on my Monkey-X blog.
'
' The mapwidth variables can be set to large(200+) or 
' small(100*100) maps. There will be multiple mines
' placed and populated if there is enough space for them
' on the map. (This version runs fast enough on low
' end laptops with maps up to 320*240 - there currently
' is no LOD implemented.)
'
' Game :
' Travel down/explore the mine.
' Has mining eggs feature. Hunting for meat.
' Monsters lay eggs. Only one monster hatches from a egg
' if there are no other monsters nearby.
' Monsters/breeders do not attack.
' You can pick up the eggs. Kill the monsters for meat.
' You can place an egg on the ground and a monster could be
' hatched.
' Eggs can also spawn Deathwings and they attack/hurt you.
' You have health and if zero restarts the game.
' p - Place egg on the ground.
' t - Pick up egg from the ground.
' Space - Attack with spear.
' curs left/right/up - move and jump.
' 
' Thinking of adding automated machine gun nests that you 
' can place/move around and they kill the flying monsters.
'
' Mood of the game at the moment is : 
' Monsters live in an old abandoned mining area. You can 
' mine meat/eggs to make a profit. Monsters
' have been seen flying nearby mines attacking people.
'
'
Import mojo

Const mapwidth:Int=100
Const mapheight:Int=100
Const tilewidth:Int=32
Const tileheight:Int=32
Global mapx:Int=0
Global mapy:Int=0
Global mapsx:Int=0
Global mapsy:Int=0
Global maxdeathwings:Int=10

Class maptest
    Field tw:Int,th:Int
    Field w:Int,h:Int
    Field map:Int[][]
    Method New(w:Int,h:Int)
        Self.w = w
        Self.h = h
        tw = DeviceWidth()/w
        th = DeviceHeight()/h
        map = New Int[w][]
        For Local i=0 Until w
            map[i] = New Int[h]
        Next
        drawmaprect(0,0,w-1,15)
        For Local i=0 Until h
            map[1][i] = 0
            map[w-2][i] = 0
        Next
        Local x:Int=32
        While x<w-48
            makemine(x,15,Rnd(4,h/15))
            x+=Rnd(48,64)
        Wend
    End Method
    Method makemine(x:Int,y:Int,depth:Int)
        For Local mydepth=0 Until depth
        Local d1:Int=Rnd(8,16)'depth
        tunneldown(x,y,d1)
        y+=d1
        Local d2:Int=Rnd(1,4)'direction
        If d2=1 Then sidetunnel(x,y,"left")
           If d2=2 Then sidetunnel(x,y,"right")
           If d2=3 Then 
            sidetunnel(x,y,"left")
               sidetunnel(x,y,"right")
           End If
           Next
           For Local y1=0 Until y
               map[x][y1] = 2
           Next
    End Method
    Method sidetunnel(x:Int,y:Int,d:String)
        If d="left"
            Local width:Int=Rnd(5,10)
            drawmaprect(x-width+2,y,width,3)
            Local roomw:Int=Rnd(5,15)
            drawmaprect(x-width+2-roomw,y-1,roomw,5)
            For Local x1=0 Until roomw/3
                map[(x-width+2-roomw)+x1][y+4] = 3
                eggs.AddLast( New egg((x-width+2-roomw)+x1,y+4) )
            Next
'            a.AddLast(    New agent(    ((x-width+2-roomw))*tilewidth,
'                                    (y+4)*tileheight) )
        End If
        If d="right"
            Local width:Int=Rnd(5,10)
            drawmaprect(x-1,y,width,3)
            Local roomw:Int=Rnd(5,15)
            drawmaprect(x+width,y-1,roomw,5)        
            For Local x1=roomw Until roomw/1.5 Step -1
                map[(x+width)+x1][y+4] = 3
                eggs.AddLast( New egg((x+width)+x1,y+4) )
            Next            
'            a.AddLast( New agent(     (x+width+2)*tilewidth,
'                                     (y+4)*tileheight ))
        End If
    End Method
    Method tunneldown(x:Int,y:Int,d:Int)
        drawmaprect(x-2,y,4,d)
    End Method
    Method drawmaprect(x:Int,y:Int,w:Int,h:Int)
        For Local y1=y To y+h
        For Local x1=x To x+w
            map[x1][y1] = 1
        Next
        Next        
    End Method
    Method draw()
        For Local y=0 Until h
        For Local x=0 Until w
            If map[x][y] = 1
                SetColor 255,255,255
                DrawRect x*tw,y*th,tw,th
            End If
            If map[x][y] = 3
                SetColor 200,200,10
                DrawOval x*tw,y*th,tw,th
            End If            
        Next
        Next
    End Method
End Class

Class spear
    Field x:Float,y:Float
    Field w:Int=20
    Field h:Int=4
    Field d:String
    Field incx:Float
    Field active:Bool    
    Method New(x:Int,y:Int,d:String)
        Self.x = x
        Self.y = y
        Self.d = d
        incx = 3
        active = True
    End Method
    Method update()
        If active = False Then Return
        w+=incx
        incx-=0.5
        If     incx > 0
            killcreature
        End If
        If incx < -3 Then active = False
    End Method
    Method killcreature()
        For Local i:=Eachin dw
            If d="left" 
                If rectsoverlap(    i.x,i.y,32,32,
                                    x-32,y,32,32)
                    i.remove = True
                    myinven.meat+=1
                End If
            Else
                If rectsoverlap(    i.x,i.y,32,32,
                                    x+32,y,32,32)
                    i.remove = True
                    myinven.meat+=1
                End If
            End If
        Next

        For Local i:=Eachin a            
            If d="left" 
                If rectsoverlap(    i.x,i.y,32,32,
                                    x-32,y,32,32)
                    i.remove = True
                    myinven.meat+=1
                End If
            Else
                If rectsoverlap(    i.x,i.y,32,32,
                                    x+32,y,32,32)
                    i.remove = True
                    myinven.meat+=1
                End If
            End If
        Next
        For Local i:=Eachin a
            If i.remove=True
                a.Remove i
            End If
        Next
    End Method
    Method draw()
        If active = True
            SetColor 200,100,0
            If d = "right"
                DrawRect x+32,y+8,w,h
            Else
                DrawRect x-w,y+8,w,h    
            End If
        End If
    End Method
End Class

Class player    
    Field x:Float=0
    Field y:Float=0
    Field w:Int=32
    Field h:Int=32
    Field incy:Float=0
    Field isjumping:Bool = False
    Field facing:Int '0 = left , 1 = right
    Field jumpofvine:Bool=False
    Field jumpofvinetimeout:Int
    Field health:Int=10
    Method update()
        Local ox:Int=x
        Local oy:Int=y
        If pvc(0,0) = False Or jumpofvine = True Then regularmode
        If pvc(0,0) = True 
            If jumpofvine = False
                vinemode
            End If
        End If
        If KeyHit(KEY_SPACE)
            Local md:String
            If facing = 0 Then 
                md="left" 
            Else
                md="right"
            End If
            myspear = New spear(p.x,p.y,md)
        End If
        If KeyHit(KEY_T)
            Local offx:Int
            If p.facing=0 Then 
                offx=0
            Else
                offx=32
            End If
               Local cx = (p.x+offx)/tilewidth+mapx
            Local cy = (p.y)/tileheight+mapy
            If mymaptest.map[cx][cy] = 3
                mymaptest.map[cx][cy] = 1
                myinven.eggs+=1
                For Local i:=Eachin eggs
                    If i.x = cx And
                        i.y = cy
                        i.remove = True
                    End If
                Next
                For Local i:=Eachin eggs
                    If i.remove=True
                        eggs.Remove i
                    End If
                Next
            End If
        End If
        If KeyHit(KEY_P)
            If myinven.eggs > 0
               Local cx = (p.x)/tilewidth+mapx
            Local cy = (p.y)/tileheight+mapy
            If mymaptest.map[cx][cy] = 1
                mymaptest.map[cx][cy] = 3
                eggs.AddLast(New egg(cx,cy))
            End If            
            End If
            myinven.eggs-=1
        End If
    End Method
    Method movea(x1:Int,y1:Int)
        For Local i:=Eachin a
            i.x+=x1
            i.y+=y1
        Next
        For Local i:=Eachin dw
            i.x+=x1
            i.y+=y1
        Next        
    End Method
    Method vinemode()
        isjumping = False
        incy=0
        If KeyDown(KEY_J)
            jumpofvine = True
            jumpofvinetimeout = Millisecs() + 1000
            isjumping = True
            incy=-4
        End If
        If KeyDown(KEY_UP)
            For Local i=0 Until 4
                If pvc(0,0) = True And ptc(0,-1) = False
                    y-=1
                End If
            Next
        End If
        If KeyDown(KEY_DOWN)
            For Local i=0 Until 4
                If pvc(0,0) = True And ptc(0,1) = False
                    y+=1
                End If
            Next
        End If
        If KeyDown(KEY_LEFT)
            For Local i=0 Until 4
                If pvc(0,0) = True And ptc(-1,0) = False
                    x-=1
                    facing=0
                End If
            Next
        End If
        If KeyDown(KEY_RIGHT)
            For Local i=0 Until 4
                If pvc(0,0) = True And ptc(1,0) = False
                    x+=1
                    facing = 1
                End If
            Next
        End If
    End Method
    Method regularmode()        
        If jumpofvine = True
            If Millisecs() > jumpofvinetimeout Then jumpofvine=False
        End If
        'Left and Right movement
        If KeyDown(KEY_RIGHT)
            For Local i=0 Until 4 ' move with 4 pixels at a time
                If ptc(1,0) = False
                    x+=1
                    facing = 1
                End If
            Next
        End If
        If KeyDown(KEY_LEFT)
            For Local i=0 Until 4
                If ptc(-1,0) = False
                    x-=1
                    facing = 0
                End If
            Next
        End If
        'player gravity part
        'if in the air and not in jump
        If isjumping = False
            If ptc(0,1) = False
                isjumping=True
                incy=0
            End If
        End If
        ' jump
        If KeyDown(KEY_UP)
            If isjumping = False
                isjumping = True
                incy=-4
            End If
        End If
        ' if we are in a jump/falling down
        If isjumping=True
            If incy>=0 'if we are going down
                If incy<4 Then incy+=.1
                For Local i=0 Until(incy)
                    If ptc(0,1) = False
                        y+=1
                    Else
                        isjumping = False
                    End If
                Next
            End If
            If incy<0 'if we are going up
                incy+=.1
                For Local i=0 Until Abs(incy)
                    If ptc(0,-1) = False
                        y-=1
                    Else
                        incy=0
                    End If
                Next
            End If
        End If    
    End Method
    Method draw()
        SetColor 255,255,0
        DrawRect x,y,w,h
    End Method
End Class

Class deathwing
    Field x:Int,y:Int
    Field w:Int,h:Int
    Field state:String
    Field remove:Bool=False
    Field lasthurt:Int=Millisecs()
    
    Method New(x:Int,y:Int)
        Self.x = x
        Self.y = y
        w = 32
        h = 32
        state = "flyup"
    End Method
    Method update()    
        hurtplayer    
        Local x1:Int = ((Self.x-mapsx+16)/tilewidth)+mapx
        Local y1:Int = ((Self.y)/tileheight)+mapy
        canattack
        Select state
            Case "attack"
                If p.x < x Then x-=1
                If p.y < y Then y-=1
                If p.x > x Then x+=1
                If p.y > y Then y+=1
            Case "flyup"                
                ' if high enough
                If y1<12 Then 
                    If Rnd(10)<5                    
                        state="flyleft"                        
                        Else
                        state="flyright"
                    End If                
                End If
                'if can fly up
                If mymaptest.map[x1][y1-2] = 1
                    y-=1
                Else
                    If Rnd(10)<5
                        state="flyleft"
                        Else
                        state="flyright"
                    End If                
                End If                
            Case "flyleft"
                If mymaptest.map[x1][y1] = 1
                    x-=1
                Else
                    state="flyright"
                End If
                If Rnd(150)<5
                    If canflyup(x1+2,y1) = True
                        state="flyup"
                    End If
                Elseif Rnd(150)>140
                    If canflydown(x1+2,y1) = True
                        state="flydown"
                    End If
                End If
            Case "flyright"
                If mymaptest.map[x1+2][y1] = 1
                    x+=1
                Else                
                    state="flyleft"
                End If
                If Rnd(150)<5 Then
                    If canflyup(x1,y1) = True
                        state="flyup"
                    End If
                Elseif Rnd(150)>140
                    If canflydown(x1,y1) = True
                        state="flydown"
                    End If
                End If
            Case "flydown"
                If mymaptest.map[x1][y1+1] = 1
                    y+=1
                    Else
                    state="flyup"
                End If
        End Select
    End Method
    Method hurtplayer()
        If lasthurt<Millisecs()
            lasthurt = Millisecs()+600
            If rectsoverlap(p.x,p.y,32,32,x,y,32,32)
                p.health-=1
            End If
        End If
    End Method
    Method canattack()
        If state="attack"
        If distance(p.x,p.y,x,y) > 32
            If Rnd(10)<5 Then
                state="flyright"
            Else
                state="flyleft"
            End    If            
        End If
        End If
        If rectsoverlap(p.x-16,p.y-16,96,96,x-16,y-16,96,96)
            state="attack"
        End If
    End Method
    Method canflydown:Bool(x1:Int,y1:Int)
        For Local y2=y1 To y1+5
            If mymaptest.map[x1][y2] <> 1 Then
                Return False
            End If
        Next
        Return True        
    End Method
    Method canflyup:Bool(x1:Int,y1:Int)
        If y1<14 Then Return False
        For Local y2=y1 To y1-5 Step -1
            If mymaptest.map[x1][y2] <> 1 Then
                Return False
            End If
        Next
        Return True        
    End Method
    Method draw()
        SetColor 0,250,20
        DrawOval x,y,w,h
        SetColor 255,255,255
    End Method
End Class

Class agent
    Field x:Int,y:Int
    Field w:Int,h:Int
    Field state:String
    Field remove:Bool=False
    Method New(x:Int,y:Int)
        Self.x = x
        Self.y = y
        w = 32
        h = 32
        state="right"
    End Method
    Method update()
        Local x1:Int = ((Self.x-mapsx+16)/tilewidth)+mapx
        Local y1:Int = ((Self.y)/tileheight)+mapy
        Select state
            Case "right"
            If mymaptest.map[x1+2][y1] = 0 Or
                mymaptest.map[x1+2][y1+1] = 1 Then
                state="left"
                Else
                x+=1
            End If
            Case "left"
            If mymaptest.map[x1][y1] = 0 Or
                mymaptest.map[x1][y1+1] = 1 Then
                state="right"
                Else 
                x-=1
            End If
        End Select
        If Rnd(1500)<2
            Local placeegg:Bool=True
            For Local i:=Eachin eggs
                If i.x = x1 And i.y = y1
                    placeegg=False
                End If
            Next
            If placeegg = True
                If mymaptest.map[x1][y1] = 1
                mymaptest.map[x1][y1] = 3
                eggs.AddLast(New egg(x1,y1) )
                End If
            End If
        End If
    End Method
    Method draw()
        SetColor 255,0,0
        DrawRect x,y,w,h
        SetColor 255,255,255
    End Method
End Class

Class egg
    Field x:Int,y:Int
    Field hatch:Bool=False
    Field remove:Bool=False
    Method New(x:Int,y:Int)
        Self.x = x
        Self.y = y
    End Method
End Class

Class inventory
    Field eggs:Int=0
    Field meat:Int=0
    Method New()
    End Method
    Method draw()
        SetColor 0,0,0
        DrawRect     0,DeviceHeight()-64,
                    DeviceWidth(),64
        SetColor 255,255,255
        DrawText "Eggs :"+eggs,10,DeviceHeight()-40
        DrawText "Meat :"+meat,10,DeviceHeight()-20
        DrawText "Health :"+p.health,100,DeviceHeight()-40
        DrawText "This game template might be updated if I get inspired",DeviceWidth()/3,DeviceHeight()-60
        DrawText "or if there are lots of hits on it. So check this",DeviceWidth()/3,DeviceHeight()-40
        DrawText "link in the future.",DeviceWidth()/3,DeviceHeight()-20
    End Method
End Class

' -----------------------------------------------------------------------------------------------

Global myinven:inventory = New inventory
Global mymaptest:maptest
Global p:player
Global a:List<agent> = New List<agent>
Global dw:List<deathwing> = New List<deathwing>
Global eggs:List<egg> = New List<egg>
Global myspear:spear = New spear

Class MyGame Extends App

    Method OnCreate()
        SetUpdateRate(60)
        restartgame
    End Method
    Method OnUpdate()    
        If p.health < 0 Or
            KeyHit(KEY_F1)
            restartgame
        End If
        p.update    
        alignmap
        For Local i:=Eachin a
            i.update
        Next
        For Local i:=Eachin dw
            i.update
            If i.remove = True 
                dw.Remove i
            End If
        Next        
        egghatch()
        myspear.update
    End Method
    Method OnRender()
        Cls 0,0,0 
        drawmap
        For Local i:=Eachin a
            i.draw
        Next
        For Local i:=Eachin dw
            i.draw
        Next
        myspear.draw
        p.draw        
        SetColor 0,0,0
        DrawRect 0,0,DeviceWidth(),50
        SetColor 255,255,255
        myinven.draw
        DrawText "Use Cursor left/right to move, cursor up to jump. Travel down the mine and mine eggs/meat.",0,0
        DrawText "Space to attack. t to take egg. p to place egg. Monsters spawn from eggs.",0,16
        DrawText "Monsters lay eggs. M - Show map.",0,32
        If KeyDown(KEY_M) Then
            Cls 0,0,0
            mymaptest.draw
        End If
    End Method
End Class



Function Main()
    New MyGame()
End Function

Function egghatch()
    'hatch deathwing
    For Local z=0 Until 2                    
        Local ax:Int=Rnd(mapwidth)
        Local ay:Int=Rnd(mapheight)
        If mymaptest.map[ax][ay] = 3
        If countdeathwings()<maxdeathwings    
            dw.AddLast( New deathwing(    (ax*tilewidth)-
                                        (mapx*tilewidth),
                                        (ay*tileheight)-
                                        (mapy*tileheight)+
                                        (mapsy) ) )        
            For Local i:=Eachin eggs
                If i.x = ax
                If i.y = ay
                    i.remove = True
                End If
                End If
            Next
            mymaptest.map[ax][ay]  = 1
            For Local i:=Eachin eggs
                If i.remove = True
                    eggs.Remove i
                End If
            Next                
            Exit
        End If
        End If
    Next
    'hatch breeder
    For Local z=0 Until mapwidth/2
        Local ax:Int=Rnd(mapwidth)
        Local ay:Int=Rnd(mapheight)
        If mymaptest.map[ax][ay] = 3
            For Local i:=Eachin eggs
            If i.x = ax And i.y = ay
                i.hatch = True
                For Local ii:=Eachin a
                    Local x1:Int = ((ii.x-mapsx+16)/tilewidth)+mapx
                    Local y1:Int = ((ii.y)/tileheight)+mapy

                    If distance(i.x,i.y,x1,y1) < 15 
                        i.hatch = False            
                    End If
                Next
            End If
            Next            
        End If        
    Next
    For Local i:=Eachin eggs
        If i.hatch = True
            mymaptest.map[i.x][i.y] = 1
            If Rnd(10) > 3  'hatch monster
                a.AddLast( New     agent(     (i.x*tilewidth)-
                                        (mapx*tilewidth),
                                        (i.y*tileheight)-
                                        (mapy*tileheight)+
                                        (mapsy) ) )
            End If
            i.remove = True
            For Local ii:=Eachin eggs
            If i<>ii
                If distance(i.x,i.y,ii.x,ii.y) < 10
                    ii.hatch=False
                End If
            End If
            Next
        End If
    Next
    For Local i:=Eachin eggs
        If i.remove = True Then
            eggs.Remove i
        End If
    Next
End Function

Function countdeathwings:Int()
    Local cnt:Int=0
    For Local i:=Eachin dw
        cnt+=1
    Next
    Return cnt
End Function

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

        For Local i=0 Until 4
        If p.x < DeviceWidth / 2
            If mapx > 0
                mapsx+=1
                If mapsx > 31 Then 
                    mapsx = 0
                    mapx -= 1
                Endif
                p.x+=1
                p.movea(1,0)
            End If
        End If
        Next
        ' scrolling down
        For Local i=0 Until 16
        If p.y > DeviceHeight / 2
            If mapy+14 < mapheight-1
                mapsy-=1
                If mapsy < 0 Then 
                    mapsy = 31
                    mapy += 1
                Endif
                p.y-=1
                p.movea(0,-1)
            End If
        End If
        Next
        ' scrolling up
        For Local i=0 Until 16
        If p.y < DeviceHeight / 2
            If mapy > 0
                mapsy+=1
                If mapsy > 31 Then 
                    mapsy = 0
                    mapy -= 1
                Endif
                p.y+=1
                p.movea(0,1)
            End If
        End If
        Next
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+mapy
    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 mymaptest.map[x2][y2] = 0
                Local x3 = (x2-mapx)*tilewidth-32+mapsx
                Local y3 = (y2-mapy)*tileheight+mapsy
                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

'player collide with vines blocks true/false
Function pvc:Bool(offsetx:Int=0,offsety:Int=0)
    Local cx = (p.x+offsetx)/tilewidth+mapx
    Local cy = (p.y+offsety)/tileheight+mapy
    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 mymaptest.map[x2][y2] = 2
                Local x3 = (x2-mapx)*tilewidth-32+mapsx
                Local y3 = (y2-mapy)*tileheight+mapsy
                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 drawmap:Void()
    For Local y=0 To 14
    For Local x=0 To 20
        Local x1 = ((x*tilewidth)+mapsx)-tilewidth
        Local y1 = ((y*tileheight)+mapsy)
        Select mymaptest.map[x+mapx][y+mapy]
            Case 0'Wall
            SetColor 100,100,100
            DrawRect x1,y1,tilewidth,tileheight
            Case 2'vine
            SetColor 10,100,10
            DrawRect x1,y1,tilewidth,tileheight
            Case 3'coin
            SetColor 200,200,10
            DrawOval x1+4,y1+4,tilewidth-8,tileheight-4
          End Select
     Next
     Next
     
End Function

Function restartgame()
    a.Clear()
    dw.Clear()
    eggs.Clear()
    mapx=0
    mapy=0
    mapsx=0
    mapsy=0
    mymaptest = New maptest(mapwidth,mapheight)
    p = New player
    p.x+=64
    p.movea(64,0)

End Function

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

No comments:

Post a Comment