Scramble (arcade game remake)
#21
Quote:And you're running Windows 10? What browser? I've never had a problem with DOSBox for this type of thing and that's just an online flavour of the same.

I have Win 10 Prof last updates. Browser is Chrome. - There is nothing more:

[Image: Captain-Comic2023-03-01-1512.jpg]
Reply
#22
I found Naalaa code
it is not very big ..so if anyone interested i will post it
Reply
#23
(03-02-2023, 04:01 PM)aurel Wrote: I found Naalaa code
it is not very big ..so if anyone interested i will post it

I am interested in comparing Naalaa version to JB version, post at BASIC4ALL forum, it's perfect subject for there!
b = b + ...
Reply
#24
I don't think there's a comparison, bplus
Reply
#25
(02-26-2023, 10:56 PM)RokCoder Wrote: Having finished Galaga I decided to try converting my all time favourite arcade game, Scramble. It's gone pretty well I think!

Controls are arrow keys to move and A/Z to fire and bomb.

The ZIP file contains scramble.bas along with a subfolder called assets which contains all the sound effects, graphics, etc. After building the project, the EXE must reside in the same folder as the BAS file. It accesses the assets folder relatively so won't find it if the EXE is in the wrong place.

Hope you have fun!

Great job on this, it is very faithful to the original. 

That meteor stage is HARD! If I was back in 6th grade, I would have already spent my entire summer's paper route money in one evening!

I realize the original game was played on a vertically oriented CRT, however on the PC, I find the screen orientation is a little claustrophobic. 

How difficult would it be for you to tweak a "widescreen" version that makes use of the full screen, and displays more of the game horizontally? 

PS And next, how about taking Scramble and moving it into 3 dimensions for... ZAXXON? 

Anyway, great job and thanks for sharing this! 

PS In order to not lose any more paper route money, I added some constants (lines 46-50) so I have a prayer at making it to the final stages of the game! Maybe adding some difficulty settings would make it enjoyable for young players, or those of us who are challenged in the reflexes department. 

Code: (Select All)
'======================================================================================================================================================================================================
' SCRAMBLE
'------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
' Programmed by RokCoder
'------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
' Scramble is a side-scrolling shooter game released for arcades in 1981. It was
' developed by Konami, and manufactured and distributed by Leijac in Japan and
' Stern in North America.
' It was the first side-scrolling shooter with forced scrolling and multiple
' distinct levels, serving as a foundation for later side-scrolling shooters.
'
' This version is a tribute to the original programmed using QB64PE
'------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
' V0.1 - 26/02/2023 - First release
'------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
' https://github.com/rokcoder-qb64/scramble
'------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
' https://www.rokcoder.com
' https://www.github.com/rokcoder
' https://www.facebook.com/rokcoder
' https://www.youtube.com/rokcoder
'======================================================================================================================================================================================================

$VersionInfo:CompanyName=RokSoft
$VersionInfo:FileDescription=QB64 Scramble
$VersionInfo:InternalName=scramble.exe
$VersionInfo:ProductName=QB64 Scramble
$VersionInfo:OriginalFilename=scramble.exe
$VersionInfo:LegalCopyright=(c)2023 RokSoft
$VersionInfo:FILEVERSION#=0,1,0,0
$VersionInfo:PRODUCTVERSION#=0,1,0,0
$ExeIcon:'./assets/scramble.ico'

'======================================================================================================================================================================================================

Option _Explicit
Option _ExplicitArray

'======================================================================================================================================================================================================

Const FALSE = 0
Const TRUE = Not FALSE

' *****************************************************************************
' FOR THOSE OF US WITH POOR REFLEXES AND NEED TO STACK THE GAME IN THEIR FAVOR:
Const MAX_BOMBS = 12
Const X_SPEED = 2
Const Y_SPEED = 2
Const BOMB_PAUSE = 2
Const FIRE_PAUSE = 2
' (MAYBE LATER ADD SOME DIFFICULTY OPTIONS FOR YOUNGER OR CHALLENGED PLAYERS?)
' *****************************************************************************

Const SCREEN_WIDTH = 224
Const SCREEN_HEIGHT = 256
Const NUM_STAGES = 6
Const TILE_WIDTH = 8
Const TILE_HEIGHT = 8
Const NUM_COLUMNS = Int(SCREEN_WIDTH / TILE_WIDTH)
Const NUM_ROWS = 25
Const GAME_HEIGHT = NUM_ROWS * TILE_HEIGHT

Const KEYDOWN_LEFT = 19200
Const KEYDOWN_RIGHT = 19712
Const KEYDOWN_UP = 18432
Const KEYDOWN_DOWN = 20480
Const KEYDOWN_FIRE = 97
Const KEYDOWN_BOMB = 122

Const PLAYER_FLYING = 0
Const PLAYER_EXPLODING = 1
Const PLAYER_SPAWNING = 2

Const PLAYER_WIDTH = 32
Const PLAYER_HEIGHT = 16

Const MAX_FUEL = 112
Const INITIAL_FUEL_SPEED = 20
Const DELTA_FUEL_SPEED_PER_PASS = 2

Const AMMO_BULLET = 18
Const AMMO_BOMB = 8

Const TYPE_MISSILE = 0
Const TYPE_FUEL = 1
Const TYPE_MYSTERY = 2
Const TYPE_BASE = 3
Const TYPE_METEOR = 4
Const TYPE_UFO = 5

Const SPRITE_ROCKET = 0
Const SPRITE_FUEL = 1
Const SPRITE_MYSTERY = 2
Const SPRITE_BASE = 3
Const SPRITE_METEOR = 4
Const SPRITE_UFO = 5
Const SPRITE_PLAYER = 6
Const SPRITE_PLAYER_EXPLOSION = 7
Const SPRITE_BOMB = 8
Const SPRITE_BOMB_EXPLOSION = 9
Const SPRITE_UFO_EXPLOSION = 10
Const SPRITE_LIVE = 11
Const SPRITE_LEVEL_FLAG = 12
Const SPRITE_MYSTERY_SCORE = 13
Const SPRITE_OBJECT_EXPLOSION = 14
Const SPRITE_TERRAIN = 15
Const SPRITE_FUEL_BAR = 16
Const SPRITE_STAGE = 17
Const SPRITE_BULLET = 18
Const SPRITE_TEXT = 19

Const SFX_LASER = 0
Const SFX_FUEL_WARNING = 1
Const SFX_SMALL_EXPLOSION = 2
Const SFX_ENGINE = 3
Const SFX_ROCKET_EXPLOSION = 4
Const SFX_BOMB = 5
Const SFX_START_GAME = 6
Const SFX_EXPLOSION = 7

Const text$ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789?c.- "

Const TEXT_WHITE = 0
Const TEXT_RED = 1
Const TEXT_BLUE = 2
Const TEXT_YELLOW = 3
Const TEXT_PURPLE = 4

Const STATE_TITLE = 0
Const STATE_HIGHSCORES = 1
Const STATE_SCORETABLE = 2
Const STATE_DEMO = 3
Const STATE_STARTING_GAME = 4
Const STATE_PLAY = 5
Const STATE_GAME_OVER = 6
Const STATE_REACHED_BASE = 7

'======================================================================================================================================================================================================

Type POINT
    x As Integer
    y As Integer
End Type

Type RECT
    x As Integer
    y As Integer
    w As Integer
    h As Integer
    cx As Integer
    cy As Integer
End Type

Type SPRITE
    spriteId As Integer
    position As POINT
    frame As Integer
    counter As Integer
End Type

Type SPRITEDATA
    offset As Integer
    size As POINT
    hitbox As RECT
End Type

Type PLAYER
    sprite As SPRITE
    state As Integer
    fuel As Integer
    fuelCounter As Integer
    fuelSpeed As Integer
    firePause As Integer
    firePressed As Integer
    bombPause As Integer
    bombPressed As Integer
End Type

Type OBJECT
    sprite As SPRITE
    inFlight As Integer
End Type

Type GAME
    frameCounter As Long
    fps As Integer
    dataIndex As Integer
    columnIndex As Integer
    stage As Integer
    currentPalette As Integer
    progressPalette As Integer
    score As Integer
    hiscore As Integer
    lives As Integer
    scrollOffset As Integer
    state As Integer
    highlightScore As Integer
    baseDestroyed As Integer
    flagCount As Integer
End Type

Type COLUMN
    texture As Long
    top As Integer
    bottom As Integer
End Type

Type SPAWNDATA
    x As Integer
    y As Integer
    count As Integer
End Type

Type STARS
    sprite0 As Long
    sprite1 As Long
    sprite2 As Long
    sprite3 As Long
    frame As Integer
    counter As Integer
End Type

'======================================================================================================================================================================================================

Dim Shared spriteSheet&
Dim Shared virtualScreen&
Dim Shared mapData$
Dim Shared game As GAME
Dim Shared spriteData(64) As SPRITEDATA
Dim Shared stageDataOffset%(6)
Dim Shared column(NUM_COLUMNS + 1) As COLUMN
Dim Shared ammo(32) As SPRITE
Dim Shared ammoCount%
Dim Shared object(16) As OBJECT
Dim Shared objectCount%
Dim Shared player As PLAYER
Dim Shared pal&(8, 4), gPal%(4)
Dim Shared tileU%(27), tileV%(27)
Dim Shared paletteOrder%(7)
Dim Shared spawnedSprite(32) As SPRITE
Dim Shared spawnedSpriteCount%
Dim Shared spriteUV(320) As POINT
Dim Shared sfx&(8)
Dim Shared stars As STARS
Dim Shared playerExplosionFrameOrder%(7)
Dim Shared place$(10)
Dim Shared placeColour%(10)
Dim Shared scoreTable$(6)
Dim Shared hiscores%(10)

'===== Game loop ======================================================================================================================================================================================

PrepareScramble

Do: _Limit (game.fps%)
    UpdateFrame
    RenderFrame
Loop

'===== Error handling =================================================================================================================================================================================

fileReadError:
InitialiseHiscores
Resume Next

fileWriteError:
On Error GoTo 0
Resume Next

'===== One time initialisations =======================================================================================================================================================================

Sub PrepareScramble
    Dim m%, i%
    m% = Int((_DesktopHeight - 80) / SCREEN_HEIGHT)
    virtualScreen& = _NewImage(SCREEN_WIDTH, SCREEN_HEIGHT, 256)
    For i% = 0 To NUM_COLUMNS
        column(i%).texture& = _NewImage(TILE_WIDTH, SCREEN_HEIGHT, 256)
        _ClearColor _RGB(0, 0, 0), column(i%).texture&
    Next i%
    Screen _NewImage(SCREEN_WIDTH * m%, SCREEN_HEIGHT * m%, 256)
    _Delay 0.5
    _ScreenMove _Middle
    '$RESIZE:STRETCH
    _AllowFullScreen _SquarePixels , _Smooth
    _Title "Scramble"
    _Dest virtualScreen&
    game.fps% = 60
    Randomize Timer
    game.frameCounter& = 0
    spriteSheet& = LoadImage&("sprite-sheet")
    _ClearColor _RGB(0, 0, 0), spriteSheet&
    stars.sprite0& = LoadImage&("stars-1")
    stars.sprite1& = LoadImage&("stars-2")
    stars.sprite2& = LoadImage&("stars-3")
    stars.sprite3& = LoadImage&("stars-4")
    _ClearColor _RGB(0, 0, 0), stars.sprite1&
    _ClearColor _RGB(0, 0, 0), stars.sprite2&
    _ClearColor _RGB(0, 0, 0), stars.sprite3&
    stars.frame% = 1
    LoadDataFromROM
    PrepareSprites
    ExtractPalettes
    ReadData
    InitialiseStageDataOffset
    LoadAllSFX
    ReadHiscores
    game.highlightScore% = -1
    game.hiscore% = hiscores%(0)
    SetGameState STATE_TITLE
End Sub

Sub PrepareSprites
    Dim i%, c%
    i% = 0
    SetSpriteataWithHitbox SPRITE_PLAYER, i%, 32, 16, 6, 2, 26, 12
    AddSpriteStrip i%, 1, 7, 4, 0, 18
    SetSpriteData SPRITE_PLAYER_EXPLOSION, i%, 32, 16
    AddSpriteStrip i%, 69, 7, 4, 0, 18
    SetSpriteataWithHitbox SPRITE_BOMB, i%, 16, 16, 6, 6, 4, 4
    AddSpriteStrip i%, 103, 7, 5, 0, 18
    SetSpriteData SPRITE_BOMB_EXPLOSION, i%, 16, 16
    AddSpriteStrip i%, 121, 7, 4, 0, 18
    SetSpriteData SPRITE_UFO_EXPLOSION, i%, 16, 16
    AddSpriteStrip i%, 139, 7, 4, 0, 18
    SetSpriteataWithHitbox SPRITE_METEOR, i%, 16, 16, 0, 3, 16, 10
    AddSpriteStrip i%, 157, 7, 4, 0, 18
    SetSpriteData SPRITE_LIVE, i%, 16, 8
    AddSpriteStrip i%, 1, 78, 1, 0, 0
    SetSpriteData SPRITE_LEVEL_FLAG, i%, 8, 8
    AddSpriteStrip i%, 19, 78, 1, 0, 0
    SetSpriteataWithHitbox SPRITE_UFO, i%, 16, 16, 2, 4, 12, 8
    AddSpriteStrip i%, 121, 79, 1, 0, 0
    SetSpriteataWithHitbox SPRITE_ROCKET, i%, 16, 16, 4, 0, 8, 16
    AddSpriteStrip i%, 1, 97, 3, 0, 18
    SetSpriteataWithHitbox SPRITE_BASE, i%, 16, 16, 0, 0, 16, 16
    AddSpriteStrip i%, 37, 97, 3, 0, 18
    SetSpriteData SPRITE_MYSTERY_SCORE, i%, 16, 16
    AddSpriteStrip i%, 73, 97, 3, 0, 18
    SetSpriteataWithHitbox SPRITE_MYSTERY, i%, 16, 16, 0, 0, 16, 16
    AddSpriteStrip i%, 91, 97, 1, 0, 0
    SetSpriteataWithHitbox SPRITE_FUEL, i%, 16, 16, 0, 0, 16, 16
    AddSpriteStrip i%, 91, 115, 1, 0, 0
    SetSpriteData SPRITE_OBJECT_EXPLOSION, i%, 16, 16
    AddSpriteStrip i%, 109, 97, 3, 0, 18
    AddSpriteStrip i%, 91, 133, 1, 0, 0
    SetSpriteData SPRITE_TERRAIN, i%, 8, 8
    AddSpriteStrip i%, 1, 151, 17, 10, 0
    AddSpriteStrip i%, 1, 161, 17, 10, 0
    SetSpriteData SPRITE_FUEL_BAR, i%, 8, 8
    AddSpriteStrip i%, 1, 171, 9, 10, 0
    SetSpriteData SPRITE_STAGE, i%, 32, 8
    AddSpriteStrip i%, 1, 191, 1, 0, 0
    AddSpriteStrip i%, 1, 181, 4, 32, 0
    AddSpriteStrip i%, 35, 191, 1, 0, 0
    AddSpriteStrip i%, 131, 181, 2, 34, 0
    SetSpriteataWithHitbox SPRITE_BULLET, i%, 8, 8, 3, 3, 2, 2
    AddSpriteStrip i%, 181, 201, 1, 0, 0
    SetSpriteData SPRITE_TEXT, i%, 8, 8
    For c% = 0 To 4
        AddSpriteStrip i%, 91, 201 + c% * 30, 9, 9, 0
        AddSpriteStrip i%, 1, 210 + c% * 30, 17, 9, 0
        AddSpriteStrip i%, 1, 201 + c% * 30, 10, 9, 0
        AddSpriteStrip i%, 1, 220 + c% * 30, 1, 9, 0
        AddSpriteStrip i%, 172, 201 + c% * 30, 2, 9, 0
        AddSpriteStrip i%, 154, 210 + c% * 30, 2, 9, 0
    Next c%
