Play game
lcd-assault's itch.io pageResults
Criteria | Rank | Score* | Raw Score |
Winner | #2 | 3.167 | 3.167 |
Gameplay | #2 | 3.333 | 3.333 |
Graphics | #9 | 2.833 | 2.833 |
Sound | #12 | 1.667 | 1.667 |
Ranked from 6 ratings. Score is adjusted from raw score by the median number of ratings per game in the jam.
Source Code
Strict
Import mojo
#MOJO_IMAGE_FILTERING_ENABLED=False
#GLFW_WINDOW_TITLE="lcd-assault"
Class LCD
Field _imageRects:Int[]
Field _image:Image
Field _pageWidth:Int = 640
Field _ptrMap := New StringMap<Int>
Field _figureCount:Int = 0
Function Load:LCD(path:String)
Local lcd := New LCD
Local lcdStr:String = LoadString(path)
Local lines := lcdStr.Split("~n")
Local tempStack := New IntStack
Local currentPage:Int = 0
Local currentPtr:Int = 0
For Local l := Eachin lines
l = l.Trim()
If l.StartsWith("#") Then
Continue
Elseif l.StartsWith("IMAGE:") Then
lcd._image = LoadImage(l[6..])
Elseif l.StartsWith("PAGE_WIDTH:") Then
lcd._pageWidth = Int(l[11..])
Elseif l.StartsWith("PAGE:") Then
currentPage = Int(l[5..])
Elseif l.StartsWith("PTR:") Then
lcd.SetPtr(l[4..], currentPtr)
Else l.Contains(",")
Local tempRect := l.Split(",")
If tempRect.Length >= 4 Then
tempStack.Push(Int(tempRect[0].Trim())+currentPage*lcd._pageWidth)
tempStack.Push(Int(tempRect[1].Trim()))
tempStack.Push(Int(tempRect[2].Trim()))
tempStack.Push(Int(tempRect[3].Trim()))
Else
tempStack.Push(0)
tempStack.Push(0)
tempStack.Push(1)
tempStack.Push(1)
End
currentPtr += 1
End
End
lcd._imageRects = tempStack.ToArray()
lcd._figureCount = lcd._imageRects.Length/4
Return lcd
End
Method SetPtr:Void(name:String, value:Int)
_ptrMap.Set(name, value)
End
Method GetPtr:Int(name:String)
Return _ptrMap.Get(name)
End
Method DrawFigure:Void(id:Int)
If id < 0 Or id >= _figureCount Then Return
DrawImageRect(_image, _imageRects[id*4] Mod _pageWidth, _imageRects[id*4+1], _imageRects[id*4], _imageRects[id*4+1], _imageRects[id*4+2], _imageRects[id*4+3])
End
Method FigureX:Int(id:Int)
Return _imageRects[id*4] Mod _pageWidth
End
Method FigureY:Int(id:Int)
Return _imageRects[id*4+1]
End
Method FigureWidth:Int(id:Int)
Return _imageRects[id*4+2]
End
Method FigureHeight:Int(id:Int)
Return _imageRects[id*4+3]
End
Method FigureCenterX:Int(id:Int)
Return (_imageRects[id*4] Mod _pageWidth)+(_imageRects[id*4+1]/2)
End
Method FigureCenterY:Int(id:Int)
Return _imageRects[id*4+1]+(_imageRects[id*4+2]/2)
End
Method FigureCount:Int() Property
Return _figureCount
End
Method RenderBackground:Void()
SetColor(0, 255, 0)
SetColor(255, 255, 255)
DrawImageRect(_image, 0, 0, 0, 0, _pageWidth, _image.Height())
End
Method RenderShadow:Void()
SetAlpha(0.05)
Local pages:Int = _image.Width() / _pageWidth
For Local i := 0 Until pages
DrawImageRect(_image, 0, 0, _pageWidth*i, 0, _pageWidth, _image.Height())
End
SetAlpha(1.0)
End
End
Class MainApp Extends App
Field fnt:Font
Field lcd:LCD
Field BULLETS:Int
Field ENEMY_PARTS_SMALL:Int
Field ENEMY_PARTS_LARGE:Int
Field BOSS:Int
Field PLAYER:Int
Field BAR_SHIELD:Int
Field BAR_HEAT:Int
Field TXT_SHIELD:Int
Field TXT_HEAT:Int
Field TXT_POINTS:Int
Field TXT_SHIPS:Int
Field TXT_GAMEOVER:Int
Field BOSS_LP_BAR:Int
Field BOSS_LP_BORDER:Int
Field mode:Int
Enumerate
MODE_LOADING,
MODE_START,
MODE_GAME,
MODE_GAMEOVER
Field counter:Float = 0
Field updateSpeed:Float = DEFAULT_SPEED
Field movementCounter:Float = 0
Field movementSpeed:Float = DEFAULT_SPEED/40.0
Field soundLaser:Sound
Field soundExplosion:Sound
Field soundHurt:Sound
Const DEFAULT_SPEED:Float = 1.5
Method OnCreate:Int()
' get random seed:
Local date:=GetDate()
Seed=date[5]+date[4]*60+date[3]*60*60+date[2]*60*60*24+12345
SetChannelVolume(0, 0.25)
SetChannelVolume(1, 0.25)
SetChannelVolume(2, 0.25)
fnt = Font.Load("fnt.png", 48, 10, False) '40, 65, 10)
SetFont(fnt)
lcd = LCD.Load("lcd.txt")
BULLETS = lcd.GetPtr("bullets")
ENEMY_PARTS_SMALL = lcd.GetPtr("small")
ENEMY_PARTS_LARGE = lcd.GetPtr("large")
BOSS = lcd.GetPtr("boss")
PLAYER = lcd.GetPtr("player")
BAR_SHIELD = lcd.GetPtr("bar_shield")
BAR_HEAT = lcd.GetPtr("bar_heat")
TXT_SHIELD = lcd.GetPtr("txt_shield")
TXT_HEAT = lcd.GetPtr("txt_heat")
TXT_POINTS = lcd.GetPtr("txt_points")
TXT_SHIPS = lcd.GetPtr("txt_ships")
TXT_GAMEOVER = lcd.GetPtr("txt_gameover")
BOSS_LP_BAR = lcd.GetPtr("bar_boss")
BOSS_LP_BORDER = lcd.GetPtr("boss_lp_border")
soundLaser = LoadSound("laser.wav")
soundExplosion = LoadSound("explosion.wav")
soundHurt = LoadSound("hurt.wav")
SetMode(MODE_LOADING)
SetUpdateRate(60)
Return 0
End
Field loadingAni:Float = 0
Method OnLoading:Int()
'Cls(11, 216, 163)
'loadingAni += 1
'Print("Loading ... ("+loading+")")
DrawLoadingScreen()
Return 0
End
Method DrawLoadingScreen:Void()
SetColor(255, 243, 229)
DrawRect(-2, -2, 644, 484)
loadingAni += 2.0
Local f:Float
SetColor(0,0,0)
f = 0.5+0.5*Sin(loadingAni)
For Local i:Int = 0 Until 360 Step 72
DrawCircle(320+Sin(Float(i)+loadingAni)*30.0*f, 240+Cos(Float(i)+loadingAni)*30.0*f, 11)
End
SetColor(255,186,114)
f = 0.5+0.5*Sin(loadingAni)
For Local i:Int = 0 Until 360 Step 72
DrawCircle(320+Sin(Float(i)+loadingAni)*30.0*f, 240+Cos(Float(i)+loadingAni)*30.0*f, 10)
End
SetColor(255,255,255)
End
Method OnUpdate:Int()
#if TARGET="html5"
If KeyHit(KEY_ESCAPE) Then playerLP = 0 ; gameover = True ; gameOverWave = waveNumber ; PlaySound(soundExplosion, 2)'SetMode(MODE_START)
#Else
If KeyHit(KEY_ESCAPE) Then EndApp()
#End
If gameover Then movementSpeed = 0.15
Local tick := False
If Int(counter+updateSpeed) <> Int(counter) Then tick = True
Local movementTick := False
If Int(movementCounter+movementSpeed) <> Int(movementCounter) Then movementTick = True
counter += updateSpeed
movementCounter += movementSpeed
Select mode
Case MODE_START
If counter >= 50 Then
SetMode(MODE_GAME)
InitPlayer()
End
Case MODE_GAME
UpdatePlayer(tick)
UpdateEnemies(movementTick)
End
Return 0
End
Field waiting:Bool = True
Method OnRender:Int()
Cls()
SetColor(255, 255, 255)
DrawRect(0, 0, 640, 480)
' draw background image:
lcd.RenderBackground()
' draw shadows:
lcd.RenderShadow()
' draw shadows (points, ships):
SetAlpha(0.05)
DrawText(888, 24, 132)
DrawText(88, 524, 132)
SetAlpha(1.0)
Select mode
Case MODE_LOADING
SetMode(MODE_START)
Case MODE_START
updateSpeed = 1.5
movementSpeed = DEFAULT_SPEED/40.0
If counter < 24 Then
For Local f := 0 Until lcd.FigureCount
If lcd.FigureCenterY(f) < counter*16 Then lcd.DrawFigure(f)
End
Else
For Local f := 0 Until lcd.FigureCount
If lcd.FigureCenterY(f) > (counter-24)*16 Then lcd.DrawFigure(f)
End
End
Case MODE_GAME
If waiting Then gameover = True ; playerLP = 0
RenderPlayer()
RenderPlayerBullets()
RenderEnemies()
End
Return 0
End
Method SetMode:Void(m:Int)
mode = m
counter = 0.0
updateSpeed = DEFAULT_SPEED
End
Function Flicker:Bool()
If Millisecs() Mod 128 > 48 Then Return True
Return False
End
Function Blink:Bool()
If Millisecs() Mod 500 > 200 Then Return True
Return False
End
'### PLAYER ######################################################
Field playerPos:Int
Field playerLP:Int
Field playerHeat:Int
Field playerBullets := New Int[5*10]
Field points:Int
'Field ships:Int = 3
Field waveNumber:Int
Field gameover:Bool = False
Field gameoverCounter:Int
Field damage:Int
Field gameoverAni:Int
Field gameOverTime:Int
Field gameOverWave:Int
Method InitPlayer:Void()
playerPos = 2
playerLP = 16
playerHeat = 0
points = 0
waveNumber = 1
gameover = False
gameoverCounter = 0
damage = 0
updateSpeed = 1.5
movementSpeed = DEFAULT_SPEED/40.0
FirstWave()
' clear player bullets:
For Local i := 0 Until playerBullets.Length
playerBullets[i] = 0
End
End
Method RenderPlayer:Void()
DrawPoints()
If gameover Then
If Blink() And Not waiting Then lcd.DrawFigure(TXT_GAMEOVER)
gameoverAni += 1
For Local i:Int = 0 Until 16
If Abs(i-(Int(Float(gameoverAni)/4.0) Mod 20-2)) <= 1 Then
lcd.DrawFigure(BAR_HEAT+15-i)
lcd.DrawFigure(BAR_SHIELD+15-i)
End
End
Return
End
If Not damage Or Flicker() Then lcd.DrawFigure(PLAYER+playerPos)
' hear bar:
lcd.DrawFigure(TXT_HEAT)
For Local i := 0 Until playerHeat
'For Local i := playerHeat Until 16
lcd.DrawFigure(BAR_HEAT+i)
End
' lp bar
lcd.DrawFigure(TXT_SHIELD)
'For Local i := playerLP Until 16
For Local i := 0 Until playerLP
lcd.DrawFigure(BAR_SHIELD+i)
End
End
Method DrawPoints:Void()
' points and ships:
lcd.DrawFigure(TXT_POINTS)
lcd.DrawFigure(TXT_SHIPS)
Local visiblePoints := points
Local visibleWaveNumber := waveNumber
If gameover Then visibleWaveNumber = gameOverWave
If visiblePoints < 0 Then visiblePoints = 0
If visiblePoints > 999 Then visiblePoints = 999
If visibleWaveNumber < 0 Then visibleWaveNumber = 0
If visibleWaveNumber > 99 Then visibleWaveNumber = 99
If visiblePoints < 10 Then
DrawText(visiblePoints, 24+80, 132)
Elseif visiblePoints < 100 Then
DrawText(visiblePoints, 24+40, 132)
Else
DrawText(visiblePoints, 24, 132)
End
If visibleWaveNumber < 10 Then
DrawText(visibleWaveNumber, 524+40, 132)
Else
DrawText(visibleWaveNumber, 524, 132)
End
End
Method UpdatePlayer:Void(tick:Bool = False)
If gameover Then
If (Millisecs()-gameOverTime > 3000 Or waiting) And (KeyHit(KEY_SPACE) Or KeyHit(KEY_X) Or KeyHit(KEY_Y) Or KeyHit(KEY_Z) Or (waiting And (KeyHit(KEY_LEFT) Or KeyHit(KEY_RIGHT)))) Then SetMode(MODE_START) ; bossEnergy = 0 ; InitPlayer() ; waiting = False
UpdatePlayerBullets()
Return
End
If KeyHit(KEY_LEFT) Then
playerPos -= 1
If playerPos < 0 Then playerPos = 0
End
If KeyHit(KEY_RIGHT) Then
playerPos += 1
If playerPos > 4 Then playerPos = 4
End
If KeyHit(KEY_SPACE) Or KeyHit(KEY_X) Or KeyHit(KEY_Y) Or KeyHit(KEY_Z) Then
If playerBullets[playerPos+9*5] = 0 And playerHeat < 16-5 Then
playerBullets[playerPos+9*5] = 1
playerHeat += 5
PlaySound(soundLaser, 0)
End
End
If tick Then
If damage > 0 Then damage -= 1
UpdatePlayerBullets()
If playerHeat > 0 Then playerHeat -= 1 Else playerHeat = 0
End
End
Method PlayerBulletHitCheck:Void()
' player bullets <=> enemy ship:
For Local i:Int = 0 Until 5*10
If playerBullets[i] And enemyShips[i] Then
enemyShips[i] -= 1
If Not enemyShips[i] Then points += 1 ; PlaySound(soundExplosion, 1)
playerBullets[i] = 0
Elseif playerBullets[i] And asteroids[i] Then
asteroids[i] -= 1
If Not asteroids[i] Then points += 3 ; PlaySound(soundExplosion, 1)
playerBullets[i] = 0
End
End
End
Method UpdatePlayerBullets:Void()
PlayerBulletHitCheck()
' move bullets:
For Local y:Int = 0 Until 9
For Local x:Int = 0 Until 5
If y = 0
If playerBullets[x+y*5] And bossEnergy > 0 And bossDamageCounter <= 0 And bossPosition = x Then
bossEnergy -= 1
If bossEnergy <= 0 Then points += 10 ; PlaySound(soundExplosion, 1)
bossDamageCounter = 3
End
playerBullets[x+y*5] = 0
End
If playerBullets[x+(y+1)*5] <> 0 Then
playerBullets[x+y*5] = playerBullets[x+(y+1)*5]
playerBullets[x+(y+1)*5] = 0
End
End
End
PlayerBulletHitCheck()
End
Method RenderPlayerBullets:Void()
For Local i:Int = 0 Until 50
If playerBullets[i] Then lcd.DrawFigure(BULLETS+i)
End
End
Method DamagePlayer:Void()
If damage > 0 Or playerLP <= 0 Then Return
damage = 10
playerLP -= 1
If playerLP < 1 Then
gameover = True
gameOverWave = waveNumber
PlaySound(soundExplosion, 2)
'gameoverCounter = 10
gameOverTime = Millisecs()
Else
PlaySound(soundHurt, 2)
End
End
'### ENEMY #######################################################
Field wave:Int = WAVE_START
Enumerate
WAVE_START,
WAVE_RANDOM_SHIPS,
WAVE_FORMATION_1,
WAVE_BOSS,
WAVE_ASTEROIDS,
WAVE_FRONT_1,
NUMBER_OF_WAVE_TYPES
Field waveCounter:Int
Const WAVE_DURATION:Int = 80
Field enemyShips:Int[5*10]
Field asteroids:Int[5*10]
Method FirstWave:Void()
updateSpeed = 0.15
' clear normal enemy ships:
For Local i := 0 Until 5*9
enemyShips[i] = 0
End
wave = WAVE_START
waveCounter = WAVE_DURATION
' clear enemies:
For Local i:Int = 0 Until enemyShips.Length()
enemyShips[i] = 0
End
For Local i:Int = 0 Until asteroids.Length()
asteroids[i] = 0
End
bossEnergy = 0
End
Method StartNewWave:Void()
updateSpeed += 0.005
movementSpeed += 0.005
waveNumber += 1
Local lastWave := wave
Local type:Int = WAVE_RANDOM_SHIPS
If wave <> WAVE_START Then type = Int(Rnd(1000)) Mod NUMBER_OF_WAVE_TYPES
If type = lastWave Then type = (type+1) Mod NUMBER_OF_WAVE_TYPES
wave = type
waveCounter = WAVE_DURATION
End
Field bossEnergy:Int
Field bossPosition:Int
Field bossDirection:Int = -1
Field bossDamageCounter:Int
Method UpdateEnemies:Void(tick:Bool)
If tick Then
waveCounter -= 1
If waveCounter <= 0 Then StartNewWave()
Select wave
Case WAVE_START
If waveCounter Mod 10 = 5 Or waveCounter Mod 20 = 12 Then
CreateEnemyShip(Rnd(5))
Else
If Rnd(100) < 10 Then CreateAsteroid(Rnd(5))
End
Case WAVE_RANDOM_SHIPS
If waveCounter Mod 5 = 2 Or waveCounter Mod 20 = 10 Then
CreateEnemyShip(Rnd(5))
Else
If Rnd(100) < 10 Then CreateAsteroid(Rnd(5))
End
Case WAVE_FORMATION_1
Select waveCounter
Case 70, 60, 50, 40, 30, 20, 10
CreateEnemyShip(Rnd(5))
Case 77, 46, 26
CreateEnemyShip(2)
Case 76, 45, 25
CreateEnemyShip(1)
CreateEnemyShip(3)
Case 75, 44, 24
CreateEnemyShip(0)
CreateEnemyShip(4)
Default
If Rnd(100) < 10 Then CreateAsteroid(Rnd(5))
End
Case WAVE_ASTEROIDS
If Rnd(100) < 40 Then CreateAsteroid(Rnd(5))
Case WAVE_BOSS
Select waveCounter
Case 78
bossEnergy = 10
bossDamageCounter = 3
bossPosition = Rnd(1000) Mod 5
Case 1
If Not gameover Then
If bossEnergy > 0 Then waveCounter = 10
Else
bossEnergy = 0
End
End
If waveCounter < 78 And bossEnergy <= 0 Then waveCounter = 0
Case WAVE_FRONT_1
Select waveCounter
Case 70, 60, 50, 35, 30, 20, 5, 1
CreateEnemyShip(Rnd(5))
Case 77,74, 46,43, 26,13
CreateEnemyShip(2)
Case 76,73, 45,42, 25,12
CreateEnemyShip(1)
CreateEnemyShip(3)
Case 75,72, 44,41, 24,11
CreateEnemyShip(0)
CreateEnemyShip(4)
End
Default '(WAVE_FORMATION_1)
Select waveCounter
Case 90, 80, 70, 60, 50, 40, 30, 20, 10
CreateEnemyShip(Rnd(5))
Case 97, 56, 26
CreateEnemyShip(2)
Case 96, 55, 25
CreateEnemyShip(1)
CreateEnemyShip(3)
Case 95, 54, 24
CreateEnemyShip(0)
CreateEnemyShip(4)
End
End
UpdateEnemyShips()
UpdateAsteroids()
UpdateBoss(tick)
End
End
Method RenderEnemies:Void()
RenderEnemyShips()
RenderAsteroids()
RenderBoss()
End
Method CreateEnemyShip:Void(pos:Int)
If pos < 0 Or pos >= 5 Then Return
enemyShips[pos] = 2
End
Method CreateAsteroid:Void(pos:Int)
If pos < 0 Or pos >= 5 Then Return
asteroids[pos] = 4
End
Method UpdateEnemyShips:Void()
' update enemy ships
For Local y:Int = 10 To 0 Step -1
For Local x:Int = 0 Until 5
If y = 0 Then
' do nothing?
Elseif y < 10
If enemyShips[x+(y-1)*5] <> 0 Then
enemyShips[x+y*5] = enemyShips[x+(y-1)*5]
enemyShips[x+(y-1)*5] = 0
End
'Elseif y = 9 Then
Else
If enemyShips[x+(y-1)*5] And playerPos = x Then DamagePlayer()
enemyShips[x+(y-1)*5] = 0
End
End
End
End
Method RenderEnemyShips:Void()
For Local i:Int = 0 Until 5*10
If enemyShips[i] = 0 Then Continue
lcd.DrawFigure(ENEMY_PARTS_LARGE+i-5)
lcd.DrawFigure(BULLETS+i-5)
lcd.DrawFigure(ENEMY_PARTS_SMALL+i-5)
lcd.DrawFigure(ENEMY_PARTS_SMALL+i)
End
End
Method UpdateAsteroids:Void()
' update enemy ships
For Local y:Int = 10 To 0 Step -1
For Local x:Int = 0 Until 5
If y = 0 Then
' do nothing?
Elseif y < 10
If asteroids[x+(y-1)*5] <> 0 Then
asteroids[x+y*5] = asteroids[x+(y-1)*5]
asteroids[x+(y-1)*5] = 0
End
Else
If asteroids[x+(y-1)*5] And playerPos = x Then DamagePlayer()
asteroids[x+(y-1)*5] = 0
End
End
End
End
Method RenderAsteroids:Void()
For Local i:Int = 0 Until 5*10
If asteroids[i] = 0 Then Continue
lcd.DrawFigure(BULLETS+i)
lcd.DrawFigure(ENEMY_PARTS_SMALL+i)
End
End
Method UpdateBoss:Void(tick:Bool)
If bossEnergy <= 0 Then Return
If tick Then
If Rnd(1000) < 200 Then bossDirection *= -1
bossDamageCounter -= 1
bossPosition += bossDirection
If bossPosition < 0 Then bossPosition = 0 ; bossDirection = 1
If bossPosition > 4 Then bossPosition = 4 ; bossDirection = -1
If Rnd(1000) < 200 Then CreateAsteroid(bossPosition)
End
End
Method RenderBoss:Void()
If bossEnergy <= 0 Then Return
If bossDamageCounter <= 0 Or Flicker() Then lcd.DrawFigure(BOSS+bossPosition)
If Not gameover Then
lcd.DrawFigure(BOSS_LP_BORDER)
For Local i := 0 Until bossEnergy
lcd.DrawFigure(BOSS_LP_BAR+i)
End
End
End
'#################################################################
End
Function Main:Int()
New MainApp
Return 0
End
Leave a comment
Log in with itch.io to leave a comment.
Comments
I love the fact you limited the amount the player can shoot with the overheating system, rather than something like ammo, that's been done and would be more boring. I also quite enjoyed how the ship bends a little bit depending on your position.
Thank you! Overheating was part of some games that I played as a child and somehow I really like this concept ;)
Very well put together LCD game! Flawless style, and the concept allows for much variation. And you really topped it with the boss fight! Shame that the balancing is not yet there, if you fix it I'd suggest that you also add more different flight formations for the fighters, to make it even better!
Thx! New formations can be added easily and the balancing will also be adjusted no time – however I will do this after the voting.
Brilliant - blood brilliant. I keep playing it.....
Thank you! After the voting I will fix some minor issues (balancing …) and eventually add some new enemies and behaviors so that there is a little bit more variety.
Very nice and extremely well playable. I want to play a physical version of it :)
Tank you so much!