'Cloned Shades - by @FellippeHeitor - fellippeheitor@gmail.com
'
'(a clone of 'Shades' which was originally developed by
'UOVO - http://www.uovo.dk/ - for iOS)
'
'The goal of this game is to use the arrow keys to choose where
'to lay the next block falling down. If you align 4 blocks of
'the same color shade horizontally, you erase a line. If you
'pile two identical blocks, they will merge to become darker,
'unless they are already the darkest shade available (of 5).
'
'It has a tetris feeling to it, but it's not tetris at all.
'
'The idea is not original, since it's a clone, but I coded it
'from the ground up.
'
'Changes:
'- Beta 1
' - So far I got the game to work, but I'm running into issues
' trying to show scores on the screen, mainly because I relied
' on POINT to check whether I could move blocks down or not.
' - There's no interface except for the actual gameboard.
'
'- Beta 2
' - Been discarded. At some point everything was working well but
' I managed to screw it all up, to the point I made the decision
' to go back to beta 1 and start over. I like to mention it here
' because even failure can teach you a lesson, and this one is
' not one to forget.
'
'- Beta 3
' - Converted all audio files to .OGG for smaller files and faster
' loading.
' - Game window now has an appropriate icon, generated at runtime.
' - Block movement has been coded again, and now it doesn't rely
' on POINT to detect blocks touching each other, but it actually
' evaluates the current Y position. Like it should have been from
' the start, to be honest.
' - Redone the merge animation. Still looks the same, but it had to
' be redone after the new movement routines have been implemented.
' - Added a background image to the game board ("bg.png"), but uses
' a gray background in case it cannot be loaded for some reason.
' - Code is a bit easier to read, after I moved most of the main
' loop code into separate subroutines.
' - SCORES ON THE SCREEN!
'
' - Beta 4
' - Adaptative resolution when the desktop isn't at least 900px tall.
' - New shades, which are alternated every time a new game is started.
' - Visual intro, mimicking the original game's.
' - Improved game performance by selectively limiting the layering of
' images in SUB UpdateScreen.
' - Added a "danger mode" visual indication, by turning on a TIMER that
' overlays a shade of red over the game play, similar to a security
' alarm.
' - Added a menu to start the game or change setting or leave.
' - Settings are now saved to "shades.dat", and include switches for
' sound and music, as well as a highscore.
' - Added an end screen, that shows the score, number of merges and
' number of lines destroyed during game.
'
' - Beta 5
' - Fixed game starting with the blocks that were put during the menu
' demonstration.
' - Fixed the 'danger' warning coming back from the previous game.
' - Added an option to select shades (GREEN, ORANGE, BLUE, PINK) or
' to have it AUTOmatically rotate everytime you start the game.
' (thanks to Clippy for suggesting it)
' - Added a confirmation message before clearing highscore.
' - Added a confirmation message before closing the game with ESC.
' - Fixed a bug with page _DEST that caused scores to be put in
' OverlayGraphics page instead of InfoScreen while InDanger
' triggered the ShowAlert sub.
'
' - Beta 6
' - ESC during the game shows a menu asking user to confirm QUIT or
' RESUME. The previous behaviour (in beta 5) was to quit to main
' menu after ESC was hit twice. Now ESC is interpreted as 'oops,
' I didn't mean to hit ESC'.
' - A new sound for when a line is destroyed ("line.ogg"); "wind.ogg"
' is no longer used/needed.
' - Added a sound to the game over screen ("gameover.ogg").
' - Fixed menu alignment issues when showing disabled choices.
' - Changed code to selectively NOT do what can't be done in MAC OS X
' (so that now we can compile in MAC OS X, tested in El Capitan).
' - Three lines of code changed to make the code backward compatible
' with QB64 v0.954 (SDL): BackgroundColor goes back to being a shared
' LONG instead of a CONST (since SDL won't allow _RGB32 in a CONST)
' and inside SUB CheckMerge we no longer clear the background using
' a patch from BgImage using _PUTIMAGE with STEP. Turns out the bug
' was in QB64, not in my code.
'
' - Beta 7
' - Fixed a bug that prevented the end screen to be shown because of
' the InDanger timer still being on.
' - Added three levels of difficulty, which affect gravity.
' - Since speed is different, a new soundtrack was added for faster
' modes ("Crowd_Hammer.ogg" and "Upbeat_Forever.ogg")
' - Added FILL mode (thanks to Pete for the suggestion), in which instead
' of an infinite game your goal is to fill the screen with blocks,
' avoiding merges at all costs, since they make you lose points (which
' is indicated by a "shock.ogg" sound and visual warning.
' - Added a new parameter to FUNCTION Menu, Info(), that holds descriptions
' for menu items. The goal is to be able to show highscores even
' before starting a game.
' - Clicking and dragging emulates keystrokes, allowing basic mouse
' controls (code courtesy of Steve McNeill - Thanks again!)
'
' - Version 1.0
' - Fixed: Disabling music through settings didn't stop the music. Duh.
' - Fixed: Shock sound during Fill mode sounded weird because it was being
' played inside the animation loop, and not once, before animation.
' - Fixed: BgMusic selection while in Fill mode.
' - Added: If player takes more than 3 seconds to choose a game mode,
' a brief description is shown on the screen.
' - Added: Quick end screen animation.
' - Countdown to game start with "GET READY" on the screen.
'
'Game constants -------------------------------------------------------
'General Use:
'Game definitions:
CONST NORMALINCREMENT
= 5 CONST FLASHINCREMENT
= 10
'Animations:
CONST TopAnimationSteps
= 15
'Colors:
'Menu actions:
CONST RESETHIGHSCORES
= 4
'Misc:
CONST FileID
= "CLONEDSHADES"
'Type definitions: ----------------------------------------------------
ID
AS STRING * 13 ' CLONEDSHADES + CHR$(# of the latest version/beta with new .dat format) ColorMode
AS LONG '0 = Automatic, 1 = Green, 2 = Orange, 3 = Blue, 4 = Pink
'Variables ------------------------------------------------------------
'Variables for game control:
'Variables for screen pages:
'Variable for sound:
'Other variables
'Screen initialization: ------------------------------------------------
'Default window size is 600x800. If the desktop resolution is smaller
'than 900px tall, resize the UI while keeping the aspect ratio.
UIWidth = 600
UIHeight = 800
UIWidth = UIHeight * .75
MainScreen
= _NEWIMAGE(UIWidth
, UIHeight
, 32)
BackgroundColor
= _RGB32(170, 170, 170)
'Coordinates for block locations in the board: ------------------------
InDanger = False
GameOver = False
GameEnded = False
'Read settings from file "shades.dat", if it exists: ------------------
'Invalid settings file or file doesn't exist: use defaults
Settings.ID
= FileID
+ CHR$(7) Settings.ColorMode = 0
Settings.SoundOn = True
Settings.MusicOn = True
Settings.HighscoreZEN = 0
Settings.HighscoreNORMAL = 0
Settings.HighscoreFLASH = 0
Settings.HighscoreFILL = 0
'RGB data for shades: --------------------------------------------------
SelectGlobalShade
'Since now we already have read the shades' rgb data,
'let's generate the window icon (Windows only):
PrepareIntro
'Load sounds: ---------------------------------------------------------
LoadAssets
Intro
NextShade
= _CEIL(RND * 3) 'Randomly chooses a shade for the next block
ON TIMER(AlertTimer
, .005) ShowAlert
UpdateScreen
'Main game loop: ------------------------------------------------------
_KEYCLEAR 'Clears keyboard buffer to avoid unwanted ESCs - Thanks, Steve. SelectGlobalShade
InitialIncrement = 1
Choices
(1) = "Cloned Shades" + CHR$(0) Choices
(2) = " " + CHR$(0) Choices(3) = "Classic Mode"
HighestOfHighest = Settings.HighscoreZEN
IF Settings.HighscoreNORMAL
> HighestOfHighest
THEN HighestOfHighest
= Settings.HighscoreNORMAL
IF Settings.HighscoreFLASH
> HighestOfHighest
THEN HighestOfHighest
= Settings.HighscoreFLASH
IF HighestOfHighest
> 0 THEN Info
(3) = "Best: " + TRIM$
(HighestOfHighest
) LINE (0, 0)-(319, 129), _RGBA32(255, 255, 255, 235), BF
Tip(1) = "Your goal in Classic Mode is to make"
Tip(2) = "as many points as you can by merging"
Tip(3) = "same color blocks and by creating"
Tip(4) = "lines of four blocks of the same shade."
Tip(5) = "There are three different skills to"
Tip(6) = "choose from: ZEN, NORMAL and FLASH."
Tip(8) = "Can you believe your eyes?"
Choices(4) = "Fill Mode"
IF Settings.HighscoreFILL
> 0 THEN Info
(4) = "Best: " + TRIM$
(Settings.HighscoreFILL
) LINE (0, 0)-(399, 129), _RGBA32(255, 255, 255, 235), BF
Tip(1) = "In Fill Mode you have to tweak your brain"
Tip(2) = "to do the opposite of what you did in Classic:"
Tip(3) = "now it's time to pile blocks up and avoid"
Tip(4) = "merging them at all costs. If you happen"
Tip(5) = "to forget your goal and end up connecting"
Tip(6) = "them, an electric response is triggered."
Tip(8) = "Do you have what it takes?"
Choices(5) = "Settings"
Choices
(6) = " " + CHR$(0) Choices(7) = "Quit"
Choice = Menu(3, 7, Choices(), Info(), Tips(), 3)
Choices
(1) = "SKILLS" + CHR$(0) Choices(2) = "Zen"
IF Settings.HighscoreZEN
> 0 THEN Info
(2) = "Best: " + TRIM$
(Settings.HighscoreZEN
) Choices(3) = "Normal"
IF Settings.HighscoreNORMAL
> 0 THEN Info
(3) = "Best: " + TRIM$
(Settings.HighscoreNORMAL
) Choices(4) = "Flash"
IF Settings.HighscoreFLASH
> 0 THEN Info
(4) = "Best: " + TRIM$
(Settings.HighscoreFLASH
) Choices
(5) = " " + CHR$(0) Choices(6) = "Go back"
GameMode = 1 'Default = Zen mode
SELECT CASE Menu
(2, 6, Choices
(), Info
(), Tips
(), 3) CASE 2: GameMode
= ZENMODE: InitialIncrement
= ZENINCREMENT
CASE 3: GameMode
= NORMALMODE: InitialIncrement
= NORMALINCREMENT
CASE 4: GameMode
= FLASHMODE: InitialIncrement
= FLASHINCREMENT
Score = 0
PreviousScore = -1
TotalMerges = 0
TotalLines = 0
RedrawBoard
InDanger = False
InGame = True
GenerateNewBlock
MoveBlock
CheckDanger
CheckMerge
CheckConnectedLines
InGame = False
CASE ZENMODE:
IF Settings.HighscoreZEN
< Score
THEN Settings.HighscoreZEN
= Score
CASE NORMALMODE:
IF Settings.HighscoreNORMAL
< Score
THEN Settings.HighscoreNORMAL
= Score
CASE FLASHMODE:
IF Settings.HighscoreFLASH
< Score
THEN Settings.HighscoreFLASH
= Score
ShowEndScreen
GameOver = False
GameEnded = False
'Fill mode is actually just a hack. We play in ZENMODE conditions, but the points are
'calculated differently. Also, DANGER mode displays a different message/color warning.
GameMode = FILLMODE
InitialIncrement = ZENINCREMENT
Score = 0
PreviousScore = -1
TotalMerges = 0
TotalLines = 0
RedrawBoard
ShowGetReady 3
InDanger = False
InGame = True
IF Settings.MusicOn
AND BgMusic
(ZENMODE
) THEN GenerateNewBlock
MoveBlock
CheckDanger
CheckMerge
CheckConnectedLines
InGame = False
IF Settings.HighscoreFILL
< Score
THEN Settings.HighscoreFILL
= Score
ShowEndScreen
GameOver = False
GameEnded = False
SettingChoice = 1
IF Settings.SoundOn
THEN Choices
(1) = "Sound: ON" ELSE Choices
(1) = "Sound: OFF" IF Settings.MusicOn
THEN Choices
(2) = "Music: ON" ELSE Choices
(2) = "Music: OFF" CASE 0: Choices
(3) = "Color: AUTO" CASE 1: Choices
(3) = "Color: GREEN" CASE 2: Choices
(3) = "Color: ORANGE" CASE 3: Choices
(3) = "Color: BLUE" CASE 4: Choices
(3) = "Color: PINK" Choices(4) = "Reset Highscores"
HighestOfHighest = Settings.HighscoreZEN
IF Settings.HighscoreNORMAL
> HighestOfHighest
THEN HighestOfHighest
= Settings.HighscoreNORMAL
IF Settings.HighscoreFLASH
> HighestOfHighest
THEN HighestOfHighest
= Settings.HighscoreFLASH
IF Settings.HighscoreFILL
> HighestOfHighest
THEN HighestOfHighest
= Settings.HighscoreFILL
IF HighestOfHighest
= 0 THEN Choices
(4) = Choices
(4) + CHR$(0)
Info(4) = "Can't be undone."
Choices(5) = "Return"
SettingChoice = Menu(SettingChoice, 5, Choices(), Info(), Tips(), 3)
Settings.SoundOn
= NOT Settings.SoundOn
Settings.MusicOn
= NOT Settings.MusicOn
Settings.ColorMode = Settings.ColorMode + 1
IF Settings.ColorMode
> 4 THEN Settings.ColorMode
= 0 SelectGlobalShade
Choices(1) = "Reset"
Choices(2) = "Cancel"
IF Menu
(1, 2, Choices
(), Info
(), Tips
(), 3) = 1 THEN Settings.HighscoreZEN = 0
Settings.HighscoreNORMAL = 0
Settings.HighscoreFLASH = 0
Settings.HighscoreFILL = 0
SettingChoice = SWITCHSOUND
QuitGame = True
DontSave:
Greens:
Oranges:
Blues:
Pinks:
BlockPositions:
RowCoordinates:
DATA 735,670,605,540,475,410,345,280,215,150,85,20
'SUBs and FUNCTIONs ----------------------------------------------------
'Randomly chooses where the next block will start falling down
CurrentShade = NextShade
'Randomly chooses the next shade. It is done at this point so
'that the "next" bar will be displayed correctly across the game screen.
'Block's Y coordinate starts offscreen
Y = -48: PrevY = Y
'Animate the birth of a new block:
LineSize = 600
LineStart = 0
LineEnd = 599
TargetLineStart = (CurrentColumn * BlockWidth) - BlockWidth
TargetLineEnd = CurrentColumn * BlockWidth
LeftSideIncrement = (TargetLineStart - LineStart) / TopAnimationSteps
RightSideIncrement = (LineEnd - TargetLineEnd) / TopAnimationSteps
FOR i
= 1 TO TopAnimationSteps
IF BgImage
< -1 THEN _PUTIMAGE (0, 0)-(599, 15), BgImage
, GameScreen
, (0, 0)-(599, 15) ELSE LINE (0, 0)-(599, 15), BackgroundColor
, BF
LINE (LineStart
, 0)-(LineEnd
, 15), Shade&
(CurrentShade
), BF
LineStart = LineStart + LeftSideIncrement
LineEnd = LineEnd - RightSideIncrement
UpdateScreen
IF BgImage
< -1 THEN _PUTIMAGE (0, 0)-(599, 15), BgImage
, GameScreen
, (0, 0)-(599, 15) ELSE LINE (0, 0)-(599, 15), BackgroundColor
, BF
FadeStep = 0
Increment = InitialIncrement
'Before moving the block using Increment, check if the movement will
'cause the block to move to another row. If so, check if such move will
'cause to block to be put down.
IF ConvertYtoRow
(Y
+ Increment
) <> ConvertYtoRow
(Y
) AND NOT AlignedWithRow
THEN Y = BlockRows(ConvertYtoRow(Y))
AlignedWithRow = True
Y = Y + Increment
AlignedWithRow = False
CurrentRow = ConvertYtoRow(Y)
IF Board
(CurrentRow
- 1, CurrentColumn
).State
THEN BlockPut
= True
BlockPut = True
IF GameMode
= FILLMODE
THEN Score
= Score
+ 5 ELSE Score
= Score
+ 2 Board(CurrentRow, CurrentColumn).State = True
Board(CurrentRow, CurrentColumn).Shade = CurrentShade
IF Board
(12, CurrentColumn
).State
= True
AND Board
(12, CurrentColumn
).Shade
<> Board
(11, CurrentColumn
).Shade
THEN GameOver = True
'Erase previous block put on screen
_PUTIMAGE (BlockPos
(CurrentColumn
), PrevY
)-(BlockPos
(CurrentColumn
) + BlockWidth
, PrevY
+ Increment
), BgImage
, GameScreen
, (BlockPos
(CurrentColumn
), PrevY
)-(BlockPos
(CurrentColumn
) + BlockWidth
, PrevY
+ Increment
) LINE (BlockPos
(CurrentColumn
), PrevY
)-(BlockPos
(CurrentColumn
) + BlockWidth
, PrevY
+ Increment
), BackgroundColor
, BF
PrevY = Y
'Show the next shade on the top bar unless in DemoMode
FadeStep = FadeStep + 1
LINE (0, 0)-(599, 15), _RGBA32(Shades
(NextShade
).R
, Shades
(NextShade
).G
, Shades
(NextShade
).B
, FadeStep
), BF
'Draw the current block
LINE (BlockPos
(CurrentColumn
), Y
)-STEP(BlockWidth
, BlockHeight
), Shade&
(CurrentShade
), BF
UpdateScreen
'Emulate arrow keys if mouse was clicked+held+moved on screen
'Code courtesy of Steve McNeill:
OldX = MX
OldY = MY
OldX = MX
OldY = MY
Increment = BlockHeight
IF Board
(CurrentRow
, CurrentColumn
- 1).State
= False
THEN IF BgImage
< -1 THEN _PUTIMAGE (BlockPos
(CurrentColumn
), Y
)-(BlockPos
(CurrentColumn
) + BlockWidth
, Y
+ BlockHeight
), BgImage
, GameScreen
, (BlockPos
(CurrentColumn
), Y
)-(BlockPos
(CurrentColumn
) + BlockWidth
, Y
+ BlockHeight
) ELSE LINE (BlockPos
(CurrentColumn
), Y
)-(BlockPos
(CurrentColumn
) + BlockWidth
, Y
+ BlockHeight
), BackgroundColor
, BF
CurrentColumn = CurrentColumn - 1
IF Board
(CurrentRow
, CurrentColumn
+ 1).State
= False
THEN IF BgImage
< -1 THEN _PUTIMAGE (BlockPos
(CurrentColumn
), Y
)-(BlockPos
(CurrentColumn
) + BlockWidth
, Y
+ BlockHeight
), BgImage
, GameScreen
, (BlockPos
(CurrentColumn
), Y
)-(BlockPos
(CurrentColumn
) + BlockWidth
, Y
+ BlockHeight
) ELSE LINE (BlockPos
(CurrentColumn
), Y
)-(BlockPos
(CurrentColumn
) + BlockWidth
, Y
+ BlockHeight
), BackgroundColor
, BF
CurrentColumn = CurrentColumn + 1
Choices(1) = "Quit"
Choices(2) = "Resume"
IF Menu
(1, 2, Choices
(), Info
(), Tips
(), 3) = 1 THEN GameEnded = True
RedrawBoard
'CASE " "
' GameOver = True
'Check if a block merge will be required:
Merged = False
AnimationLimit = 60 'Default for NORMALINCREMENT
CASE ZENINCREMENT: AnimationLimit
= 30 CASE FLASHINCREMENT: AnimationLimit
= 90
IF Board
(CurrentRow
, CurrentColumn
).Shade
= Board
(CurrentRow
- 1, CurrentColumn
).Shade
THEN 'Change block's color and the one touched to a darker shade, if it's not the darkest yet
IF GameMode
= FILLMODE
THEN Score
= Score
- 5 - CurrentShade
* 2 ELSE Score
= Score
+ CurrentShade
* 2 Merged = True
TotalMerges = TotalMerges + 1
i = CurrentShade
RStep = (Shades(i).R - Shades(i + 1).R) / MergeSteps
GStep = (Shades(i).G - Shades(i + 1).G) / MergeSteps
BStep = (Shades(i).B - Shades(i + 1).B) / MergeSteps
YStep = (BlockHeight) / MergeSteps
RToGo = Shades(i).R
GToGo = Shades(i).G
BToGo = Shades(i).B
ShrinkingHeight = BlockHeight * 2
RToGo = RToGo - RStep
GToGo = GToGo - GStep
BToGo = BToGo - BStep
ShrinkingHeight = ShrinkingHeight - YStep
_PUTIMAGE (BlockPos
(CurrentColumn
), BlockRows
(CurrentRow
))-(BlockPos
(CurrentColumn
) + BlockWidth
, BlockRows
(CurrentRow
) + BlockHeight
* 2 + 1), BgImage
, GameScreen
, (BlockPos
(CurrentColumn
), BlockRows
(CurrentRow
))-(BlockPos
(CurrentColumn
) + BlockWidth
, BlockRows
(CurrentRow
) + BlockHeight
* 2 + 1) LINE (BlockPos
(CurrentColumn
), BlockRows
(CurrentRow
))-STEP(BlockWidth
, BlockHeight
* 2 + 1), BackgroundColor
, BF
'Draw the merging blocks:
LINE (BlockPos
(CurrentColumn
), BlockRows
(CurrentRow
) + (BlockHeight
* 2) - ShrinkingHeight
- 1)-STEP(BlockWidth
, ShrinkingHeight
+ 2), _RGB32(RToGo
, GToGo
, BToGo
), BF
InWatchOut = True
WatchOutColor
= NOT WatchOutColor
DangerMessage$ = "WATCH OUT!"
UpdateScreen
InWatchOut = False
Board(CurrentRow, CurrentColumn).State = False
Board(CurrentRow - 1, CurrentColumn).Shade = i + 1
CurrentRow = CurrentRow - 1
CurrentShade = CurrentShade + 1
Y = BlockRows(CurrentRow)
PrevY = Y
CheckDanger
'Check for connected lines with the same shade and
'compute the new score, besides generating the disappearing
'animation:
Matched = False
CurrentMatch = CheckMatchingLine%
Matched = True
IF GameMode
= FILLMODE
THEN Score
= Score
- 40 ELSE Score
= Score
+ 40
MatchLineStart = BlockRows(CurrentMatch) + BlockHeight \ 2
FOR i
= 1 TO BlockHeight \
2 _PUTIMAGE (0, MatchLineStart
- i
)-(599, MatchLineStart
+ i
), BgImage
, GameScreen
, (0, MatchLineStart
- i
)-(599, MatchLineStart
+ i
) LINE (0, MatchLineStart
- i
)-(599, MatchLineStart
+ i
), BackgroundColor
, BF
InWatchOut = True
WatchOutColor
= NOT WatchOutColor
DangerMessage$ = "ARE YOU CRAZY?!"
UpdateScreen
InWatchOut = False
DestroyLine CurrentMatch
TotalLines = TotalLines + 1
RedrawBoard
IF DemoMode
THEN DemoMode
= False
'Returns the row on the board through which the block is currently
'passing.
ConvertYtoRow = 12
ConvertYtoRow = 11
ConvertYtoRow = 10
ConvertYtoRow = 9
ConvertYtoRow = 8
ConvertYtoRow = 7
ConvertYtoRow = 6
ConvertYtoRow = 5
ConvertYtoRow = 4
ConvertYtoRow = 3
ConvertYtoRow = 2
ConvertYtoRow = 1
'Returns the column on the board being currently hovered
IF CurrentX
>= BlockPos
(1) AND CurrentX
< BlockPos
(2) THEN ConvertXtoCol = 1
ConvertXtoCol = 2
ConvertXtoCol = 3
ConvertXtoCol = 4
Shade&
= _RGB32(Shades
(CurrentShade
).R
, Shades
(CurrentShade
).G
, Shades
(CurrentShade
).B
)
a.s = Board(i, 1).State
b.s = Board(i, 2).State
c.s = Board(i, 3).State
d.s = Board(i, 4).State
a = Board(i, 1).Shade
b = Board(i, 2).Shade
c = Board(i, 3).Shade
d = Board(i, 4).Shade
CheckMatchingLine% = i
CheckMatchingLine% = 0
FOR i
= LineToDestroy
TO 11 Board(i, 1).State = Board(i + 1, 1).State
Board(i, 2).State = Board(i + 1, 2).State
Board(i, 3).State = Board(i + 1, 3).State
Board(i, 4).State = Board(i + 1, 4).State
Board(i, 1).Shade = Board(i + 1, 1).Shade
Board(i, 2).Shade = Board(i + 1, 2).Shade
Board(i, 3).Shade = Board(i + 1, 3).Shade
Board(i, 4).Shade = Board(i + 1, 4).Shade
Board(12, i).State = False
Board(12, i).Shade = 0
Board(12, i).State = False
Board(12, i).Shade = 0
StartY = BlockRows(i)
EndY = StartY + BlockHeight
IF Board
(i
, CurrentColumn
).State
= True
THEN LINE (BlockPos
(CurrentColumn
), StartY
)-(BlockPos
(CurrentColumn
) + BlockWidth
, EndY
), Shade&
(Board
(i
, CurrentColumn
).Shade
), BF
PreviousScore = Score
ScoreString
= "Score:" + STR$(Score
)
CASE ZENMODE: ModeHighScore
= Settings.HighscoreZEN
CASE NORMALMODE: ModeHighScore
= Settings.HighscoreNORMAL
CASE FLASHMODE: ModeHighScore
= Settings.HighscoreFLASH
CASE FILLMODE: ModeHighScore
= Settings.HighscoreFILL
'_FONT 16
PrintShadow
15, 15, ScoreString
, _RGB32(255, 255, 255)
PrintShadow
15, 32, "Highscore: " + TRIM$
(ModeHighScore
), _RGB32(255, 255, 255) PrintShadow
15, 32, "You beat the highscore!", _RGB32(255, 255, 255)
'Generates the icon that will be placed on the window title of the game
LINE (0, i
* (IconSize
/ 5) - (IconSize
/ 5))-(IconSize
, i
* (IconSize
/ 5)), Shade&
(i
), BF
'Checks if any block pile is 11 blocks high, which
'means danger, which means player needs to think faster,
'which means we'll make him a little bit more nervous by
'switching our soothing bg song to a fast paced circus
'like melody.
IF Board
(11, 1).State
OR Board
(11, 2).State
OR Board
(11, 3).State
OR Board
(11, 4).State
THEN InDanger = True
InDanger = False
'Loads sound files at startup.
LineSound
= _SNDOPEN("line.ogg", "SYNC") GameOverSound
= _SNDOPEN("gameover.ogg", "SYNC") Whistle
= _SNDOPEN("whistle.ogg", "SYNC,VOL")
ShockSound
= _SNDOPEN("shock.ogg", "SYNC")
IF NOT SplashSound
(i
) THEN SplashSound
(i
) = _SNDOPEN("water" + TRIM$
(i
) + ".ogg", "SYNC")
BgMusic
(1) = _SNDOPEN("Water_Prelude.ogg", "SYNC,VOL") BgMusic
(2) = _SNDOPEN("Crowd_Hammer.ogg", "SYNC,VOL") BgMusic
(3) = _SNDOPEN("Upbeat_Forever.ogg", "SYNC,VOL") BgMusic
(4) = _SNDOPEN("quick.ogg", "SYNC,VOL")
'Display the gamescreen, overlay and score layers
SUB PrintShadow
(x%
, y%
, Text$
, ForeColor&
) 'Shadow:
'Text:
IF Settings.ColorMode
= 0 THEN GlobalShade
= (GlobalShade
) MOD MaxShades
+ 1 GlobalShade = Settings.ColorMode
'The intro shows the board about to be cleared,
'which then happens after assets are loaded. The intro
'is generated using the game engine.
'DemoMode prevents sounds to be played
DemoMode = True
LoadingMessage$ = "Cloned Shades"
LoadingMessage$ = "loading..."
'Setup the board to show an "about to merge" group of blocks
'which will end up completing a dark line at the bottom.
Board(1, 1).State = True
Board(1, 1).Shade = 5
Board(1, 2).State = True
Board(1, 2).Shade = 5
Board(1, 3).State = True
Board(1, 3).Shade = 4
Board(1, 4).State = True
Board(1, 4).Shade = 5
Board(2, 3).State = True
Board(2, 3).Shade = 3
Board(3, 3).State = True
Board(3, 3).Shade = 2
Board(4, 3).State = True
Board(4, 3).Shade = 2
CurrentColumn = 3
CurrentRow = 4
CurrentShade = 2
Y = BlockRows(CurrentRow)
PrevY = Y
BlockPut = True
RedrawBoard
Board(4, 3).State = False
UpdateScreen
'The current board setup must have been prepared using PrepareIntro first.
'Use the game engine to show the intro:
CheckMerge
CheckConnectedLines
'Clear the "loading..." text
IF FadeLevel
> 100 THEN FadeLevel
= 0 FadeLevel = FadeLevel + 1
IF GameMode
= FILLMODE
THEN DangerMessage$
= "BE EXTRA CAREFUL!" ELSE DangerMessage$
= "DANGER!"
DemoMode = True: InMenu = True
Message$ = "GET READY"
UpdateScreen
DemoMode = False: InMenu = False
'Displays Choices() on the screen and lets the player choose one.
'Uses OverlayGraphics page to display options.
'Player must use arrow keys to make a choice then ENTER.
DemoMode = True
InMenu = True
Choice = CurrentChoice
ON TIMER(ChooseColorTimer
, 3.5) SelectGlobalShade
'Use the game engine while the menu is displayed, except while InGame:
GenerateNewBlock: BlockPut = False
MoveBlock
'TipTime has passed since the user selected the current choice, so
'if Tips(Choice) contains an image, it is _PUTIMAGEd on the screen.
MenuTip = Tips(Choice)
TipShown = True
Choice
= (Choice
) MOD MaxChoice
+ 1 TipShown = False
MenuTip = False
Choice
= (Choice
+ MaxChoice
- 2) MOD MaxChoice
+ 1 TipShown = False
MenuTip = False
ChoiceWasMade = True
MenuTip = False
ChoiceWasMade = True
Choice = MaxChoice
InMenu = False
DemoMode = False
MenuTip = False
Menu = Choice
ShowCurrentChoice:
'Choices ending with CHR$(0) are shown as unavailable/grey.
ItemShade = Shade&(5)
PrintShadow
(_WIDTH(OverlayGraphics
) \
2) - (_PRINTWIDTH("> " + Choices
(i
)) \
2), ThisItemY
, CHR$(16) + Choices
(i
), ItemShade
ItemShade
= _RGB32(255, 255, 255) ItemShade = Shade&(4)
PrintShadow
(_WIDTH(OverlayGraphics
) \
2) - (_PRINTWIDTH(Choices
(i
)) \
2), ThisItemY
, Choices
(i
), ItemShade
UpdateScreen
PreviousChoice = Choice
InDanger = False
IF GameMode
= FILLMODE
AND Score
> 0 THEN Message$
(1) = "YOU WIN!" ELSE Message$
(1) = "GAME OVER" Message$(3) = "Your score:"
Message$(4) = TRIM$(Score)
Message$(5) = "Merged blocks:"
Message$(6) = TRIM$(TotalMerges)
Message$(7) = "Lines destroyed:"
Message$(8) = TRIM$(TotalLines)
Message$(10) = "Press ENTER..."
MessageColor = Shade&(5)
PrintShadow
(_WIDTH(OverlayGraphics
) \
2) - (_PRINTWIDTH(Message$
(i
)) \
2), i
* 16, Message$
(i
), MessageColor