End Sub

Sub SetSpriteBasics (s%, i%, sw%, sh%)
    spriteData(s%).offset% = i%
    spriteData(s%).size.x% = sw%
    spriteData(s%).size.y% = sh%
End Sub

Sub SetSpriteData (s%, i%, sw%, sh%)
    SetSpriteBasics s%, i%, sw%, sh%
    SetRect spriteData(s%).hitbox, 0, 0, sw%, sh%
End Sub

Sub SetSpriteataWithHitbox (s%, i%, sw%, sh%, x%, y%, w%, h%)
    SetSpriteBasics s%, i%, sw%, sh%
    SetRect spriteData(s%).hitbox, x%, y%, w%, h%
End Sub

Sub AddSpriteStrip (spriteIndex%, u%, v%, n%, du%, dv%)
    Dim i%
    For i% = 1 To n%
        spriteUV(spriteIndex%).x% = u%
        spriteUV(spriteIndex%).y% = v%
        u% = u% + du%
        v% = v% + dv%
        spriteIndex% = spriteIndex% + 1
    Next i%
End Sub

Sub ExtractPalettes
    Dim i%, x%, y%
    _Source spriteSheet&
    i% = 0
    For y% = 1 To 4
        For x% = 0 To 7
            pal&(Int(i% / 4), i% Mod 4) = _PaletteColor(Point(127 + x% * 4, 97 + y% * 4), spriteSheet&)
            i% = i% + 1
        Next x%
    Next y%
    i% = 0
    For x% = 0 To 3
        gPal%(i%) = Point(127 + x% * 4, 97)
        i% = i% + 1
    Next x%
End Sub

Sub ReadData
    Dim i%
    For i% = 0 To 27: Read tileU%(i%): Next i%
    For i% = 0 To 27: Read tileV%(i%): Next i%
    For i% = 0 To 6: Read paletteOrder%(i%): Next i%
    For i% = 0 To 6: Read playerExplosionFrameOrder%(i%): Next i%
    For i% = 0 To 9: Read place$(i%): Next i%
    For i% = 0 To 9: Read placeColour%(i%): Next i%
    For i% = 0 To 5: Read scoreTable$(i%): Next i%
    Data 161,11,31,1,21,51,71,41,61,91,111,81,101,131,141,121,151,161,151,1,91,101,71,91,121,141,111,131
    Data 161,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,161,161,161,161,161,161,161,161,161,161
    Data 0,1,3,4,5,6,7
    Data 0,1,0,1,0,2,3
    Data "1ST","2ND","3RD","4TH","5TH","6TH","7TH","8TH","9TH","10TH"
    Data 3,3,3,2,2,2,4,4,4,4
    Data " ...  50 PTS     "," ...  80 PTS     "," ... 100 PTS     "," ... 150 PTS     "," ... 800 PTS     "," ... MYSTERY"
End Sub

Sub LoadDataFromROM
    Dim handle&
    handle& = FreeFile
    Open "assets/game-data.bin" For Binary As #handle& Len = 1
    mapData$ = Space$(LOF(handle&))
    Get #handle&, , mapData$
    Close #handle&
End Sub

'===== High score code ================================================================================================================================================================================

Sub ReadHiscores
    Dim i%, handle&
    On Error GoTo fileReadError
    If Not _FileExists("scores.txt") Then InitialiseHiscores: Exit Sub
    handle& = FreeFile
    Open "scores.txt" For Input As #handle&
    For i% = 0 To 9
        Input #handle&, hiscores%(i%)
    Next i%
    Close #handle&
    On Error GoTo 0
End Sub

Sub InitialiseHiscores
    Dim i%
    For i% = 0 To 9
        hiscores%(i%) = (10 - i%) * 1000
    Next i%
End Sub

Sub WriteHiscores
    Dim i%, handle&
    On Error GoTo fileWriteError
    handle& = FreeFile
    Open "scores.txt" For Output As #handle&
    For i% = 0 To 9
        Print #handle&, hiscores%(i%)
    Next i%
    Close #handle&
    On Error GoTo 0
End Sub

'===== Frame update functions =========================================================================================================================================================================

Sub UpdateFrame
    UpdateStars
    Select Case game.state%
        Case STATE_TITLE
            If game.frameCounter& > 8 * game.fps% Then SetGameState STATE_HIGHSCORES
        Case STATE_HIGHSCORES
            If game.frameCounter& > 8 * game.fps% Then SetGameState STATE_SCORETABLE
        Case STATE_SCORETABLE
            If game.frameCounter& > 8 * game.fps% Then PrepareForLevel: game.lives% = 1: SetGameState STATE_DEMO
        Case STATE_DEMO
            If player.state% = PLAYER_FLYING Then UpdateScroll
            UpdateObjects
            UpdatePlayer
            UpdateAmmo
            UpdateSpawnedSprites
            CalculateCollisions game.scrollOffset% And 7
            If LifeLost% Then SetGameState STATE_TITLE
        Case STATE_STARTING_GAME
            If game.frameCounter& > 2.5 * game.fps% Then SetGameState STATE_PLAY
        Case STATE_PLAY
            If player.state% = PLAYER_FLYING Then UpdateScroll
            UpdateObjects
            UpdatePlayer
            UpdateAmmo
            UpdateSpawnedSprites
            CalculateCollisions game.scrollOffset% And 7
            If LifeLost% Then
                If game.lives% = 0 Then SetGameState STATE_GAME_OVER Else PrepareForLevel
            End If
            If game.stage% = 5 Then
                If (game.dataIndex% = Len(mapData$) + 150 And game.baseDestroyed%) Or game.dataIndex% = 2 * Len(mapData$) - stageDataOffset%(game.stage%) + 150 Then SetGameState STATE_REACHED_BASE
            End If
        Case STATE_GAME_OVER
            If game.frameCounter& > 2 * game.fps% Then SetGameState STATE_HIGHSCORES
        Case STATE_REACHED_BASE
            If game.frameCounter& > 3 * game.fps% Then
                SetGameState STATE_PLAY
                If game.baseDestroyed% Then BaseDefeated Else PrepareForLevel
            End If
    End Select
End Sub

Sub UpdateScroll
    Dim i%
    game.scrollOffset% = game.scrollOffset% + 1
    If (game.scrollOffset% And 7) = 0 Then UpdateLandscape
    For i% = 0 To objectCount% - 1: object(i%).sprite.position.x% = object(i%).sprite.position.x% - 1: Next i%
    For i% = 0 To spawnedSpriteCount% - 1: spawnedSprite(i%).position.x% = spawnedSprite(i%).position.x% - 1: Next i%
End Sub

Sub UpdateFromVirtualScreen
    game.frameCounter& = game.frameCounter& + 1
    _PutImage , virtualScreen&, 0, (0, 0)-(SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1)
    _Display
End Sub

'===== Frame render functions =========================================================================================================================================================================

Sub RenderFrame
    RenderStars
    Select Case game.state%
        Case STATE_TITLE
            RenderTitle
            RenderStartKey
        Case STATE_HIGHSCORES
            RenderHighscores
            RenderStartKey
        Case STATE_SCORETABLE
            RenderScoreTable
            RenderStartKey
        Case STATE_DEMO
            RenderLandscape game.scrollOffset% And 7
            RenderObjects
            RenderPlayer
            RenderAmmo
            RenderSpawnedSprites
            RenderHud
            RenderStartKey
        Case STATE_STARTING_GAME
            RenderStartingGame
        Case STATE_PLAY
            RenderLandscape game.scrollOffset% And 7
            RenderObjects
            RenderPlayer
            RenderAmmo
            RenderSpawnedSprites
            RenderHud
        Case STATE_GAME_OVER
            RenderGameOver
        Case STATE_REACHED_BASE
            RenderReachedBase
    End Select
    UpdateFromVirtualScreen
End Sub

Sub RenderSpawnedSprites
    Dim i%
    For i% = spawnedSpriteCount% - 1 To 0 Step -1
        RenderSprite spawnedSprite(i%), 40
    Next i%
End Sub

Sub RenderHud
    Dim i%, d%
    Line (0, 0)-(SCREEN_WIDTH - 1, 39), 0, BF
    Line (0, 240)-(SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1), 0, BF
    RenderScore
    For i% = 0 To 5
        RenderImage SPRITE_STAGE, i%, 16 + i% * 32, 24
        RenderImage SPRITE_STAGE, 6 - (i% <= game.stage%), 16 + i% * 32, 32
    Next i%
    RenderText 3, 30, "FUEL", TEXT_YELLOW
    For i% = 0 To game.lives% - 1
        RenderImage SPRITE_LIVE, 0, 16 * i%, 248
    Next i%
    For i% = 0 To game.flagCount% - 1
        RenderImage SPRITE_LEVEL_FLAG, 0, 216 - 8 * i%, 248
    Next i%
    For i% = 0 To MAX_FUEL / 8 - 1
        d% = player.fuel% - i% * 8
        If d% < 0 Then d% = 0 Else If d% > 8 Then d% = 8
        RenderImage SPRITE_FUEL_BAR, 8 - d%, 64 + 8 * i%, 240
    Next i%
End Sub

Sub RenderScore
    Dim s$
    RenderText 3, 0, "1UP", TEXT_WHITE
    RenderText 9, 0, "HIGH SCORE", TEXT_WHITE
    s$ = LTrim$(Str$(game.score%))
    RenderText 7 - Len(s$), 1, s$, TEXT_YELLOW
    s$ = LTrim$(Str$(game.hiscore%))
    RenderText 17 - Len(s$), 1, s$, TEXT_YELLOW
End Sub

Sub RenderStartingGame
    RenderScore
    RenderText 9, 20, "PLAYER ONE", TEXT_WHITE
End Sub

Sub RenderGameOver
    RenderScore
    RenderText 9, 20, "PLAYER ONE", TEXT_WHITE
    RenderText 9, 22, "GAME  OVER", TEXT_WHITE
End Sub

Sub RenderTitle
    RenderScore
    RenderText 12, 6, "PLAY", TEXT_YELLOW
    RenderText 8, 9, "- SCRAMBLE -", TEXT_BLUE
    RenderText 3, 17, "HOW FAR CAN YOU INVADE", TEXT_RED
    RenderText 4, 20, "OUR SCRAMBLE SYSTEM ?", TEXT_RED
End Sub

Sub RenderStartKey
    Static counter&, pressed%
    If counter& Mod game.fps% < game.fps% * 0.8 Then RenderText 4, 31, "PRESS SPACE TO START", TEXT_WHITE
    If _KeyDown(32) Then pressed% = TRUE Else If pressed% = TRUE Then pressed% = FALSE: SetGameState STATE_STARTING_GAME
    counter& = counter& + 1
End Sub

Sub RenderHighscores
    Dim i%, s$, c%
    RenderScore
    RenderText 5, 4, "- SCORE RANKING -", TEXT_RED
    For i% = 0 To 9
        c% = placeColour%(i%)
        If i% = game.highlightScore% Then c% = TEXT_WHITE
        RenderText 6, 7 + i% * 2, place$(i%), c%
        s$ = LTrim$(Str$(hiscores%(i%))) + " PTS"
        RenderText 22 - Len(s$), 7 + i% * 2, s$, c%
    Next i%
End Sub

Sub RenderScoreTable
    Dim i%, j%, c%
    RenderScore
    SetPalette 0
    RenderText 7, 7, "- SCORE TABLE -", TEXT_YELLOW
    RenderImage SPRITE_ROCKET, 0, 8 * 8, 2 + 9 * 8
    RenderImage SPRITE_ROCKET, 2, 8 * 8, 2 + 12 * 8
    RenderImage SPRITE_UFO, 0, 8 * 8, 2 + 15 * 8
    RenderImage SPRITE_FUEL, 0, 8 * 8, 2 + 18 * 8
    RenderImage SPRITE_BASE, 0, 8 * 8, 2 + 21 * 8
    RenderImage SPRITE_MYSTERY, 0, 8 * 8, 2 + 24 * 8
    c% = Int(game.frameCounter& / (game.fps% / 16)) - 1
    For i% = 0 To 5
        For j% = 1 To Len(scoreTable$(i%))
            If c% > 0 Then RenderText 9 + j%, 10 + i% * 3, Mid$(scoreTable$(i%), j%, 1), TEXT_WHITE
            c% = c% - 1
        Next j%
    Next i%
End Sub

Sub RenderReachedBase
    RenderScore
    If game.baseDestroyed% Then
        RenderText 7, 12, "CONGRATULATIONS", TEXT_RED
        RenderText 2, 14, "YOU COMPLETED YOUR DUTIES", TEXT_YELLOW
        RenderText 2, 16, "GOOD LUCK NEXT TIME AGAIN", TEXT_BLUE
    Else
        RenderText 4, 12, "DISASTER - YOU FAILED", TEXT_RED
        RenderText 5, 14, "TO DESTROY THE BASE", TEXT_YELLOW
        RenderText 10, 16, "TRY AGAIN", TEXT_BLUE
    End If
End Sub

'===== Simple asset loading functions =================================================================================================================================================================

Sub AssetError (fname$)
    Screen 0
    Print "Unable to load "; fname$
    Print "Please make sure EXE is in same folder as scramble.bas"
    Print "(Set Run/Output EXE to Source Folder option in the IDE before compiling)"
    End
End Sub

Function LoadImage& (fname$)
    Dim asset&, f$
    f$ = "./assets/" + fname$ + ".png"
    asset& = _LoadImage(f$, 256)
    If asset& = -1 Then AssetError (f$)
    LoadImage& = asset&
End Function

Function SndOpen& (fname$)
    Dim asset&, f$
    f$ = "./assets/" + fname$
    asset& = _SndOpen(f$)
    If asset& = -1 Then AssetError (f$)
    SndOpen& = asset&
End Function

Sub SetRect (r As RECT, x%, y%, w%, h%)
    r.x% = x%
    r.y% = y%
    r.w% = w%
    r.h% = h%
    r.cx% = r.x% + r.w% / 2
    r.cy% = r.y% + r.h% / 2
End Sub

'===== Terrain code ===================================================================================================================================================================================

Sub DrawPreStageTerrain
    Dim dataOffset%, column%
    ResetLandscape
    dataOffset% = stageDataOffset%(game.stage%)
    For column% = 0 To NUM_COLUMNS - 1
        AddColumn column%, 0, 0, 20, 11, FillerType%
        game.columnIndex% = game.columnIndex% Xor 1
    Next column%
End Sub

Sub NextColumn
    'There's some magic code in here that loops the background for the final level
    Dim i%, o%, d%
    If game.dataIndex% < Len(mapData$) Then
        If Asc(mapData$, game.dataIndex%) = 255 Then
            If game.stage% < 5 Then
                game.stage% = game.stage% + 1
                game.dataIndex% = game.dataIndex% + 1
            End If
        End If
    End If
    i% = game.dataIndex%
    Do Until i% < Len(mapData$)
        i% = i% - (Len(mapData$) - stageDataOffset%(game.stage%))
    Loop
    AddColumn NUM_COLUMNS, Asc(mapData$, i%), Int(Asc(mapData$, i% + 1) / 2), Asc(mapData$, i% + 2), Int(Asc(mapData$, i% + 3) / 2), FillerType%
    o% = Asc(mapData$, i% + 4)
    d% = TRUE
    If o% = 8 Then
        If Asc(mapData$, i% - 2) = 8 Then d% = FALSE
    End If
    If d% Then SpawnObjectFromData o%, Asc(mapData$, i% + 5)
    SpawnNonDataObjects
    game.dataIndex% = game.dataIndex% + 6
End Sub

