Posts: 1,002
Threads: 50
Joined: May 2022
Reputation:
27
03-01-2023, 06:43 PM
(This post was last modified: 03-01-2023, 06:43 PM by Kernelpanic.)
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:
Posts: 296
Threads: 10
Joined: Apr 2022
Reputation:
6
I found Naalaa code
it is not very big ..so if anyone interested i will post it
Posts: 3,978
Threads: 177
Joined: Apr 2022
Reputation:
220
(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 + ...
Posts: 301
Threads: 16
Joined: Apr 2022
Reputation:
51
I don't think there's a comparison, bplus
Posts: 733
Threads: 103
Joined: Apr 2022
Reputation:
14
(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
'======================================================================================================================================================================================================
Posts: 80
Threads: 7
Joined: Jan 2023
Reputation:
10
(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
RokCoder - dabbling in QB64pe for fun
Posts: 733
Threads: 103
Joined: Apr 2022
Reputation:
14
03-23-2023, 07:58 PM
(This post was last modified: 03-23-2023, 08:02 PM by madscijr.)
(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
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
'======================================================================================================================================================================================================
Posts: 1,586
Threads: 59
Joined: Jul 2022
Reputation:
52
03-24-2023, 02:20 AM
(This post was last modified: 03-24-2023, 02:28 AM by mnrvovrfc.
Edit Reason: Getting carried away with humor a bit
)
Github: PR = pull request
never figured out what that means.
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.
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.
Posts: 80
Threads: 7
Joined: Jan 2023
Reputation:
10
(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!
RokCoder - dabbling in QB64pe for fun
Posts: 733
Threads: 103
Joined: Apr 2022
Reputation:
14
(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.
|