Sub UpdateLandscape
    ScrollLandscapeTiles
    NextColumn
    game.columnIndex% = game.columnIndex% Xor 1
    game.progressPalette% = game.progressPalette% + 1
    If game.progressPalette% = 64 Then NextPalette
End Sub

Sub RenderLandscape (offset%)
    Dim i%
    For i% = 0 To NUM_COLUMNS + 1
        _PutImage (i% * TILE_WIDTH - offset%, 40), column(i%).texture&, ,
    Next i%
End Sub

Sub AddColumn (column%, topTileY%, topTile%, bottomTileY%, bottomTile%, fillerTile%)
    Dim i%, handle&, o%
    i% = 0
    handle& = column(column%).texture&
    _Dest handle&
    Cls
    column(column%).top% = topTileY%
    column(column%).bottom% = bottomTileY%
    If topTile% > 0 Then
        Do Until i% = topTileY%
            _PutImage (0, i% * TILE_HEIGHT), spriteSheet&, handle&, (tileU%(fillerTile%), tileV%(fillerTile%))-(tileU%(fillerTile%) + 7, tileV%(fillerTile%) + 7)
            i% = i% + 1
        Loop
        If topTile% > 63 Then topTile% = topTile% - 44
        _PutImage (0, i% * TILE_HEIGHT), spriteSheet&, handle&, (tileU%(topTile%), tileV%(topTile%))-(tileU%(topTile%) + 7, tileV%(topTile%) + 7)
        i% = i% + 1
    End If
    i% = bottomTileY%
    If bottomTile% >= 33 Then
        'Dealing with the "KONAMI" text on the final level
        o% = spriteData(SPRITE_TEXT).offset% + bottomTile% - 33
        _PutImage (0, i% * TILE_HEIGHT), spriteSheet&, handle&, (spriteUV(o%).x%, spriteUV(o%).y%)-(spriteUV(o%).x% + spriteData(SPRITE_TEXT).size.x% - 1, spriteUV(o%).y% + spriteData(SPRITE_TEXT).size.y% - 1)
    Else
        _PutImage (0, i% * TILE_HEIGHT), spriteSheet&, handle&, (tileU%(bottomTile%), tileV%(bottomTile%))-(tileU%(bottomTile%) + 7, tileV%(bottomTile%) + 7)
    End If
    i% = i% + 1
    Do Until i% = NUM_ROWS
        _PutImage (0, i% * TILE_HEIGHT), spriteSheet&, handle&, (tileU%(fillerTile%), tileV%(fillerTile%))-(tileU%(fillerTile%) + 7, tileV%(fillerTile%) + 7)
        i% = i% + 1
    Loop
    _Dest virtualScreen&
End Sub

Sub ResetLandscape
    game.columnIndex = 0
End Sub

Sub ScrollLandscapeTiles
    Dim i%, columnCache As COLUMN
    columnCache = column(0)
    For i% = 0 To NUM_COLUMNS - 1
        column(i%) = column(i% + 1)
    Next i%
    column(NUM_COLUMNS) = columnCache
End Sub

Function FillerType%
    If game.stage% < 3 Then
        FillerType% = 14
    Else
        If game.columnIndex Mod 2 = 0 Then
            FillerType% = 16
        Else
            FillerType% = 19
        End If
    End If
End Function

'===== Player code ====================================================================================================================================================================================

Sub UpdatePlayer
    Select Case player.state%
        Case PLAYER_FLYING
            player.sprite.frame% = Int(game.frameCounter& / 8) Mod 3
            If player.fuel% > 0 Then
                player.fuelCounter% = player.fuelCounter% - 1
                If player.fuelCounter% = 0 Then
                    player.fuelCounter% = player.fuelSpeed%
                    player.fuel% = player.fuel% - 1
                    If player.fuel% = 20 Then
                        PlaySfxLooping SFX_FUEL_WARNING
                    End If
                End If
            End If
            If game.state% = STATE_PLAY Then
                If player.fuel% > 0 Then
                   
                    player.sprite.position.x% = player.sprite.position.x% + (_KeyDown(KEYDOWN_LEFT) * X_SPEED) - (_KeyDown(KEYDOWN_RIGHT) * X_SPEED)
                    player.sprite.position.y% = player.sprite.position.y% + (_KeyDown(KEYDOWN_UP) * Y_SPEED) - (_KeyDown(KEYDOWN_DOWN) * Y_SPEED)
                    'player.sprite.position.x% = player.sprite.position.x% + _KeyDown(KEYDOWN_LEFT) - _KeyDown(KEYDOWN_RIGHT)
                    'player.sprite.position.y% = player.sprite.position.y% + _KeyDown(KEYDOWN_UP) - _KeyDown(KEYDOWN_DOWN)
                Else
                    player.sprite.position.y% = player.sprite.position.y% + 1
                End If
               
                ' CHECK MOVEMENT BOUNDARIES
                If player.sprite.position.x% < 8 Then
                    player.sprite.position.x% = 8
                ElseIf player.sprite.position.x% > SCREEN_WIDTH - 24 Then
                    player.sprite.position.x% = SCREEN_WIDTH - 24
                    'ElseIf player.sprite.position.x% > SCREEN_WIDTH / 2 - 24 Then
                    '   player.sprite.position.x% = SCREEN_WIDTH / 2 - 24
                End If
               
                If player.sprite.position.y% < 0 Then
                    player.sprite.position.y% = 0
                ElseIf player.sprite.position.y% > GAME_HEIGHT - 16 Then
                    player.sprite.position.y% = GAME_HEIGHT - 16
                End If
               
                If player.firePause% = 0 Then
                    If _KeyDown(KEYDOWN_FIRE) Then
                        If Not player.firePressed% Then
                            player.firePressed% = TRUE
                            'player.firePause = 4
                            player.firePause = FIRE_PAUSE
                            PlaySfx SFX_LASER
                            CreateAmmo AMMO_BULLET
                        End If
                    Else
                        player.firePressed% = FALSE
                    End If
                Else
                    player.firePause% = player.firePause% - 1
                End If
                If player.bombPause% = 0 Then
                    If _KeyDown(KEYDOWN_BOMB) Then
                        If Not player.bombPressed% Then
                            player.bombPressed% = TRUE
                           
                            'If BombCount% < 2 Then
                            If BombCount% < MAX_BOMBS Then
                                'player.bombPause = 4
                                player.bombPause = BOMB_PAUSE
                                PlaySfx SFX_BOMB
                                CreateAmmo AMMO_BOMB
                            End If
                        End If
                    Else
                        player.bombPressed% = FALSE
                    End If
                Else
                    player.bombPause% = player.bombPause% - 1
                End If
            End If

        Case PLAYER_SPAWNING
            player.state% = PLAYER_FLYING
            game.lives% = game.lives% - 1
            PlaySfxLooping SFX_ENGINE
    End Select
End Sub

Sub RenderPlayer
    If player.state% = PLAYER_FLYING Then RenderSprite player.sprite, 40
End Sub

Sub DestroyPlayer
    player.state% = PLAYER_EXPLODING
    SpawnSprite SPRITE_PLAYER_EXPLOSION, player.sprite.position: PlaySfx SFX_EXPLOSION
End Sub

'===== Game enemy object handling =====================================================================================================================================================================

Sub UpdateObjects
    Dim i%
    For i% = objectCount% - 1 To 0 Step -1
        object(i%).sprite.counter% = object(i%).sprite.counter% + 1
        Select Case object(i%).sprite.spriteId%
            Case TYPE_MISSILE: UpdateMissile (i%)
            Case TYPE_UFO: UpdateUfo (i%)
            Case TYPE_BASE: UpdateBase (i%)
            Case TYPE_METEOR: UpdateMeteor (i%)
        End Select
        If object(i%).sprite.position.x% < -15 Or object(i%).sprite.position.y% < -15 Then RemoveObject (i%)
    Next i%
End Sub

Sub UpdateMissile (i%)
    If object(i%).inFlight Then
        object(i%).sprite.position.y% = object(i%).sprite.position.y% - 1
        object(i%).sprite.frame% = 1 + (Int(object(i%).sprite.counter% / 8) And 1)
    Else
        If Not (game.stage% = 1 Or game.stage% = 2) Then
            If game.state% = STATE_DEMO Then
                If object(i%).sprite.position.x% = 40 Then object(i%).inFlight% = TRUE
            Else
                If object(i%).sprite.position.x% = NUM_COLUMNS * 4 Then
                    If Rnd < 0.25 Then object(i%).inFlight% = TRUE
                ElseIf object(i%).sprite.position.x% < NUM_COLUMNS * 4 Then
                    If Rnd < 0.01 Then object(i%).inFlight% = TRUE
                End If
            End If
        End If
    End If
End Sub

Sub UpdateBase (i%)
    object(i%).sprite.frame% = Int(object(i%).sprite.counter% / 8) Mod 3
End Sub

Sub UpdateMeteor (i%)
    object(i%).sprite.position.x% = object(i%).sprite.position.x% - 3
    object(i%).sprite.frame% = Int(object(i%).sprite.counter% / 8) And 3
End Sub

Sub UpdateUfo (i%)
    object(i%).sprite.position.y% = GAME_HEIGHT / 2 - 8 + 32 * Sin(_D2R(object(i%).sprite.counter% * 6))
End Sub

Sub RenderObjects
    Dim i%
    For i% = 0 To objectCount% - 1
        RenderSprite object(i%).sprite, 40
    Next i%
End Sub

Sub RemoveObject (i%)
    object(i%) = object(objectCount% - 1)
    objectCount% = objectCount% - 1
End Sub

Sub DestroyObject (i%)
    Dim d%
    Select Case object(i%).sprite.spriteId%
        Case TYPE_METEOR: Exit Sub
        Case TYPE_MISSILE: SpawnSprite SPRITE_OBJECT_EXPLOSION, object(i%).sprite.position: d% = 50 - 30 * object(i%).inFlight%: PlaySfx SFX_ROCKET_EXPLOSION
        Case TYPE_FUEL: SpawnSprite SPRITE_OBJECT_EXPLOSION, object(i%).sprite.position: d% = 150: player.fuel% = player.fuel% + 15: PlaySfx SFX_EXPLOSION: If player.fuel% > 20 Then StopSfx SFX_FUEL_WARNING: If player.fuel% > MAX_FUEL Then player.fuel% = MAX_FUEL
        Case TYPE_MYSTERY: d% = Int(Rnd * 3): SpawnSprite SPRITE_MYSTERY_SCORE, object(i%).sprite.position: spawnedSprite(spawnedSpriteCount% - 1).frame% = d%: d% = (d% + 1) * 100: PlaySfx SFX_EXPLOSION
        Case TYPE_BASE: SpawnSprite SPRITE_OBJECT_EXPLOSION, object(i%).sprite.position: d% = 800: PlaySfx SFX_EXPLOSION: game.baseDestroyed% = TRUE
        Case TYPE_UFO: SpawnSprite SPRITE_UFO_EXPLOSION, object(i%).sprite.position: d% = 100: PlaySfx SFX_ROCKET_EXPLOSION
    End Select
    If game.state% = STATE_PLAY Then IncreaseScore (d%)
    RemoveObject i%
End Sub

'===== Background stars ===============================================================================================================================================================================

Sub UpdateStars
    stars.counter% = stars.counter% + 1
    If stars.counter% >= game.fps% Then
        stars.counter% = 0
        stars.frame% = stars.frame% + 1
        If stars.frame% = 4 Then stars.frame% = 1
    End If
End Sub

Sub RenderStars
    _PutImage , stars.sprite0&
    Select Case stars.frame%
        Case 1: _PutImage , stars.sprite2&: _PutImage , stars.sprite3&
        Case 2: _PutImage , stars.sprite1&: _PutImage , stars.sprite3&
        Case 3: _PutImage , stars.sprite1&: _PutImage , stars.sprite2&
    End Select
End Sub

'===== Player's ammo ==================================================================================================================================================================================

Sub RenderAmmo
    Dim i%
    For i% = 0 To ammoCount% - 1
        RenderSprite ammo(i%), 40
    Next i%
End Sub

Sub UpdateAmmo
    Dim i%
    For i% = ammoCount% - 1 To 0 Step -1
        Select Case ammo(i%).spriteId%
            Case AMMO_BULLET:
                ammo(i%).position.x% = ammo(i%).position.x% + 4
                If ammo(i%).position.x% > SCREEN_WIDTH Then
                    ammo(i%) = ammo(ammoCount% - 1)
                    ammoCount% = ammoCount% - 1
                End If
            Case AMMO_BOMB:
                Select Case ammo(i%).counter%
                    Case 0: ammo(i%).frame% = 0
                    Case 4: ammo(i%).frame% = 1
                    Case 8: ammo(i%).frame% = 0
                    Case 16: ammo(i%).frame% = 2
                    Case 28: ammo(i%).frame% = 3
                    Case 40: ammo(i%).frame% = 4
                End Select
                Select Case ammo(i%).counter%
                    Case 0 To 15: ammo(i%).position.x% = ammo(i%).position.x% + 1
                    Case 16 To 27: ammo(i%).position.x% = ammo(i%).position.x% + 1: ammo(i%).position.y% = ammo(i%).position.y% + 1 - (ammo(i%).counter% And 1)
                    Case 28 To 39: ammo(i%).position.x% = ammo(i%).position.x% + (ammo(i%).counter% And 1): ammo(i%).position.y% = ammo(i%).position.y% + 1
                    Case Else: ammo(i%).position.y% = ammo(i%).position.y% + 1
                End Select
                ammo(i%).counter% = ammo(i%).counter% + 1
        End Select
    Next i%
End Sub

Sub DestroyAmmo (i%)
    If ammo(i%).spriteId% = AMMO_BOMB Then SpawnSprite SPRITE_BOMB_EXPLOSION, ammo(i%).position: PlaySfx SFX_SMALL_EXPLOSION: StopSfx SFX_BOMB
    ammo(i%) = ammo(ammoCount% - 1)
    ammoCount% = ammoCount% - 1
End Sub

Sub CreateAmmo (ammoType%)
    Select Case ammoType%
        Case AMMO_BULLET: SetSprite ammo(ammoCount%), ammoType%, player.sprite.position.x% + 28, player.sprite.position.y% + 4
        Case AMMO_BOMB: SetSprite ammo(ammoCount%), ammoType%, player.sprite.position.x% + 10, player.sprite.position.y% + 6
    End Select
    ammoCount% = ammoCount% + 1
End Sub

Function BombCount%
    Dim c%, i%
    For i% = 0 To ammoCount% - 1
        c% = c% - (ammo(i%).spriteId% = AMMO_BOMB)
    Next i%
    BombCount% = c%
End Function

'===== Sound manager ==================================================================================================================================================================================

Sub LoadSfx (sfx%, sfx$)
    sfx&(sfx%) = _SndOpen("assets/" + sfx$ + ".ogg")
    If sfx&(sfx%) = 0 Then AssetError sfx$
End Sub

Sub LoadAllSFX
    LoadSfx SFX_LASER, "laser"
    LoadSfx SFX_FUEL_WARNING, "fuel-warning"
    LoadSfx SFX_SMALL_EXPLOSION, "small-explosion"
    LoadSfx SFX_ENGINE, "engine"
    LoadSfx SFX_ROCKET_EXPLOSION, "rocket-explosion"
    LoadSfx SFX_BOMB, "bomb"
    LoadSfx SFX_START_GAME, "start-game"
    LoadSfx SFX_EXPLOSION, "explosion"
End Sub

Sub PlaySfx (sfx%)
    If Not game.state% = STATE_DEMO Then _SndPlay sfx&(sfx%)
End Sub

Sub PlaySfxLooping (sfx%)
    If Not game.state% = STATE_DEMO Then _SndLoop sfx&(sfx%)
End Sub

Sub StopSfx (sfx%)
    _SndStop sfx&(sfx%)
End Sub

Function IsPlayingSfx% (sfx%)
    IsPlayingSfx% = _SndPlaying(sfx&(sfx%))
End Function

'===== Collision detection ============================================================================================================================================================================

Sub CalculateCollisions (xOffset%)
    If player.state% = PLAYER_FLYING Then
        If CheckMapCollision%(xOffset%, player.sprite.position.x% + 8, player.sprite.position.y%, PLAYER_WIDTH - 8, PLAYER_HEIGHT) Then DestroyPlayer Else If CheckObjectCollision% Then DestroyPlayer
    End If
    CheckAmmoCollisions xOffset%
End Sub

Function CheckMapCollision% (xOffset%, x%, y%, w%, h%)
    Dim cLeft%, cRight%, cTop%, cBottom%, i%
    cLeft% = Int((x% + xOffset%) / TILE_WIDTH)
    cRight% = Int((x% + w% - 1 + xOffset%) / TILE_WIDTH)
    cTop% = Int(y% / TILE_HEIGHT)
    cBottom% = Int((y% + h% - 1) / TILE_HEIGHT)
    For i% = cLeft% To cRight%
        If cTop% < column(i%).top% Or cBottom% > column(i%).bottom% Then CheckMapCollision% = TRUE: Exit Function
    Next i%
    CheckMapCollision% = FALSE
End Function

Function CheckObjectCollision%
    Dim i%
    For i% = 0 To objectCount% - 1
        If SpriteCollision%(player.sprite, object(i%).sprite) Then DestroyObject i%: CheckObjectCollision% = TRUE: Exit Function
    Next i%
    CheckObjectCollision% = FALSE
End Function

Sub CheckAmmoCollisions (xOffset%)
    Dim c%, i%, o%, p%
    For o% = objectCount% - 1 To 0 Step -1
        For i% = ammoCount% - 1 To 0 Step -1
            If SpriteCollision%(ammo(i%), object(o%).sprite) Then DestroyObject o%: DestroyAmmo (i%): Exit For
        Next i%
    Next o%
    For i% = ammoCount% - 1 To 0 Step -1
        c% = Int(((ammo(i%).position.x% + spriteData(ammo(i%).spriteId%).hitbox.cx%) + xOffset%) / TILE_WIDTH)
        p% = ammo(i%).position.y% + spriteData(ammo(i%).spriteId%).hitbox.cy%
        If p% - 2 < column(c%).top% * TILE_HEIGHT Or p% + 2 > column(c%).bottom% * TILE_HEIGHT Then DestroyAmmo (i%)
    Next i%
End Sub

Function SpriteCollision% (s1 As SPRITE, s2 As SPRITE)
    Dim dx%, dy%
    dx% = Abs((s1.position.x% + spriteData(s1.spriteId).hitbox.cx%) - (s2.position.x% + spriteData(s2.spriteId).hitbox.cx%))
    dy% = Abs((s1.position.y% + spriteData(s1.spriteId).hitbox.cy%) - (s2.position.y% + spriteData(s2.spriteId).hitbox.cy%))
    SpriteCollision% = dx% < (spriteData(s1.spriteId).hitbox.w% + spriteData(s2.spriteId).hitbox.w%) / 2 And dy% < (spriteData(s1.spriteId).hitbox.h% + spriteData(s2.spriteId).hitbox.h%) / 2
End Function

'===== Spawned sprite handling ========================================================================================================================================================================

Sub SpawnSprite (spriteId%, p As POINT)
    SetSprite spawnedSprite(spawnedSpriteCount%), spriteId%, p.x%, p.y%
    spawnedSpriteCount% = spawnedSpriteCount% + 1
End Sub

Sub UpdateSpawnedSprites
    Dim i%, id%
    For i% = spawnedSpriteCount% - 1 To 0 Step -1
        spawnedSprite(i%).counter% = spawnedSprite(i%).counter% + 1
        id% = spawnedSprite(i%).spriteId%
        Select Case id%
            Case SPRITE_PLAYER_EXPLOSION: If spawnedSprite(i%).counter% = 112 Then RemoveSpawnedSprite i% Else spawnedSprite(i%).frame% = playerExplosionFrameOrder%(Int(spawnedSprite(i%).counter% / 16)): If (spawnedSprite(i%).counter% And 3) = 0 Then NextPalette
            Case SPRITE_MYSTERY_SCORE: If spawnedSprite(i%).counter% = 48 Then RemoveSpawnedSprite i%
            Case Else: If spawnedSprite(i%).counter% = 64 Then RemoveSpawnedSprite i% Else spawnedSprite(i%).frame% = Int(spawnedSprite(i%).counter% / 8) And 3
        End Select
    Next i%
End Sub

Sub RemoveSpawnedSprite (i%)
    spawnedSprite(i%) = spawnedSprite(spawnedSpriteCount% - 1)
    spawnedSpriteCount% = spawnedSpriteCount% - 1
End Sub

'===== Rendering utility code =========================================================================================================================================================================

Sub RenderSprite (s As SPRITE, yOffset%)
    Dim o%
    o% = spriteData(s.spriteId%).offset% + s.frame%
    _PutImage (s.position.x%, s.position.y% + yOffset%), spriteSheet&, , (spriteUV(o%).x%, spriteUV(o%).y%)-(spriteUV(o%).x% + spriteData(s.spriteId%).size.x% - 1, spriteUV(o%).y% + spriteData(s.spriteId%).size.y% - 1)
End Sub

Sub RenderImage (id%, f%, x%, y%)
    Dim o%
    o% = spriteData(id%).offset% + f%
    _PutImage (x%, y%), spriteSheet&, , (spriteUV(o%).x%, spriteUV(o%).y%)-(spriteUV(o%).x% + spriteData(id%).size.x% - 1, spriteUV(o%).y% + spriteData(id%).size.y% - 1)
End Sub

Sub RenderText (x%, y%, t$, c%)
    Dim i%, c$
    For i% = 0 To Len(t$) - 1
        c$ = Mid$(t$, i% + 1, 1)
        If c$ <> " " Then RenderImage SPRITE_TEXT, c% * Len(text$) + InStr(text$, c$) - 1, (x% + i%) * 8, y% * 8
    Next i%
End Sub

Sub SetPalette (p%)
    Dim i%
    For i% = 0 To 3
        _PaletteColor gPal%(i%), pal&(paletteOrder%(p%), i%), 0
    Next i%
    game.currentPalette% = p%
    game.progressPalette% = 0
End Sub

Sub NextPalette
    SetPalette (game.currentPalette% + 1) Mod 7
End Sub

'===== Game utility functions =========================================================================================================================================================================

Sub IncreaseScore (d%)
    game.score% = game.score% + d%
    If game.score% >= 10000 And game.score% - d% < 10000 Then game.lives% = game.lives% + 1
    If game.score% > game.hiscore% Then game.hiscore% = game.score%
End Sub

Sub PrepareForLevel
    objectCount% = 0
    spawnedSpriteCount% = 0
    ammoCount% = 0
    player.state% = PLAYER_SPAWNING
    player.fuelCounter% = player.fuelSpeed%
    player.fuel% = MAX_FUEL
    SetPalette 0
    game.scrollOffset% = 0
    game.dataIndex = stageDataOffset%(game.stage%)
    game.baseDestroyed% = FALSE
    DrawPreStageTerrain
    NextColumn
    UpdateScroll
    SetSprite player.sprite, SPRITE_PLAYER, 8, 40
    StopSfx SFX_FUEL_WARNING
    StopSfx SFX_ENGINE
End Sub

Function LifeLost%
    LifeLost% = (player.state% = PLAYER_EXPLODING) And spawnedSpriteCount% = 0
End Function

Sub BaseDefeated
    game.stage% = 0
    PrepareForLevel
    player.fuelSpeed% = player.fuelSpeed% - 1
    game.flagCount% = game.flagCount% + 1
End Sub

Sub SetGameState (s%)
    game.state% = s%
    game.frameCounter& = 0
    If s% = STATE_TITLE Then game.stage% = 0: player.fuelSpeed% = INITIAL_FUEL_SPEED
    If s% = STATE_STARTING_GAME Then game.lives% = 3: game.score% = 0: game.hiscore% = hiscores%(0): game.flagCount% = 1: PrepareForLevel: PlaySfx SFX_START_GAME
    If s% = STATE_GAME_OVER Then StopSfx SFX_FUEL_WARNING: StopSfx SFX_ENGINE: CheckScore
    If s% = STATE_REACHED_BASE Then StopSfx SFX_FUEL_WARNING: StopSfx SFX_ENGINE
End Sub

Sub SetSprite (s As SPRITE, id%, x%, y%)
    s.spriteId% = id%
    s.position.x% = x%
    s.position.y% = y%
    s.counter% = 0
    s.frame% = 0
End Sub

Sub InitialiseStageDataOffset
    Dim dataOffset%, stage%
    dataOffset% = 1
    stageDataOffset%(0) = dataOffset%
    stage% = 1
    Do Until stage% = NUM_STAGES
        dataOffset% = dataOffset% + 6
        If Asc(mapData$, dataOffset%) = 255 Then
            dataOffset% = dataOffset% + 1
            stageDataOffset%(stage%) = dataOffset%
            stage% = stage% + 1
        End If
    Loop
End Sub

Sub SpawnObjectFromData (positionY%, objectType%)
    If positionY% = 0 Or objectType% > 15 Then Exit Sub
    SpawnObject positionY%, Log(objectType%) / Log(2)
End Sub

Sub SpawnObject (positionY%, objectType%)
    SetSprite object(objectCount%).sprite, objectType%, NUM_COLUMNS * TILE_WIDTH, positionY% * TILE_HEIGHT
    object(objectCount%).inFlight% = FALSE
    objectCount% = objectCount% + 1
End Sub

Sub SpawnNonDataObjects
    If game.stage% = 1 Then
        If ((game.dataIndex% - stageDataOffset%(game.stage%)) / 6) Mod 10 = 0 And game.dataIndex% < stageDataOffset%(2) - NUM_COLUMNS * 6 Then SpawnObject 0, TYPE_UFO
    ElseIf game.stage% = 2 Then
        If ((game.dataIndex% - stageDataOffset%(game.stage%)) / 6) Mod 2 = 0 And game.dataIndex% < stageDataOffset%(3) - NUM_COLUMNS * 6 Then SpawnObject Rnd * (NUM_ROWS - 11) + 1, TYPE_METEOR
    End If
End Sub

Sub CheckScore
    Dim i%, j%
    game.highlightScore% = -1
    For i% = 0 To 9
        If game.score% > hiscores%(i%) Then
            For j% = 9 To i% + 1 Step -1
                hiscores%(j%) = hiscores%(j% - 1)
            Next j%
            hiscores%(i%) = game.score%
            game.highlightScore% = i%
            WriteHiscores
            Exit Sub
        End If
    Next i%
End Sub

'======================================================================================================================================================================================================
Reply
#26
(03-23-2023, 05:45 AM)madscijr Wrote:
(02-26-2023, 10:56 PM)RokCoder Wrote: Having finished Galaga I decided to try converting my all time favourite arcade game, Scramble. It's gone pretty well I think!

Controls are arrow keys to move and A/Z to fire and bomb.

The ZIP file contains scramble.bas along with a subfolder called assets which contains all the sound effects, graphics, etc. After building the project, the EXE must reside in the same folder as the BAS file. It accesses the assets folder relatively so won't find it if the EXE is in the wrong place.

Hope you have fun!

Great job on this, it is very faithful to the original. 

That meteor stage is HARD! If I was back in 6th grade, I would have already spent my entire summer's paper route money in one evening!

I realize the original game was played on a vertically oriented CRT, however on the PC, I find the screen orientation is a little claustrophobic. 

How difficult would it be for you to tweak a "widescreen" version that makes use of the full screen, and displays more of the game horizontally? 

PS And next, how about taking Scramble and moving it into 3 dimensions for... ZAXXON? 

Anyway, great job and thanks for sharing this! 

PS In order to not lose any more paper route money, I added some constants (lines 46-50) so I have a prayer at making it to the final stages of the game! Maybe adding some difficulty settings would make it enjoyable for young players, or those of us who are challenged in the reflexes department. 

Thanks for the nice feedback!

I just tested a theoretical approach to widening the screen and was pleasantly surprised that it worked perfectly. I mean, the intent was there when I wrote it with the constants but I hadn't tested it out. If you change SCREEN_WIDTH from 224 to 448 then you'll have a wide screen presentation of Scramble (albeit with scores, instructions, etc all off-centre). It wouldn't be much of a reach to get the HUD to centralise things as well but not something I'm likely to get around to doing. In a similar vein, you could use the actual full-screen dimensions for SCREEN_WIDTH and SCREEN_HEIGHT and it should work fine (as long as they're divisible by eight).

I was only aiming for a direct conversion. Options, difficulty settings, etc would all be nice to have but there have only been 29 downloads from the forum so it kinda seems a lot of work for likely a handful of people to play it. That said, please do feel free to improve things and add a PR to the github. I would love to see these things in there - I just don't see myself doing any more work on it.

I'm finishing off a few Unity projects right now but may well revisit QB64 in the future. I had in mind to do New Rally X if/when I get to that point but Zaxxon would be an interesting one, too. It depends if I can rip the graphics and layouts from somewhere. I just don't have the patience to put those together manually Smile
Reply
#27
(03-23-2023, 09:43 AM)RokCoder Wrote: I just tested a theoretical approach to widening the screen and was pleasantly surprised that it worked perfectly. I mean, the intent was there when I wrote it with the constants but I hadn't tested it out. If you change SCREEN_WIDTH from 224 to 448 then you'll have a wide screen presentation of Scramble (albeit with scores, instructions, etc all off-centre). It wouldn't be much of a reach to get the HUD to centralise things as well but not something I'm likely to get around to doing. In a similar vein, you could use the actual full-screen dimensions for SCREEN_WIDTH and SCREEN_HEIGHT and it should work fine (as long as they're divisible by eight).

Thanks for the info! I played around with widening the screen like you suggested, and added a couple other tweaks (code below):
  • Const MAX_BOMBS controls how many bombs the player can have at one time.
  • Const NUM_LIVES controls the # of lives the player starts out with.
  • The player can change the ship speed while playing, by pressing 1-5.
  • Constants KEYDOWN_SPEED_1, KEYDOWN_SPEED_2, etc. define the speed keys.
  • Constants X_SPEED_1, X_SPEED_2, Y_SPEED_1, Y_SPEED_2, etc. control the 5 speed levels.
  • Constants X_SPEED_START & Y_SPEED_START controls the ship's initial speed.
  • Const KEYDOWN_START controls the start game key (space).
  • The player can now press ESC to quit a game in progress, or exit the program if in attract mode.
  • Const KEYDOWN_QUIT controls the quit game / exit program key (escape).

(03-23-2023, 09:43 AM)RokCoder Wrote: I was only aiming for a direct conversion. Options, difficulty settings, etc would all be nice to have but there have only been 29 downloads from the forum so it kinda seems a lot of work for likely a handful of people to play it. That said, please do feel free to improve things and add a PR to the github. I would love to see these things in there - I just don't see myself doing any more work on it.

That makes sense - no worries. What's a PR? 

(03-23-2023, 09:43 AM)RokCoder Wrote: I'm finishing off a few Unity projects right now but may well revisit QB64 in the future. I had in mind to do New Rally X if/when I get to that point but Zaxxon would be an interesting one, too. It depends if I can rip the graphics and layouts from somewhere. I just don't have the patience to put those together manually Smile

The sprites and background are available here.


Here's the Scramble hack - the game is pretty fun and it wasn't too hard to modify... 

Code: (Select All)
'======================================================================================================================================================================================================
' SCRAMBLE
'------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
' Programmed by RokCoder
'------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
' Scramble is a side-scrolling shooter game released for arcades in 1981. It was
' developed by Konami, and manufactured and distributed by Leijac in Japan and
' Stern in North America.
' It was the first side-scrolling shooter with forced scrolling and multiple
' distinct levels, serving as a foundation for later side-scrolling shooters.
'
' This version is a tribute to the original programmed using QB64PE
'------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
' V0.1 - 26/02/2023 - First release
'------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
' https://github.com/rokcoder-qb64/scramble
'------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
' https://www.rokcoder.com
' https://www.github.com/rokcoder
' https://www.facebook.com/rokcoder
' https://www.youtube.com/rokcoder
'======================================================================================================================================================================================================

$VersionInfo:CompanyName=RokSoft
$VersionInfo:FileDescription=QB64 Scramble
$VersionInfo:InternalName=scramble.exe
$VersionInfo:ProductName=QB64 Scramble
$VersionInfo:OriginalFilename=scramble.exe
$VersionInfo:LegalCopyright=(c)2023 RokSoft
$VersionInfo:FILEVERSION#=0,1,0,0
$VersionInfo:PRODUCTVERSION#=0,1,0,0
$ExeIcon:'./assets/scramble.ico'

'======================================================================================================================================================================================================

Option _Explicit
Option _ExplicitArray

'======================================================================================================================================================================================================

Const FALSE = 0
Const TRUE = Not FALSE

' *****************************************************************************
' FOR THOSE OF US WITH POOR REFLEXES AND NEED TO STACK THE GAME IN THEIR FAVOR:
Const MAX_BOMBS = 12
Const BOMB_PAUSE = 2
Const FIRE_PAUSE = 2
Const NUM_LIVES = 3
Const X_SPEED_START = 1
Const X_SPEED_1 = 1
Const X_SPEED_2 = 2
Const X_SPEED_3 = 3
Const X_SPEED_4 = 4
Const X_SPEED_5 = 5
Const Y_SPEED_START = 1
Const Y_SPEED_1 = 1
Const Y_SPEED_2 = 2
Const Y_SPEED_3 = 3
Const Y_SPEED_4 = 4
Const Y_SPEED_5 = 5
' (MAYBE LATER ADD OPTIONS TO SLOW DOWN THE GAME OR THE ENEMIES?)
' *****************************************************************************

'Const SCREEN_WIDTH = 224
'Const SCREEN_WIDTH = 448
Const SCREEN_WIDTH = 800
'Const SCREEN_WIDTH = 1024
'Const SCREEN_WIDTH = 1360

'Const SCREEN_HEIGHT = 256
Const SCREEN_HEIGHT = 600
'Const SCREEN_HEIGHT = 768

'_Width(0)
'_Height(0)

Const NUM_STAGES = 6
Const TILE_WIDTH = 8
Const TILE_HEIGHT = 8
Const NUM_COLUMNS = Int(SCREEN_WIDTH / TILE_WIDTH)
Const NUM_ROWS = 25
Const GAME_HEIGHT = NUM_ROWS * TILE_HEIGHT

Const KEYDOWN_LEFT = 19200 ' left
Const KEYDOWN_RIGHT = 19712 ' right
Const KEYDOWN_UP = 18432 ' up
Const KEYDOWN_DOWN = 20480 ' down
Const KEYDOWN_FIRE = 97 ' a
Const KEYDOWN_BOMB = 122 ' z

' ADDED SOME ADDITIONAL KEYS:
Const KEYDOWN_SPEED_1 = 49
Const KEYDOWN_SPEED_2 = 50
Const KEYDOWN_SPEED_3 = 51
Const KEYDOWN_SPEED_4 = 52
Const KEYDOWN_SPEED_5 = 53
'Const KEYDOWN_SPEED_6 = 54
'Const KEYDOWN_SPEED_7 = 55
'Const KEYDOWN_SPEED_8 = 56
'Const KEYDOWN_SPEED_9 = 57

Const KEYDOWN_START = 32 ' space
Const KEYDOWN_QUIT = 27 ' escape

Const PLAYER_FLYING = 0
Const PLAYER_EXPLODING = 1
Const PLAYER_SPAWNING = 2

Const PLAYER_WIDTH = 32
Const PLAYER_HEIGHT = 16

Const MAX_FUEL = 112
Const INITIAL_FUEL_SPEED = 20
Const DELTA_FUEL_SPEED_PER_PASS = 2

Const AMMO_BULLET = 18
Const AMMO_BOMB = 8

Const TYPE_MISSILE = 0
Const TYPE_FUEL = 1
Const TYPE_MYSTERY = 2
Const TYPE_BASE = 3
Const TYPE_METEOR = 4
Const TYPE_UFO = 5

Const SPRITE_ROCKET = 0
Const SPRITE_FUEL = 1
Const SPRITE_MYSTERY = 2
Const SPRITE_BASE = 3
Const SPRITE_METEOR = 4
Const SPRITE_UFO = 5
Const SPRITE_PLAYER = 6
Const SPRITE_PLAYER_EXPLOSION = 7
Const SPRITE_BOMB = 8
Const SPRITE_BOMB_EXPLOSION = 9
Const SPRITE_UFO_EXPLOSION = 10
Const SPRITE_LIVE = 11
Const SPRITE_LEVEL_FLAG = 12
Const SPRITE_MYSTERY_SCORE = 13
Const SPRITE_OBJECT_EXPLOSION = 14
Const SPRITE_TERRAIN = 15
Const SPRITE_FUEL_BAR = 16
Const SPRITE_STAGE = 17
Const SPRITE_BULLET = 18
Const SPRITE_TEXT = 19

Const SFX_LASER = 0
Const SFX_FUEL_WARNING = 1
Const SFX_SMALL_EXPLOSION = 2
Const SFX_ENGINE = 3
Const SFX_ROCKET_EXPLOSION = 4
Const SFX_BOMB = 5
Const SFX_START_GAME = 6
Const SFX_EXPLOSION = 7

Const text$ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789?c.- "

Const TEXT_WHITE = 0
Const TEXT_RED = 1
Const TEXT_BLUE = 2
Const TEXT_YELLOW = 3
Const TEXT_PURPLE = 4

Const STATE_TITLE = 0
Const STATE_HIGHSCORES = 1
Const STATE_SCORETABLE = 2
Const STATE_DEMO = 3
Const STATE_STARTING_GAME = 4
Const STATE_PLAY = 5
Const STATE_GAME_OVER = 6
Const STATE_REACHED_BASE = 7
'Const STATE_QUIT = 8

'======================================================================================================================================================================================================

Type POINT
    x As Integer
    y As Integer
End Type

Type RECT
    x As Integer
    y As Integer
    w As Integer
    h As Integer
    cx As Integer
    cy As Integer
End Type

Type SPRITE
    spriteId As Integer
    position As POINT
    frame As Integer
    counter As Integer
End Type

Type SPRITEDATA
    offset As Integer
    size As POINT
    hitbox As RECT
End Type

Type PLAYER
    sprite As SPRITE
    state As Integer
    fuel As Integer
    fuelCounter As Integer
    fuelSpeed As Integer
    firePause As Integer
    firePressed As Integer
    bombPause As Integer
    bombPressed As Integer
End Type

Type OBJECT
    sprite As SPRITE
    inFlight As Integer
End Type

Type GAME
    frameCounter As Long
    fps As Integer
    dataIndex As Integer
    columnIndex As Integer
    stage As Integer
    currentPalette As Integer
    progressPalette As Integer
    score As Integer
    hiscore As Integer
    lives As Integer
    scrollOffset As Integer
    state As Integer
    highlightScore As Integer
    baseDestroyed As Integer
    flagCount As Integer
End Type

Type COLUMN
    texture As Long
    top As Integer
    bottom As Integer
End Type

Type SPAWNDATA
    x As Integer
    y As Integer
    count As Integer
End Type

Type STARS
    sprite0 As Long
    sprite1 As Long
    sprite2 As Long
    sprite3 As Long
    frame As Integer
    counter As Integer
End Type

'======================================================================================================================================================================================================

Dim Shared spriteSheet&
Dim Shared virtualScreen&
Dim Shared mapData$
Dim Shared game As GAME
Dim Shared spriteData(64) As SPRITEDATA
Dim Shared stageDataOffset%(6)
Dim Shared column(NUM_COLUMNS + 1) As COLUMN
Dim Shared ammo(32) As SPRITE
Dim Shared ammoCount%
Dim Shared object(16) As OBJECT
Dim Shared objectCount%
Dim Shared player As PLAYER
Dim Shared pal&(8, 4), gPal%(4)
Dim Shared tileU%(27), tileV%(27)
Dim Shared paletteOrder%(7)
Dim Shared spawnedSprite(32) As SPRITE
Dim Shared spawnedSpriteCount%
Dim Shared spriteUV(320) As POINT
Dim Shared sfx&(8)
Dim Shared stars As STARS
Dim Shared playerExplosionFrameOrder%(7)
Dim Shared place$(10)
Dim Shared placeColour%(10)
Dim Shared scoreTable$(6)
Dim Shared hiscores%(10)
Dim Shared Current_X_Speed%
Dim Shared Current_Y_Speed%

'===== Game loop ======================================================================================================================================================================================

PrepareScramble

Do: _Limit (game.fps%)
    UpdateFrame
    RenderFrame
Loop

'===== Error handling =================================================================================================================================================================================

fileReadError:
InitialiseHiscores
Resume Next

fileWriteError:
On Error GoTo 0
Resume Next

'===== One time initialisations =======================================================================================================================================================================

Sub PrepareScramble
    Dim m%, i%
    m% = Int((_DesktopHeight - 80) / SCREEN_HEIGHT)
    virtualScreen& = _NewImage(SCREEN_WIDTH, SCREEN_HEIGHT, 256)
    For i% = 0 To NUM_COLUMNS
        column(i%).texture& = _NewImage(TILE_WIDTH, SCREEN_HEIGHT, 256)
        _ClearColor _RGB(0, 0, 0), column(i%).texture&
    Next i%
    Screen _NewImage(SCREEN_WIDTH * m%, SCREEN_HEIGHT * m%, 256)
    _Delay 0.5

    _ScreenMove _Middle
    '_ScreenMove 0, 0

    '$RESIZE:STRETCH
    _AllowFullScreen _SquarePixels , _Smooth
    _Title "Scramble"
    _Dest virtualScreen&
    game.fps% = 60
    Randomize Timer
    game.frameCounter& = 0
    spriteSheet& = LoadImage&("sprite-sheet")
    _ClearColor _RGB(0, 0, 0), spriteSheet&
    stars.sprite0& = LoadImage&("stars-1")
    stars.sprite1& = LoadImage&("stars-2")
    stars.sprite2& = LoadImage&("stars-3")
    stars.sprite3& = LoadImage&("stars-4")
    _ClearColor _RGB(0, 0, 0), stars.sprite1&
    _ClearColor _RGB(0, 0, 0), stars.sprite2&
    _ClearColor _RGB(0, 0, 0), stars.sprite3&
    stars.frame% = 1
    LoadDataFromROM
    PrepareSprites
    ExtractPalettes
    ReadData
    InitialiseStageDataOffset
    LoadAllSFX
    ReadHiscores
    game.highlightScore% = -1
    game.hiscore% = hiscores%(0)
    SetGameState STATE_TITLE
End Sub

Sub PrepareSprites
    Dim i%, c%
    i% = 0
    SetSpriteataWithHitbox SPRITE_PLAYER, i%, 32, 16, 6, 2, 26, 12
    AddSpriteStrip i%, 1, 7, 4, 0, 18
    SetSpriteData SPRITE_PLAYER_EXPLOSION, i%, 32, 16
    AddSpriteStrip i%, 69, 7, 4, 0, 18
    SetSpriteataWithHitbox SPRITE_BOMB, i%, 16, 16, 6, 6, 4, 4
    AddSpriteStrip i%, 103, 7, 5, 0, 18
    SetSpriteData SPRITE_BOMB_EXPLOSION, i%, 16, 16
    AddSpriteStrip i%, 121, 7, 4, 0, 18
    SetSpriteData SPRITE_UFO_EXPLOSION, i%, 16, 16
    AddSpriteStrip i%, 139, 7, 4, 0, 18
    SetSpriteataWithHitbox SPRITE_METEOR, i%, 16, 16, 0, 3, 16, 10
    AddSpriteStrip i%, 157, 7, 4, 0, 18
    SetSpriteData SPRITE_LIVE, i%, 16, 8
    AddSpriteStrip i%, 1, 78, 1, 0, 0
    SetSpriteData SPRITE_LEVEL_FLAG, i%, 8, 8
    AddSpriteStrip i%, 19, 78, 1, 0, 0
    SetSpriteataWithHitbox SPRITE_UFO, i%, 16, 16, 2, 4, 12, 8
    AddSpriteStrip i%, 121, 79, 1, 0, 0
    SetSpriteataWithHitbox SPRITE_ROCKET, i%, 16, 16, 4, 0, 8, 16
    AddSpriteStrip i%, 1, 97, 3, 0, 18
    SetSpriteataWithHitbox SPRITE_BASE, i%, 16, 16, 0, 0, 16, 16
    AddSpriteStrip i%, 37, 97, 3, 0, 18
    SetSpriteData SPRITE_MYSTERY_SCORE, i%, 16, 16
    AddSpriteStrip i%, 73, 97, 3, 0, 18
    SetSpriteataWithHitbox SPRITE_MYSTERY, i%, 16, 16, 0, 0, 16, 16
    AddSpriteStrip i%, 91, 97, 1, 0, 0
    SetSpriteataWithHitbox SPRITE_FUEL, i%, 16, 16, 0, 0, 16, 16
    AddSpriteStrip i%, 91, 115, 1, 0, 0
    SetSpriteData SPRITE_OBJECT_EXPLOSION, i%, 16, 16
    AddSpriteStrip i%, 109, 97, 3, 0, 18
    AddSpriteStrip i%, 91, 133, 1, 0, 0
    SetSpriteData SPRITE_TERRAIN, i%, 8, 8
    AddSpriteStrip i%, 1, 151, 17, 10, 0
    AddSpriteStrip i%, 1, 161, 17, 10, 0
    SetSpriteData SPRITE_FUEL_BAR, i%, 8, 8
    AddSpriteStrip i%, 1, 171, 9, 10, 0
    SetSpriteData SPRITE_STAGE, i%, 32, 8
    AddSpriteStrip i%, 1, 191, 1, 0, 0
    AddSpriteStrip i%, 1, 181, 4, 32, 0
    AddSpriteStrip i%, 35, 191, 1, 0, 0
    AddSpriteStrip i%, 131, 181, 2, 34, 0
    SetSpriteataWithHitbox SPRITE_BULLET, i%, 8, 8, 3, 3, 2, 2
    AddSpriteStrip i%, 181, 201, 1, 0, 0
    SetSpriteData SPRITE_TEXT, i%, 8, 8
    For c% = 0 To 4
        AddSpriteStrip i%, 91, 201 + c% * 30, 9, 9, 0
        AddSpriteStrip i%, 1, 210 + c% * 30, 17, 9, 0
        AddSpriteStrip i%, 1, 201 + c% * 30, 10, 9, 0
        AddSpriteStrip i%, 1, 220 + c% * 30, 1, 9, 0
        AddSpriteStrip i%, 172, 201 + c% * 30, 2, 9, 0
        AddSpriteStrip i%, 154, 210 + c% * 30, 2, 9, 0
    Next c%
End Sub

Sub SetSpriteBasics (s%, i%, sw%, sh%)
    spriteData(s%).offset% = i%
    spriteData(s%).size.x% = sw%
    spriteData(s%).size.y% = sh%
End Sub

Sub SetSpriteData (s%, i%, sw%, sh%)
    SetSpriteBasics s%, i%, sw%, sh%
    SetRect spriteData(s%).hitbox, 0, 0, sw%, sh%
End Sub

Sub SetSpriteataWithHitbox (s%, i%, sw%, sh%, x%, y%, w%, h%)
    SetSpriteBasics s%, i%, sw%, sh%
    SetRect spriteData(s%).hitbox, x%, y%, w%, h%
End Sub

Sub AddSpriteStrip (spriteIndex%, u%, v%, n%, du%, dv%)
    Dim i%
    For i% = 1 To n%
        spriteUV(spriteIndex%).x% = u%
        spriteUV(spriteIndex%).y% = v%
        u% = u% + du%
        v% = v% + dv%
        spriteIndex% = spriteIndex% + 1
    Next i%
End Sub

Sub ExtractPalettes
    Dim i%, x%, y%
    _Source spriteSheet&
    i% = 0
    For y% = 1 To 4
        For x% = 0 To 7
            pal&(Int(i% / 4), i% Mod 4) = _PaletteColor(Point(127 + x% * 4, 97 + y% * 4), spriteSheet&)
            i% = i% + 1
        Next x%
    Next y%
    i% = 0
    For x% = 0 To 3
        gPal%(i%) = Point(127 + x% * 4, 97)
        i% = i% + 1
    Next x%
End Sub

Sub ReadData
    Dim i%
    For i% = 0 To 27: Read tileU%(i%): Next i%
    For i% = 0 To 27: Read tileV%(i%): Next i%
    For i% = 0 To 6: Read paletteOrder%(i%): Next i%
    For i% = 0 To 6: Read playerExplosionFrameOrder%(i%): Next i%
    For i% = 0 To 9: Read place$(i%): Next i%
    For i% = 0 To 9: Read placeColour%(i%): Next i%
    For i% = 0 To 5: Read scoreTable$(i%): Next i%
    Data 161,11,31,1,21,51,71,41,61,91,111,81,101,131,141,121,151,161,151,1,91,101,71,91,121,141,111,131
    Data 161,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,161,161,161,161,161,161,161,161,161,161
    Data 0,1,3,4,5,6,7
    Data 0,1,0,1,0,2,3
    Data "1ST","2ND","3RD","4TH","5TH","6TH","7TH","8TH","9TH","10TH"
    Data 3,3,3,2,2,2,4,4,4,4
    Data " ...  50 PTS     "," ...  80 PTS     "," ... 100 PTS     "," ... 150 PTS     "," ... 800 PTS     "," ... MYSTERY"
End Sub

Sub LoadDataFromROM
    Dim handle&
    handle& = FreeFile
    Open "assets/game-data.bin" For Binary As #handle& Len = 1
    mapData$ = Space$(LOF(handle&))
    Get #handle&, , mapData$
    Close #handle&
End Sub

'===== High score code ================================================================================================================================================================================

Sub ReadHiscores
    Dim i%, handle&
    On Error GoTo fileReadError
    If Not _FileExists("scores.txt") Then InitialiseHiscores: Exit Sub
    handle& = FreeFile
    Open "scores.txt" For Input As #handle&
    For i% = 0 To 9
        Input #handle&, hiscores%(i%)
    Next i%
    Close #handle&
    On Error GoTo 0
End Sub

Sub InitialiseHiscores
    Dim i%
    For i% = 0 To 9
        hiscores%(i%) = (10 - i%) * 1000
    Next i%
End Sub

Sub WriteHiscores
    Dim i%, handle&
    On Error GoTo fileWriteError
    handle& = FreeFile
    Open "scores.txt" For Output As #handle&
    For i% = 0 To 9
        Print #handle&, hiscores%(i%)
    Next i%
    Close #handle&
    On Error GoTo 0
End Sub

'===== Frame update functions =========================================================================================================================================================================

Sub UpdateFrame
    UpdateStars
    Select Case game.state%
        Case STATE_TITLE
            If game.frameCounter& > 8 * game.fps% Then SetGameState STATE_HIGHSCORES
        Case STATE_HIGHSCORES
            If game.frameCounter& > 8 * game.fps% Then SetGameState STATE_SCORETABLE
        Case STATE_SCORETABLE
            If game.frameCounter& > 8 * game.fps% Then PrepareForLevel: game.lives% = 1: SetGameState STATE_DEMO
        Case STATE_DEMO
            If player.state% = PLAYER_FLYING Then UpdateScroll
            UpdateObjects
            UpdatePlayer
            UpdateAmmo
            UpdateSpawnedSprites
            CalculateCollisions game.scrollOffset% And 7
            If LifeLost% Then SetGameState STATE_TITLE
        Case STATE_STARTING_GAME
            If game.frameCounter& > 2.5 * game.fps% Then SetGameState STATE_PLAY
        Case STATE_PLAY
            If player.state% = PLAYER_FLYING Then UpdateScroll
            UpdateObjects
            UpdatePlayer
            UpdateAmmo
            UpdateSpawnedSprites
            CalculateCollisions game.scrollOffset% And 7
            If LifeLost% Then
                If game.lives% = 0 Then SetGameState STATE_GAME_OVER Else PrepareForLevel
            End If
            If game.stage% = 5 Then
                If (game.dataIndex% = Len(mapData$) + 150 And game.baseDestroyed%) Or game.dataIndex% = 2 * Len(mapData$) - stageDataOffset%(game.stage%) + 150 Then SetGameState STATE_REACHED_BASE
            End If

            ' PLAYER CHANGES SPEED
            If _KeyDown(KEYDOWN_SPEED_1) Then
                Current_X_Speed% = X_SPEED_1
                Current_Y_Speed% = Y_SPEED_1
            ElseIf _KeyDown(KEYDOWN_SPEED_2) Then
                Current_X_Speed% = X_SPEED_2
                Current_Y_Speed% = Y_SPEED_2
            ElseIf _KeyDown(KEYDOWN_SPEED_3) Then
                Current_X_Speed% = X_SPEED_3
                Current_Y_Speed% = Y_SPEED_3
            ElseIf _KeyDown(KEYDOWN_SPEED_4) Then
                Current_X_Speed% = X_SPEED_4
                Current_Y_Speed% = Y_SPEED_4
            ElseIf _KeyDown(KEYDOWN_SPEED_5) Then
                Current_X_Speed% = X_SPEED_5
                Current_Y_Speed% = Y_SPEED_5
            End If

            ' PLAYER QUITS GAME
            If _KeyDown(KEYDOWN_QUIT) Then
                SetGameState STATE_GAME_OVER
                _KeyClear: '_Delay 1 ' CLEAR KEYBOARD BUFFER
            End If

        Case STATE_GAME_OVER
            If game.frameCounter& > 2 * game.fps% Then SetGameState STATE_HIGHSCORES
        Case STATE_REACHED_BASE
            If game.frameCounter& > 3 * game.fps% Then
                SetGameState STATE_PLAY
                If game.baseDestroyed% Then BaseDefeated Else PrepareForLevel
            End If
    End Select
End Sub

Sub UpdateScroll
    Dim i%
    game.scrollOffset% = game.scrollOffset% + 1
    If (game.scrollOffset% And 7) = 0 Then UpdateLandscape
    For i% = 0 To objectCount% - 1: object(i%).sprite.position.x% = object(i%).sprite.position.x% - 1: Next i%
    For i% = 0 To spawnedSpriteCount% - 1: spawnedSprite(i%).position.x% = spawnedSprite(i%).position.x% - 1: Next i%
End Sub

Sub UpdateFromVirtualScreen
    game.frameCounter& = game.frameCounter& + 1
    _PutImage , virtualScreen&, 0, (0, 0)-(SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1)
    _Display
End Sub

'===== Frame render functions =========================================================================================================================================================================

Sub RenderFrame
    RenderStars
    Select Case game.state%
        Case STATE_TITLE
            RenderTitle
            RenderStartKey
        Case STATE_HIGHSCORES
            RenderHighscores
            RenderStartKey
        Case STATE_SCORETABLE
            RenderScoreTable
            RenderStartKey
        Case STATE_DEMO
            RenderLandscape game.scrollOffset% And 7
            RenderObjects
            RenderPlayer
            RenderAmmo
            RenderSpawnedSprites
            RenderHud
            RenderStartKey
        Case STATE_STARTING_GAME
            RenderStartingGame
        Case STATE_PLAY
            RenderLandscape game.scrollOffset% And 7
            RenderObjects
            RenderPlayer
            RenderAmmo
            RenderSpawnedSprites
            RenderHud
        Case STATE_GAME_OVER
            RenderGameOver
        Case STATE_REACHED_BASE
            RenderReachedBase
    End Select
    UpdateFromVirtualScreen
End Sub

Sub RenderSpawnedSprites
    Dim i%
    For i% = spawnedSpriteCount% - 1 To 0 Step -1
        RenderSprite spawnedSprite(i%), 40
    Next i%
End Sub

Sub RenderHud
    Dim i%, d%
    Line (0, 0)-(SCREEN_WIDTH - 1, 39), 0, BF
    Line (0, 240)-(SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1), 0, BF
    RenderScore
    For i% = 0 To 5
        RenderImage SPRITE_STAGE, i%, 16 + i% * 32, 24
        RenderImage SPRITE_STAGE, 6 - (i% <= game.stage%), 16 + i% * 32, 32
    Next i%
    RenderText 3, 30, "FUEL", TEXT_YELLOW
    For i% = 0 To game.lives% - 1
        RenderImage SPRITE_LIVE, 0, 16 * i%, 248
    Next i%
    For i% = 0 To game.flagCount% - 1
        RenderImage SPRITE_LEVEL_FLAG, 0, 216 - 8 * i%, 248
    Next i%
    For i% = 0 To MAX_FUEL / 8 - 1
        d% = player.fuel% - i% * 8
        If d% < 0 Then d% = 0 Else If d% > 8 Then d% = 8
        RenderImage SPRITE_FUEL_BAR, 8 - d%, 64 + 8 * i%, 240
    Next i%
End Sub

Sub RenderScore
    Dim s$
    RenderText 3, 0, "1UP", TEXT_WHITE
    RenderText 9, 0, "HIGH SCORE", TEXT_WHITE
    s$ = LTrim$(Str$(game.score%))
    RenderText 7 - Len(s$), 1, s$, TEXT_YELLOW
    s$ = LTrim$(Str$(game.hiscore%))
    RenderText 17 - Len(s$), 1, s$, TEXT_YELLOW
End Sub

Sub RenderStartingGame
    RenderScore
    RenderText 9, 20, "PLAYER ONE", TEXT_WHITE
End Sub

Sub RenderGameOver
    RenderScore
    RenderText 9, 20, "PLAYER ONE", TEXT_WHITE
    RenderText 9, 22, "GAME  OVER", TEXT_WHITE
End Sub

Sub RenderTitle
    RenderScore
    RenderText 12, 6, "PLAY", TEXT_YELLOW
    RenderText 8, 9, "- SCRAMBLE -", TEXT_BLUE
    RenderText 3, 17, "HOW FAR CAN YOU INVADE", TEXT_RED
    RenderText 4, 20, "OUR SCRAMBLE SYSTEM ?", TEXT_RED
End Sub

Sub RenderStartKey
    Static counter&, pressed%
    If counter& Mod game.fps% < game.fps% * 0.8 Then
        RenderText 4, 31, "PRESS SPACE TO START, ESC TO QUIT", TEXT_WHITE
    End If
   
    ''If _KeyDown(32) Then pressed% = TRUE Else If pressed% = TRUE Then pressed% = FALSE: SetGameState STATE_STARTING_GAME
    'If _KeyDown(KEYDOWN_START) Then pressed% = TRUE Else If pressed% = TRUE Then pressed% = FALSE: SetGameState STATE_STARTING_GAME
   
    If _KeyDown(KEYDOWN_START) Then
        pressed% = TRUE
    ElseIf _KeyDown(KEYDOWN_QUIT) Then
        System
    Else
        If pressed% = TRUE Then
            pressed% = FALSE
            SetGameState STATE_STARTING_GAME
        End If
    End If
   
    counter& = counter& + 1
End Sub

Sub RenderHighscores
    Dim i%, s$, c%
    RenderScore
    RenderText 5, 4, "- SCORE RANKING -", TEXT_RED
    For i% = 0 To 9
        c% = placeColour%(i%)
        If i% = game.highlightScore% Then c% = TEXT_WHITE
        RenderText 6, 7 + i% * 2, place$(i%), c%
        s$ = LTrim$(Str$(hiscores%(i%))) + " PTS"
        RenderText 22 - Len(s$), 7 + i% * 2, s$, c%
    Next i%
End Sub

Sub RenderScoreTable
    Dim i%, j%, c%
    RenderScore
    SetPalette 0
    RenderText 7, 7, "- SCORE TABLE -", TEXT_YELLOW
    RenderImage SPRITE_ROCKET, 0, 8 * 8, 2 + 9 * 8
    RenderImage SPRITE_ROCKET, 2, 8 * 8, 2 + 12 * 8
    RenderImage SPRITE_UFO, 0, 8 * 8, 2 + 15 * 8
    RenderImage SPRITE_FUEL, 0, 8 * 8, 2 + 18 * 8
    RenderImage SPRITE_BASE, 0, 8 * 8, 2 + 21 * 8
    RenderImage SPRITE_MYSTERY, 0, 8 * 8, 2 + 24 * 8
    c% = Int(game.frameCounter& / (game.fps% / 16)) - 1
    For i% = 0 To 5
        For j% = 1 To Len(scoreTable$(i%))
            If c% > 0 Then RenderText 9 + j%, 10 + i% * 3, Mid$(scoreTable$(i%), j%, 1), TEXT_WHITE
            c% = c% - 1
        Next j%
    Next i%
End Sub

Sub RenderReachedBase
    RenderScore
    If game.baseDestroyed% Then
        RenderText 7, 12, "CONGRATULATIONS", TEXT_RED
        RenderText 2, 14, "YOU COMPLETED YOUR DUTIES", TEXT_YELLOW
        RenderText 2, 16, "GOOD LUCK NEXT TIME AGAIN", TEXT_BLUE
    Else
        RenderText 4, 12, "DISASTER - YOU FAILED", TEXT_RED
        RenderText 5, 14, "TO DESTROY THE BASE", TEXT_YELLOW
        RenderText 10, 16, "TRY AGAIN", TEXT_BLUE
    End If
End Sub

'===== Simple asset loading functions =================================================================================================================================================================

Sub AssetError (fname$)
    Screen 0
    Print "Unable to load "; fname$
    Print "Please make sure EXE is in same folder as scramble.bas"
    Print "(Set Run/Output EXE to Source Folder option in the IDE before compiling)"
    End
End Sub

Function LoadImage& (fname$)
    Dim asset&, f$
    f$ = "./assets/" + fname$ + ".png"
    asset& = _LoadImage(f$, 256)
    If asset& = -1 Then AssetError (f$)
    LoadImage& = asset&
End Function

Function SndOpen& (fname$)
    Dim asset&, f$
    f$ = "./assets/" + fname$
    asset& = _SndOpen(f$)
    If asset& = -1 Then AssetError (f$)
    SndOpen& = asset&
End Function

Sub SetRect (r As RECT, x%, y%, w%, h%)
    r.x% = x%
    r.y% = y%
    r.w% = w%
    r.h% = h%
    r.cx% = r.x% + r.w% / 2
    r.cy% = r.y% + r.h% / 2
End Sub

'===== Terrain code ===================================================================================================================================================================================

Sub DrawPreStageTerrain
    Dim dataOffset%, column%
    ResetLandscape
    dataOffset% = stageDataOffset%(game.stage%)
    For column% = 0 To NUM_COLUMNS - 1
        AddColumn column%, 0, 0, 20, 11, FillerType%
        game.columnIndex% = game.columnIndex% Xor 1
    Next column%
End Sub

Sub NextColumn
    'There's some magic code in here that loops the background for the final level
    Dim i%, o%, d%
    If game.dataIndex% < Len(mapData$) Then
        If Asc(mapData$, game.dataIndex%) = 255 Then
            If game.stage% < 5 Then
                game.stage% = game.stage% + 1
                game.dataIndex% = game.dataIndex% + 1
            End If
        End If
    End If
    i% = game.dataIndex%
    Do Until i% < Len(mapData$)
        i% = i% - (Len(mapData$) - stageDataOffset%(game.stage%))
    Loop
    AddColumn NUM_COLUMNS, Asc(mapData$, i%), Int(Asc(mapData$, i% + 1) / 2), Asc(mapData$, i% + 2), Int(Asc(mapData$, i% + 3) / 2), FillerType%
    o% = Asc(mapData$, i% + 4)
    d% = TRUE
    If o% = 8 Then
        If Asc(mapData$, i% - 2) = 8 Then d% = FALSE
    End If
    If d% Then SpawnObjectFromData o%, Asc(mapData$, i% + 5)
    SpawnNonDataObjects
    game.dataIndex% = game.dataIndex% + 6
End Sub

Sub UpdateLandscape
    ScrollLandscapeTiles
    NextColumn
    game.columnIndex% = game.columnIndex% Xor 1
    game.progressPalette% = game.progressPalette% + 1
    If game.progressPalette% = 64 Then NextPalette
End Sub

Sub RenderLandscape (offset%)
    Dim i%
    For i% = 0 To NUM_COLUMNS + 1
        _PutImage (i% * TILE_WIDTH - offset%, 40), column(i%).texture&, ,
    Next i%
End Sub

Sub AddColumn (column%, topTileY%, topTile%, bottomTileY%, bottomTile%, fillerTile%)
    Dim i%, handle&, o%
    i% = 0
    handle& = column(column%).texture&
    _Dest handle&
    Cls
    column(column%).top% = topTileY%
    column(column%).bottom% = bottomTileY%
    If topTile% > 0 Then
        Do Until i% = topTileY%
            _PutImage (0, i% * TILE_HEIGHT), spriteSheet&, handle&, (tileU%(fillerTile%), tileV%(fillerTile%))-(tileU%(fillerTile%) + 7, tileV%(fillerTile%) + 7)
            i% = i% + 1
        Loop
        If topTile% > 63 Then topTile% = topTile% - 44
        _PutImage (0, i% * TILE_HEIGHT), spriteSheet&, handle&, (tileU%(topTile%), tileV%(topTile%))-(tileU%(topTile%) + 7, tileV%(topTile%) + 7)
        i% = i% + 1
    End If
    i% = bottomTileY%
    If bottomTile% >= 33 Then
        'Dealing with the "KONAMI" text on the final level
        o% = spriteData(SPRITE_TEXT).offset% + bottomTile% - 33
        _PutImage (0, i% * TILE_HEIGHT), spriteSheet&, handle&, (spriteUV(o%).x%, spriteUV(o%).y%)-(spriteUV(o%).x% + spriteData(SPRITE_TEXT).size.x% - 1, spriteUV(o%).y% + spriteData(SPRITE_TEXT).size.y% - 1)
    Else
        _PutImage (0, i% * TILE_HEIGHT), spriteSheet&, handle&, (tileU%(bottomTile%), tileV%(bottomTile%))-(tileU%(bottomTile%) + 7, tileV%(bottomTile%) + 7)
    End If
    i% = i% + 1
    Do Until i% = NUM_ROWS
        _PutImage (0, i% * TILE_HEIGHT), spriteSheet&, handle&, (tileU%(fillerTile%), tileV%(fillerTile%))-(tileU%(fillerTile%) + 7, tileV%(fillerTile%) + 7)
        i% = i% + 1
    Loop
    _Dest virtualScreen&
End Sub

Sub ResetLandscape
    game.columnIndex = 0
End Sub

Sub ScrollLandscapeTiles
    Dim i%, columnCache As COLUMN
    columnCache = column(0)
    For i% = 0 To NUM_COLUMNS - 1
        column(i%) = column(i% + 1)
    Next i%
    column(NUM_COLUMNS) = columnCache
End Sub

Function FillerType%
    If game.stage% < 3 Then
        FillerType% = 14
    Else
        If game.columnIndex Mod 2 = 0 Then
            FillerType% = 16
        Else
            FillerType% = 19
        End If
    End If
End Function

'===== Player code ====================================================================================================================================================================================

Sub UpdatePlayer
    Select Case player.state%
        Case PLAYER_FLYING
            player.sprite.frame% = Int(game.frameCounter& / 8) Mod 3
            If player.fuel% > 0 Then
                player.fuelCounter% = player.fuelCounter% - 1
                If player.fuelCounter% = 0 Then
                    player.fuelCounter% = player.fuelSpeed%
                    player.fuel% = player.fuel% - 1
                    If player.fuel% = 20 Then
                        PlaySfxLooping SFX_FUEL_WARNING
                    End If
                End If
            End If
            If game.state% = STATE_PLAY Then
                If player.fuel% > 0 Then

                    'ORIGINAL = NORMAL SPEED:
                    'player.sprite.position.x% = player.sprite.position.x% + _KeyDown(KEYDOWN_LEFT) - _KeyDown(KEYDOWN_RIGHT)
                    'player.sprite.position.y% = player.sprite.position.y% + _KeyDown(KEYDOWN_UP) - _KeyDown(KEYDOWN_DOWN)

                    'V2 = FAST SPEED:
                    'player.sprite.position.x% = player.sprite.position.x% + (_KeyDown(KEYDOWN_LEFT) * X_SPEED) - (_KeyDown(KEYDOWN_RIGHT) * X_SPEED)
                    'player.sprite.position.y% = player.sprite.position.y% + (_KeyDown(KEYDOWN_UP) * Y_SPEED) - (_KeyDown(KEYDOWN_DOWN) * Y_SPEED)

                    'V3 = VARIABLE SPEED:
                    player.sprite.position.x% = player.sprite.position.x% + (_KeyDown(KEYDOWN_LEFT) * Current_X_Speed%) - (_KeyDown(KEYDOWN_RIGHT) * Current_X_Speed%)
                    player.sprite.position.y% = player.sprite.position.y% + (_KeyDown(KEYDOWN_UP) * Current_Y_Speed%) - (_KeyDown(KEYDOWN_DOWN) * Current_Y_Speed%)

                Else
                    ' OUT OF FUEL, SHIP FALLS FROM THE SKY!
                    player.sprite.position.y% = player.sprite.position.y% + 1
                End If

                ' CHECK MOVEMENT BOUNDARIES
                If player.sprite.position.x% < 8 Then
                    player.sprite.position.x% = 8

                    ' NEW BOUNDARY CHECKING:
                ElseIf player.sprite.position.x% > SCREEN_WIDTH - 32 Then
                    player.sprite.position.x% = SCREEN_WIDTH - 32

                    ' BOUNDARY CHECKING V2:
                    'ElseIf player.sprite.position.x% > SCREEN_WIDTH - 24 Then
                    '    player.sprite.position.x% = SCREEN_WIDTH - 24

                    ' ORIGINAL BOUNDARY CHECKING:
                    'ElseIf player.sprite.position.x% > SCREEN_WIDTH / 2 - 24 Then
                    '    player.sprite.position.x% = SCREEN_WIDTH / 2 - 24
                End If

                If player.sprite.position.y% < 0 Then
                    player.sprite.position.y% = 0
                ElseIf player.sprite.position.y% > GAME_HEIGHT - 16 Then
                    player.sprite.position.y% = GAME_HEIGHT - 16
                End If

                If player.firePause% = 0 Then
                    If _KeyDown(KEYDOWN_FIRE) Then
                        If Not player.firePressed% Then
                            player.firePressed% = TRUE
                            'player.firePause = 4
                            player.firePause = FIRE_PAUSE
                            PlaySfx SFX_LASER
                            CreateAmmo AMMO_BULLET
                        End If
                    Else
                        player.firePressed% = FALSE
                    End If
                Else
                    player.firePause% = player.firePause% - 1
                End If
                If player.bombPause% = 0 Then
                    If _KeyDown(KEYDOWN_BOMB) Then
                        If Not player.bombPressed% Then
                            player.bombPressed% = TRUE

                            'If BombCount% < 2 Then
                            If BombCount% < MAX_BOMBS Then
                                'player.bombPause = 4
                                player.bombPause = BOMB_PAUSE
                                PlaySfx SFX_BOMB
                                CreateAmmo AMMO_BOMB
                            End If
                        End If
                    Else
                        player.bombPressed% = FALSE
                    End If
                Else
                    player.bombPause% = player.bombPause% - 1
                End If
            End If

        Case PLAYER_SPAWNING
            player.state% = PLAYER_FLYING
            game.lives% = game.lives% - 1
            PlaySfxLooping SFX_ENGINE
    End Select
End Sub

Sub RenderPlayer
    If player.state% = PLAYER_FLYING Then RenderSprite player.sprite, 40
End Sub

Sub DestroyPlayer
    player.state% = PLAYER_EXPLODING
    SpawnSprite SPRITE_PLAYER_EXPLOSION, player.sprite.position: PlaySfx SFX_EXPLOSION
End Sub

'===== Game enemy object handling =====================================================================================================================================================================

Sub UpdateObjects
    Dim i%
    For i% = objectCount% - 1 To 0 Step -1
        object(i%).sprite.counter% = object(i%).sprite.counter% + 1
        Select Case object(i%).sprite.spriteId%
            Case TYPE_MISSILE: UpdateMissile (i%)
            Case TYPE_UFO: UpdateUfo (i%)
            Case TYPE_BASE: UpdateBase (i%)
            Case TYPE_METEOR: UpdateMeteor (i%)
        End Select
        If object(i%).sprite.position.x% < -15 Or object(i%).sprite.position.y% < -15 Then RemoveObject (i%)
    Next i%
End Sub

Sub UpdateMissile (i%)
    If object(i%).inFlight Then
        object(i%).sprite.position.y% = object(i%).sprite.position.y% - 1
        object(i%).sprite.frame% = 1 + (Int(object(i%).sprite.counter% / 8) And 1)
    Else
        If Not (game.stage% = 1 Or game.stage% = 2) Then
            If game.state% = STATE_DEMO Then
                If object(i%).sprite.position.x% = 40 Then object(i%).inFlight% = TRUE
            Else
                If object(i%).sprite.position.x% = NUM_COLUMNS * 4 Then
                    If Rnd < 0.25 Then object(i%).inFlight% = TRUE
                ElseIf object(i%).sprite.position.x% < NUM_COLUMNS * 4 Then
                    If Rnd < 0.01 Then object(i%).inFlight% = TRUE
                End If
            End If
        End If
    End If
End Sub

Sub UpdateBase (i%)
    object(i%).sprite.frame% = Int(object(i%).sprite.counter% / 8) Mod 3
End Sub

Sub UpdateMeteor (i%)
    object(i%).sprite.position.x% = object(i%).sprite.position.x% - 3
    object(i%).sprite.frame% = Int(object(i%).sprite.counter% / 8) And 3
End Sub

Sub UpdateUfo (i%)
    object(i%).sprite.position.y% = GAME_HEIGHT / 2 - 8 + 32 * Sin(_D2R(object(i%).sprite.counter% * 6))
End Sub

Sub RenderObjects
    Dim i%
    For i% = 0 To objectCount% - 1
        RenderSprite object(i%).sprite, 40
    Next i%
End Sub

Sub RemoveObject (i%)
    object(i%) = object(objectCount% - 1)
    objectCount% = objectCount% - 1
End Sub

Sub DestroyObject (i%)
    Dim d%
    Select Case object(i%).sprite.spriteId%
        Case TYPE_METEOR: Exit Sub
        Case TYPE_MISSILE: SpawnSprite SPRITE_OBJECT_EXPLOSION, object(i%).sprite.position: d% = 50 - 30 * object(i%).inFlight%: PlaySfx SFX_ROCKET_EXPLOSION
        Case TYPE_FUEL: SpawnSprite SPRITE_OBJECT_EXPLOSION, object(i%).sprite.position: d% = 150: player.fuel% = player.fuel% + 15: PlaySfx SFX_EXPLOSION: If player.fuel% > 20 Then StopSfx SFX_FUEL_WARNING: If player.fuel% > MAX_FUEL Then player.fuel% = MAX_FUEL
        Case TYPE_MYSTERY: d% = Int(Rnd * 3): SpawnSprite SPRITE_MYSTERY_SCORE, object(i%).sprite.position: spawnedSprite(spawnedSpriteCount% - 1).frame% = d%: d% = (d% + 1) * 100: PlaySfx SFX_EXPLOSION
        Case TYPE_BASE: SpawnSprite SPRITE_OBJECT_EXPLOSION, object(i%).sprite.position: d% = 800: PlaySfx SFX_EXPLOSION: game.baseDestroyed% = TRUE
        Case TYPE_UFO: SpawnSprite SPRITE_UFO_EXPLOSION, object(i%).sprite.position: d% = 100: PlaySfx SFX_ROCKET_EXPLOSION
    End Select
    If game.state% = STATE_PLAY Then IncreaseScore (d%)
    RemoveObject i%
End Sub

'===== Background stars ===============================================================================================================================================================================

Sub UpdateStars
    stars.counter% = stars.counter% + 1
    If stars.counter% >= game.fps% Then
        stars.counter% = 0
        stars.frame% = stars.frame% + 1
        If stars.frame% = 4 Then stars.frame% = 1
    End If
End Sub

Sub RenderStars
    _PutImage , stars.sprite0&
    Select Case stars.frame%
        Case 1: _PutImage , stars.sprite2&: _PutImage , stars.sprite3&
        Case 2: _PutImage , stars.sprite1&: _PutImage , stars.sprite3&
        Case 3: _PutImage , stars.sprite1&: _PutImage , stars.sprite2&
    End Select
End Sub

'===== Player's ammo ==================================================================================================================================================================================

Sub RenderAmmo
    Dim i%
    For i% = 0 To ammoCount% - 1
        RenderSprite ammo(i%), 40
    Next i%
End Sub

Sub UpdateAmmo
    Dim i%
    For i% = ammoCount% - 1 To 0 Step -1
        Select Case ammo(i%).spriteId%
            Case AMMO_BULLET:
                ammo(i%).position.x% = ammo(i%).position.x% + 4
                If ammo(i%).position.x% > SCREEN_WIDTH Then
                    ammo(i%) = ammo(ammoCount% - 1)
                    ammoCount% = ammoCount% - 1
                End If
            Case AMMO_BOMB:
                Select Case ammo(i%).counter%
                    Case 0: ammo(i%).frame% = 0
                    Case 4: ammo(i%).frame% = 1
                    Case 8: ammo(i%).frame% = 0
                    Case 16: ammo(i%).frame% = 2
                    Case 28: ammo(i%).frame% = 3
                    Case 40: ammo(i%).frame% = 4
                End Select
                Select Case ammo(i%).counter%
                    Case 0 To 15: ammo(i%).position.x% = ammo(i%).position.x% + 1
                    Case 16 To 27: ammo(i%).position.x% = ammo(i%).position.x% + 1: ammo(i%).position.y% = ammo(i%).position.y% + 1 - (ammo(i%).counter% And 1)
                    Case 28 To 39: ammo(i%).position.x% = ammo(i%).position.x% + (ammo(i%).counter% And 1): ammo(i%).position.y% = ammo(i%).position.y% + 1
                    Case Else: ammo(i%).position.y% = ammo(i%).position.y% + 1
                End Select
                ammo(i%).counter% = ammo(i%).counter% + 1
        End Select
    Next i%
End Sub

Sub DestroyAmmo (i%)
    If ammo(i%).spriteId% = AMMO_BOMB Then SpawnSprite SPRITE_BOMB_EXPLOSION, ammo(i%).position: PlaySfx SFX_SMALL_EXPLOSION: StopSfx SFX_BOMB
    ammo(i%) = ammo(ammoCount% - 1)
    ammoCount% = ammoCount% - 1
End Sub

Sub CreateAmmo (ammoType%)
    Select Case ammoType%
        Case AMMO_BULLET: SetSprite ammo(ammoCount%), ammoType%, player.sprite.position.x% + 28, player.sprite.position.y% + 4
        Case AMMO_BOMB: SetSprite ammo(ammoCount%), ammoType%, player.sprite.position.x% + 10, player.sprite.position.y% + 6
    End Select
    ammoCount% = ammoCount% + 1
End Sub

Function BombCount%
    Dim c%, i%
    For i% = 0 To ammoCount% - 1
        c% = c% - (ammo(i%).spriteId% = AMMO_BOMB)
    Next i%
    BombCount% = c%
End Function

'===== Sound manager ==================================================================================================================================================================================

Sub LoadSfx (sfx%, sfx$)
    sfx&(sfx%) = _SndOpen("assets/" + sfx$ + ".ogg")
    If sfx&(sfx%) = 0 Then AssetError sfx$
End Sub

Sub LoadAllSFX
    LoadSfx SFX_LASER, "laser"
    LoadSfx SFX_FUEL_WARNING, "fuel-warning"
    LoadSfx SFX_SMALL_EXPLOSION, "small-explosion"
    LoadSfx SFX_ENGINE, "engine"
    LoadSfx SFX_ROCKET_EXPLOSION, "rocket-explosion"
    LoadSfx SFX_BOMB, "bomb"
    LoadSfx SFX_START_GAME, "start-game"
    LoadSfx SFX_EXPLOSION, "explosion"
End Sub

Sub PlaySfx (sfx%)
    If Not game.state% = STATE_DEMO Then _SndPlay sfx&(sfx%)
End Sub

Sub PlaySfxLooping (sfx%)
    If Not game.state% = STATE_DEMO Then _SndLoop sfx&(sfx%)
End Sub

Sub StopSfx (sfx%)
    _SndStop sfx&(sfx%)
End Sub

Function IsPlayingSfx% (sfx%)
    IsPlayingSfx% = _SndPlaying(sfx&(sfx%))
End Function

'===== Collision detection ============================================================================================================================================================================

Sub CalculateCollisions (xOffset%)
    If player.state% = PLAYER_FLYING Then
        If CheckMapCollision%(xOffset%, player.sprite.position.x% + 8, player.sprite.position.y%, PLAYER_WIDTH - 8, PLAYER_HEIGHT) Then DestroyPlayer Else If CheckObjectCollision% Then DestroyPlayer
    End If
    CheckAmmoCollisions xOffset%
End Sub

Function CheckMapCollision% (xOffset%, x%, y%, w%, h%)
    Dim cLeft%, cRight%, cTop%, cBottom%, i%
    cLeft% = Int((x% + xOffset%) / TILE_WIDTH)
    cRight% = Int((x% + w% - 1 + xOffset%) / TILE_WIDTH)
    cTop% = Int(y% / TILE_HEIGHT)
    cBottom% = Int((y% + h% - 1) / TILE_HEIGHT)
    For i% = cLeft% To cRight%
        If cTop% < column(i%).top% Or cBottom% > column(i%).bottom% Then CheckMapCollision% = TRUE: Exit Function
    Next i%
    CheckMapCollision% = FALSE
End Function

Function CheckObjectCollision%
    Dim i%
    For i% = 0 To objectCount% - 1
        If SpriteCollision%(player.sprite, object(i%).sprite) Then DestroyObject i%: CheckObjectCollision% = TRUE: Exit Function
    Next i%
    CheckObjectCollision% = FALSE
End Function

Sub CheckAmmoCollisions (xOffset%)
    Dim c%, i%, o%, p%
    For o% = objectCount% - 1 To 0 Step -1
        For i% = ammoCount% - 1 To 0 Step -1
            If SpriteCollision%(ammo(i%), object(o%).sprite) Then DestroyObject o%: DestroyAmmo (i%): Exit For
        Next i%
    Next o%
    For i% = ammoCount% - 1 To 0 Step -1
        c% = Int(((ammo(i%).position.x% + spriteData(ammo(i%).spriteId%).hitbox.cx%) + xOffset%) / TILE_WIDTH)
        p% = ammo(i%).position.y% + spriteData(ammo(i%).spriteId%).hitbox.cy%
        If p% - 2 < column(c%).top% * TILE_HEIGHT Or p% + 2 > column(c%).bottom% * TILE_HEIGHT Then DestroyAmmo (i%)
    Next i%
End Sub

Function SpriteCollision% (s1 As SPRITE, s2 As SPRITE)
    Dim dx%, dy%
    dx% = Abs((s1.position.x% + spriteData(s1.spriteId).hitbox.cx%) - (s2.position.x% + spriteData(s2.spriteId).hitbox.cx%))
    dy% = Abs((s1.position.y% + spriteData(s1.spriteId).hitbox.cy%) - (s2.position.y% + spriteData(s2.spriteId).hitbox.cy%))
    SpriteCollision% = dx% < (spriteData(s1.spriteId).hitbox.w% + spriteData(s2.spriteId).hitbox.w%) / 2 And dy% < (spriteData(s1.spriteId).hitbox.h% + spriteData(s2.spriteId).hitbox.h%) / 2
End Function

'===== Spawned sprite handling ========================================================================================================================================================================

Sub SpawnSprite (spriteId%, p As POINT)
    SetSprite spawnedSprite(spawnedSpriteCount%), spriteId%, p.x%, p.y%
    spawnedSpriteCount% = spawnedSpriteCount% + 1
End Sub

Sub UpdateSpawnedSprites
    Dim i%, id%
    For i% = spawnedSpriteCount% - 1 To 0 Step -1
        spawnedSprite(i%).counter% = spawnedSprite(i%).counter% + 1
        id% = spawnedSprite(i%).spriteId%
        Select Case id%
            Case SPRITE_PLAYER_EXPLOSION: If spawnedSprite(i%).counter% = 112 Then RemoveSpawnedSprite i% Else spawnedSprite(i%).frame% = playerExplosionFrameOrder%(Int(spawnedSprite(i%).counter% / 16)): If (spawnedSprite(i%).counter% And 3) = 0 Then NextPalette
            Case SPRITE_MYSTERY_SCORE: If spawnedSprite(i%).counter% = 48 Then RemoveSpawnedSprite i%
            Case Else: If spawnedSprite(i%).counter% = 64 Then RemoveSpawnedSprite i% Else spawnedSprite(i%).frame% = Int(spawnedSprite(i%).counter% / 8) And 3
        End Select
    Next i%
End Sub

Sub RemoveSpawnedSprite (i%)
    spawnedSprite(i%) = spawnedSprite(spawnedSpriteCount% - 1)
    spawnedSpriteCount% = spawnedSpriteCount% - 1
End Sub

'===== Rendering utility code =========================================================================================================================================================================

Sub RenderSprite (s As SPRITE, yOffset%)
    Dim o%
    o% = spriteData(s.spriteId%).offset% + s.frame%
    _PutImage (s.position.x%, s.position.y% + yOffset%), spriteSheet&, , (spriteUV(o%).x%, spriteUV(o%).y%)-(spriteUV(o%).x% + spriteData(s.spriteId%).size.x% - 1, spriteUV(o%).y% + spriteData(s.spriteId%).size.y% - 1)
End Sub

Sub RenderImage (id%, f%, x%, y%)
    Dim o%
    o% = spriteData(id%).offset% + f%
    _PutImage (x%, y%), spriteSheet&, , (spriteUV(o%).x%, spriteUV(o%).y%)-(spriteUV(o%).x% + spriteData(id%).size.x% - 1, spriteUV(o%).y% + spriteData(id%).size.y% - 1)
End Sub

Sub RenderText (x%, y%, t$, c%)
    Dim i%, c$
    For i% = 0 To Len(t$) - 1
        c$ = Mid$(t$, i% + 1, 1)
        If c$ <> " " Then RenderImage SPRITE_TEXT, c% * Len(text$) + InStr(text$, c$) - 1, (x% + i%) * 8, y% * 8
    Next i%
End Sub

Sub SetPalette (p%)
    Dim i%
    For i% = 0 To 3
        _PaletteColor gPal%(i%), pal&(paletteOrder%(p%), i%), 0
    Next i%
    game.currentPalette% = p%
    game.progressPalette% = 0
End Sub

Sub NextPalette
    SetPalette (game.currentPalette% + 1) Mod 7
End Sub

'===== Game utility functions =========================================================================================================================================================================

Sub IncreaseScore (d%)
    game.score% = game.score% + d%
    If game.score% >= 10000 And game.score% - d% < 10000 Then game.lives% = game.lives% + 1
    If game.score% > game.hiscore% Then game.hiscore% = game.score%
End Sub

Sub PrepareForLevel
    objectCount% = 0
    spawnedSpriteCount% = 0
    ammoCount% = 0
    player.state% = PLAYER_SPAWNING
    player.fuelCounter% = player.fuelSpeed%
    player.fuel% = MAX_FUEL
    SetPalette 0
    game.scrollOffset% = 0
    game.dataIndex = stageDataOffset%(game.stage%)
    game.baseDestroyed% = FALSE
    DrawPreStageTerrain
    NextColumn
    UpdateScroll
    SetSprite player.sprite, SPRITE_PLAYER, 8, 40
    StopSfx SFX_FUEL_WARNING
    StopSfx SFX_ENGINE
End Sub

Function LifeLost%
    LifeLost% = (player.state% = PLAYER_EXPLODING) And spawnedSpriteCount% = 0
End Function

Sub BaseDefeated
    game.stage% = 0
    PrepareForLevel
    player.fuelSpeed% = player.fuelSpeed% - 1
    game.flagCount% = game.flagCount% + 1
End Sub

Sub SetGameState (s%)
    game.state% = s%
    game.frameCounter& = 0
    If s% = STATE_TITLE Then
        game.stage% = 0
        player.fuelSpeed% = INITIAL_FUEL_SPEED
    End If
    If s% = STATE_STARTING_GAME Then
        'game.lives% = 3
        game.lives% = NUM_LIVES
        game.score% = 0
        game.hiscore% = hiscores%(0)
        game.flagCount% = 1
        PrepareForLevel
        PlaySfx SFX_START_GAME

        ' RESET SHIP SPEED:
        Current_X_Speed% = X_SPEED_START
        Current_Y_Speed% = Y_SPEED_START
    End If
    If s% = STATE_GAME_OVER Then
        StopSfx SFX_FUEL_WARNING
        StopSfx SFX_ENGINE
        CheckScore
    End If
    If s% = STATE_REACHED_BASE Then
        StopSfx SFX_FUEL_WARNING
        StopSfx SFX_ENGINE
    End If
End Sub

Sub SetSprite (s As SPRITE, id%, x%, y%)
    s.spriteId% = id%
    s.position.x% = x%
    s.position.y% = y%
    s.counter% = 0
    s.frame% = 0
End Sub

Sub InitialiseStageDataOffset
    Dim dataOffset%, stage%
    dataOffset% = 1
    stageDataOffset%(0) = dataOffset%
    stage% = 1
    Do Until stage% = NUM_STAGES
        dataOffset% = dataOffset% + 6
        If Asc(mapData$, dataOffset%) = 255 Then
            dataOffset% = dataOffset% + 1
            stageDataOffset%(stage%) = dataOffset%
            stage% = stage% + 1
        End If
    Loop
End Sub

Sub SpawnObjectFromData (positionY%, objectType%)
    If positionY% = 0 Or objectType% > 15 Then Exit Sub
    SpawnObject positionY%, Log(objectType%) / Log(2)
End Sub

Sub SpawnObject (positionY%, objectType%)
    SetSprite object(objectCount%).sprite, objectType%, NUM_COLUMNS * TILE_WIDTH, positionY% * TILE_HEIGHT
    object(objectCount%).inFlight% = FALSE
    objectCount% = objectCount% + 1
End Sub

Sub SpawnNonDataObjects
    If game.stage% = 1 Then
        If ((game.dataIndex% - stageDataOffset%(game.stage%)) / 6) Mod 10 = 0 And game.dataIndex% < stageDataOffset%(2) - NUM_COLUMNS * 6 Then SpawnObject 0, TYPE_UFO
    ElseIf game.stage% = 2 Then
        If ((game.dataIndex% - stageDataOffset%(game.stage%)) / 6) Mod 2 = 0 And game.dataIndex% < stageDataOffset%(3) - NUM_COLUMNS * 6 Then SpawnObject Rnd * (NUM_ROWS - 11) + 1, TYPE_METEOR
    End If
End Sub

Sub CheckScore
    Dim i%, j%
    game.highlightScore% = -1
    For i% = 0 To 9
        If game.score% > hiscores%(i%) Then
            For j% = 9 To i% + 1 Step -1
                hiscores%(j%) = hiscores%(j% - 1)
            Next j%
            hiscores%(i%) = game.score%
            game.highlightScore% = i%
            WriteHiscores
            Exit Sub
        End If
    Next i%
End Sub

'======================================================================================================================================================================================================
Reply
#28
Github: PR = pull request

never figured out what that means. Rolleyes

Otherwise:
public relations
Puerto Rico (but it could be PUR since Olympics insist terminally in three-letter country-name abbreviations)
people's republic (maybe not since the 1980's...)


(03-23-2023, 09:43 AM)RokCoder Wrote: I was only aiming for a direct conversion. Options, difficulty settings, etc would all be nice to have but there have only been 29 downloads from the forum so it kinda seems a lot of work for likely a handful of people to play it. That said, please do feel free to improve things and add a PR to the github. I would love to see these things in there - I just don't see myself doing any more work on it.

If they're not on Reddit or one of those other sites expected to have "ultimate social networking", rather than a posting forum like this one which is less personal, it's likely there are rather few "older geeks" around who have played video games on machines that swallowed one quarter after another. There also seems to be an aversion to the Linux "giants" -- those who were really small subordinates to big Unix bosses, and now they are eager to lord it over others (Linuxquestions enough said). There are few people 50 years old and older who have the precious memories that we do around here. Heart

Another thing is that somebody growing up toward year 2000 is most interested in a Wolfenstein/Doom remake -- far and above what was done by Namco, Capcom, Coleco, Nintendo and other companies like that. Otherwise it might be that:

MORTAL KOMBAT!

Sadly those children would consider MasterGy's latest efforts lame. Browse this site for a moment so you know what I mean:

https://wallpapercave.com

There are more children like that on Internet than those with grey and white hairs with our swallowers of quarters.
Reply
#29
(03-24-2023, 02:20 AM)mnrvovrfc Wrote: There are more children like that on Internet than those with grey and white hairs with our swallowers of quarters.

Sadly true. And on that note, I'm in Cambridge on Saturday and very much hoping to pop in to the Centre for Computing History. They even seem to have a festival for Japanese retro computing going on this weekend. Fun times!
Reply
#30
(03-24-2023, 08:17 AM)RokCoder Wrote:
(03-24-2023, 02:20 AM)mnrvovrfc Wrote: There are more children like that on Internet than those with grey and white hairs with our swallowers of quarters.

Sadly true. And on that note, I'm in Cambridge on Saturday and very much hoping to pop in to the Centre for Computing History. They even seem to have a festival for Japanese retro computing going on this weekend. Fun times!

Oooooh! Enjoy!

I wouldn't worry about how many people downloaded your game. 
If only 1 person downloads and enjoys it, then you have succeeded in making that one person happy. 
Numbers only matter when you're trying to sell things for a profit.
Reply




Users browsing this thread: 23 Guest(s)