Author Topic: Cloned Shades  (Read 5280 times)

0 Members and 1 Guest are viewing this topic.

FellippeHeitor

  • Guest
Cloned Shades
« on: April 14, 2019, 11:26:17 pm »
A good while back I wrote this game which is a clone of a game called Shades by a mobile company called UOVO.

I had released it on [abandoned, outdated and now likely malicious qb64 dot net website - don’t go there] and it remained dormant since then. Today I finally got to add it to my GitHub repositories and I decided to post it here too.

Shades is a new take on Tetris, in which you have a falling block that must be placed in the best spot you can find. Once you align four blocks of the same shade you clear a line and get extra points.

The catch is that when you place a block on top of another of the same shade, they'll merge into a darker shade block. Well, you'll see it when you play it.

I'm placing the source code in the box below just for reference, but you will definitely need the resources in the zip file.

Grab the full version from the GitHub release page: https://github.com/FellippeHeitor/Cloned-Shades/releases/tag/v1.0 (binary for Windows included).

Code: QB64: [Select]
  1. 'Cloned Shades - by @FellippeHeitor - fellippeheitor@gmail.com
  2. '
  3. '(a clone of 'Shades' which was originally developed by
  4. 'UOVO - http://www.uovo.dk/ - for iOS)
  5. '
  6. 'The goal of this game is to use the arrow keys to choose where
  7. 'to lay the next block falling down. If you align 4 blocks of
  8. 'the same color shade horizontally, you erase a line. If you
  9. 'pile two identical blocks, they will merge to become darker,
  10. 'unless they are already the darkest shade available (of 5).
  11. '
  12. 'It has a tetris feeling to it, but it's not tetris at all.
  13. '
  14. 'The idea is not original, since it's a clone, but I coded it
  15. 'from the ground up.
  16. '
  17. 'Changes:
  18. '- Beta 1
  19. '       - So far I got the game to work, but I'm running into issues
  20. '         trying to show scores on the screen, mainly because I relied
  21. '         on POINT to check whether I could move blocks down or not.
  22. '       - There's no interface except for the actual gameboard.
  23. '
  24. '- Beta 2
  25. '       - Been discarded. At some point everything was working well but
  26. '         I managed to screw it all up, to the point I made the decision
  27. '         to go back to beta 1 and start over. I like to mention it here
  28. '         because even failure can teach you a lesson, and this one is
  29. '         not one to forget.
  30. '
  31. '- Beta 3
  32. '       - Converted all audio files to .OGG for smaller files and faster
  33. '         loading.
  34. '       - Game window now has an appropriate icon, generated at runtime.
  35. '       - Block movement has been coded again, and now it doesn't rely
  36. '         on POINT to detect blocks touching each other, but it actually
  37. '         evaluates the current Y position. Like it should have been from
  38. '         the start, to be honest.
  39. '       - Redone the merge animation. Still looks the same, but it had to
  40. '         be redone after the new movement routines have been implemented.
  41. '       - Added a background image to the game board ("bg.png"), but uses
  42. '         a gray background in case it cannot be loaded for some reason.
  43. '       - Code is a bit easier to read, after I moved most of the main
  44. '         loop code into separate subroutines.
  45. '       - SCORES ON THE SCREEN!
  46. '
  47. ' - Beta 4
  48. '       - Adaptative resolution when the desktop isn't at least 900px tall.
  49. '       - New shades, which are alternated every time a new game is started.
  50. '       - Visual intro, mimicking the original game's.
  51. '       - Improved game performance by selectively limiting the layering of
  52. '         images in SUB UpdateScreen.
  53. '       - Added a "danger mode" visual indication, by turning on a TIMER that
  54. '         overlays a shade of red over the game play, similar to a security
  55. '         alarm.
  56. '       - Added a menu to start the game or change setting or leave.
  57. '       - Settings are now saved to "shades.dat", and include switches for
  58. '         sound and music, as well as a highscore.
  59. '       - Added an end screen, that shows the score, number of merges and
  60. '         number of lines destroyed during game.
  61. '
  62. ' - Beta 5
  63. '       - Fixed game starting with the blocks that were put during the menu
  64. '         demonstration.
  65. '       - Fixed the 'danger' warning coming back from the previous game.
  66. '       - Added an option to select shades (GREEN, ORANGE, BLUE, PINK) or
  67. '         to have it AUTOmatically rotate everytime you start the game.
  68. '         (thanks to Clippy for suggesting it)
  69. '       - Added a confirmation message before clearing highscore.
  70. '       - Added a confirmation message before closing the game with ESC.
  71. '       - Fixed a bug with page _DEST that caused scores to be put in
  72. '         OverlayGraphics page instead of InfoScreen while InDanger
  73. '         triggered the ShowAlert sub.
  74. '
  75. ' - Beta 6
  76. '       - ESC during the game shows a menu asking user to confirm QUIT or
  77. '         RESUME. The previous behaviour (in beta 5) was to quit to main
  78. '         menu after ESC was hit twice. Now ESC is interpreted as 'oops,
  79. '         I didn't mean to hit ESC'.
  80. '       - A new sound for when a line is destroyed ("line.ogg"); "wind.ogg"
  81. '         is no longer used/needed.
  82. '       - Added a sound to the game over screen ("gameover.ogg").
  83. '       - Fixed menu alignment issues when showing disabled choices.
  84. '       - Changed code to selectively NOT do what can't be done in MAC OS X
  85. '         (so that now we can compile in MAC OS X, tested in El Capitan).
  86. '       - Three lines of code changed to make the code backward compatible
  87. '         with QB64 v0.954 (SDL): BackgroundColor goes back to being a shared
  88. '         LONG instead of a CONST (since SDL won't allow _RGB32 in a CONST)
  89. '         and inside SUB CheckMerge we no longer clear the background using
  90. '         a patch from BgImage using _PUTIMAGE with STEP. Turns out the bug
  91. '         was in QB64, not in my code.
  92. '
  93. ' - Beta 7
  94. '       - Fixed a bug that prevented the end screen to be shown because of
  95. '         the InDanger timer still being on.
  96. '       - Added three levels of difficulty, which affect gravity.
  97. '       - Since speed is different, a new soundtrack was added for faster
  98. '         modes ("Crowd_Hammer.ogg" and "Upbeat_Forever.ogg")
  99. '       - Added FILL mode (thanks to Pete for the suggestion), in which instead
  100. '         of an infinite game your goal is to fill the screen with blocks,
  101. '         avoiding merges at all costs, since they make you lose points (which
  102. '         is indicated by a "shock.ogg" sound and visual warning.
  103. '       - Added a new parameter to FUNCTION Menu, Info(), that holds descriptions
  104. '         for menu items. The goal is to be able to show highscores even
  105. '         before starting a game.
  106. '       - Clicking and dragging emulates keystrokes, allowing basic mouse
  107. '         controls (code courtesy of Steve McNeill - Thanks again!)
  108. '
  109. ' - Version 1.0
  110. '       - Fixed: Disabling music through settings didn't stop the music. Duh.
  111. '       - Fixed: Shock sound during Fill mode sounded weird because it was being
  112. '         played inside the animation loop, and not once, before animation.
  113. '       - Fixed: BgMusic selection while in Fill mode.
  114. '       - Added: If player takes more than 3 seconds to choose a game mode,
  115. '         a brief description is shown on the screen.
  116. '       - Added: Quick end screen animation.
  117. '       - Countdown to game start with "GET READY" on the screen.
  118. '
  119. $EXEICON:'./shades.ico'
  120. 'Game constants -------------------------------------------------------
  121. 'General Use:
  122. CONST False = 0
  123. CONST True = NOT False
  124.  
  125. 'Game definitions:
  126. CONST BlockWidth = 150
  127. CONST BlockHeight = 64
  128. CONST ZENINCREMENT = 1
  129. CONST NORMALINCREMENT = 5
  130. CONST FLASHINCREMENT = 10
  131. CONST ZENMODE = 1
  132. CONST NORMALMODE = 2
  133. CONST FLASHMODE = 3
  134. CONST FILLMODE = 4
  135.  
  136. 'Animations:
  137. CONST TopAnimationSteps = 15
  138. CONST MergeSteps = 32
  139.  
  140. 'Colors:
  141. CONST MaxShades = 4
  142.  
  143. 'Menu actions:
  144. CONST PLAYGAME = 3
  145. CONST PLAYFILL = 4
  146. CONST SETTINGSMENU = 5
  147. CONST LEAVEGAME = 7
  148. CONST SWITCHSOUND = 1
  149. CONST SWITCHMUSIC = 2
  150. CONST COLORSWITCH = 3
  151. CONST RESETHIGHSCORES = 4
  152. CONST MAINMENU = 5
  153.  
  154. 'Misc:
  155. CONST FileID = "CLONEDSHADES"
  156.  
  157. 'Type definitions: ----------------------------------------------------
  158. TYPE ColorRGB
  159.     R AS LONG
  160.     G AS LONG
  161.     B AS LONG
  162.  
  163. TYPE BoardType
  164.     State AS LONG
  165.     Shade AS LONG
  166.  
  167. TYPE SettingsFile
  168.     ID AS STRING * 13 '   CLONEDSHADES + CHR$(# of the latest version/beta with new .dat format)
  169.     ColorMode AS LONG '0 = Automatic, 1 = Green, 2 = Orange, 3 = Blue, 4 = Pink
  170.     SoundOn AS _BYTE
  171.     MusicOn AS _BYTE
  172.     HighscoreZEN AS LONG
  173.     HighscoreNORMAL AS LONG
  174.     HighscoreFLASH AS LONG
  175.     HighscoreFILL AS LONG
  176.  
  177. 'Variables ------------------------------------------------------------
  178. 'Variables for game control:
  179. DIM SHARED Board(1 TO 12, 1 TO 4) AS BoardType
  180. DIM SHARED Shades(1 TO 5) AS ColorRGB, FadeStep AS LONG
  181. DIM SHARED BlockPos(1 TO 4) AS LONG
  182. DIM SHARED BlockRows(1 TO 12) AS LONG, BgImage AS LONG
  183. DIM SHARED i AS LONG, Increment AS LONG
  184. DIM SHARED CurrentRow AS LONG, CurrentColumn AS LONG
  185. DIM SHARED BlockPut AS _BIT, Y AS LONG, PrevY AS LONG
  186. DIM SHARED CurrentShade AS LONG, NextShade AS LONG
  187. DIM SHARED AlignedWithRow AS _BIT, InDanger AS _BIT
  188. DIM SHARED GameOver AS _BIT, GameEnded AS _BIT
  189. DIM SHARED PreviousScore AS LONG, Score AS LONG
  190. DIM SHARED GlobalShade AS LONG, DemoMode AS _BIT
  191. DIM SHARED AlertTimer AS LONG, TotalMerges AS LONG
  192. DIM SHARED TotalLines AS LONG, Setting AS LONG
  193. DIM SHARED InGame AS _BYTE, InitialIncrement AS LONG
  194. DIM SHARED GameMode AS _BYTE, InWatchOut AS _BIT
  195.  
  196. 'Variables for screen pages:
  197. DIM SHARED InfoScreen AS LONG
  198. DIM SHARED OverlayGraphics AS LONG
  199. DIM SHARED GameScreen AS LONG
  200. DIM SHARED MenuTip AS LONG
  201. DIM SHARED MainScreen AS LONG
  202. DIM SHARED UIWidth AS LONG
  203. DIM SHARED UIHeight AS LONG
  204.  
  205. 'Variable for sound:
  206. DIM SHARED DropSound(1 TO 3) AS LONG, Alarm AS LONG
  207. DIM SHARED LineSound AS LONG, SplashSound(1 TO 4) AS LONG, Whistle AS LONG
  208. DIM SHARED BgMusic(1 TO 4) AS LONG, GameOverSound AS LONG
  209. DIM SHARED ShockSound AS LONG
  210.  
  211. 'Other variables
  212. DIM SHARED InMenu AS _BIT, QuitGame AS _BIT
  213. DIM SHARED Settings AS SettingsFile
  214. DIM SHARED BackgroundColor AS LONG
  215. DIM SettingChoice AS LONG
  216.  
  217. 'Screen initialization: ------------------------------------------------
  218. 'Default window size is 600x800. If the desktop resolution is smaller
  219. 'than 900px tall, resize the UI while keeping the aspect ratio.
  220. UIWidth = 600
  221. UIHeight = 800
  222. IF INSTR(_OS$, "WIN") THEN
  223.         UIHeight = _HEIGHT(_SCREENIMAGE) - 150
  224.         UIWidth = UIHeight * .75
  225.     END IF
  226.  
  227. InfoScreen = _NEWIMAGE(300, 400, 32)
  228. OverlayGraphics = _NEWIMAGE(150, 200, 32)
  229. GameScreen = _NEWIMAGE(600, 800, 32)
  230. MainScreen = _NEWIMAGE(UIWidth, UIHeight, 32)
  231.  
  232. BgImage = _LOADIMAGE("bg.png", 32)
  233. IF BgImage < -1 THEN _DONTBLEND BgImage
  234.  
  235. BackgroundColor = _RGB32(170, 170, 170)
  236.  
  237. SCREEN MainScreen
  238.  
  239. _TITLE "Cloned Shades"
  240.  
  241. IF BgImage < -1 THEN _PUTIMAGE , BgImage, MainScreen
  242.  
  243. 'Coordinates for block locations in the board: ------------------------
  244. RESTORE BlockPositions
  245. FOR i = 1 TO 4
  246.     READ BlockPos(i)
  247.  
  248. RESTORE RowCoordinates
  249. FOR i = 1 TO 12
  250.     READ BlockRows(i)
  251.  
  252. InDanger = False
  253. GameOver = False
  254. GameEnded = False
  255.  
  256. 'Read settings from file "shades.dat", if it exists: ------------------
  257. IF _FILEEXISTS("shades.dat") THEN
  258.     OPEN "shades.dat" FOR BINARY AS #1
  259.     GET #1, , Settings
  260.     CLOSE #1
  261.  
  262. IF Settings.ID <> FileID + CHR$(7) THEN
  263.     'Invalid settings file or file doesn't exist: use defaults
  264.     Settings.ID = FileID + CHR$(7)
  265.     Settings.ColorMode = 0
  266.     Settings.SoundOn = True
  267.     Settings.MusicOn = True
  268.     Settings.HighscoreZEN = 0
  269.     Settings.HighscoreNORMAL = 0
  270.     Settings.HighscoreFLASH = 0
  271.     Settings.HighscoreFILL = 0
  272.  
  273. 'RGB data for shades: --------------------------------------------------
  274. SelectGlobalShade
  275.  
  276. 'Since now we already have read the shades' rgb data,
  277. 'let's generate the window icon (Windows only):
  278. IF INSTR(_OS$, "WIN") THEN MakeIcon
  279.  
  280. PrepareIntro
  281.  
  282. 'Load sounds: ---------------------------------------------------------
  283. LoadAssets
  284.  
  285. Intro
  286.  
  287. NextShade = _CEIL(RND * 3) 'Randomly chooses a shade for the next block
  288.  
  289. AlertTimer = _FREETIMER
  290. ON TIMER(AlertTimer, .005) ShowAlert
  291. TIMER(AlertTimer) OFF
  292.  
  293. _DEST GameScreen
  294. IF BgImage < -1 THEN _PUTIMAGE , BgImage, GameScreen ELSE CLS , BackgroundColor
  295. UpdateScreen
  296.  
  297.  
  298. 'Main game loop: ------------------------------------------------------
  299.     _KEYCLEAR 'Clears keyboard buffer to avoid unwanted ESCs - Thanks, Steve.
  300.     SelectGlobalShade
  301.     ERASE Board
  302.     InitialIncrement = 1
  303.     REDIM Choices(1 TO 7) AS STRING
  304.     REDIM Info(1 TO 7) AS STRING
  305.     REDIM Tips(1 TO 7) AS LONG
  306.     REDIM Tip(1 TO 8) AS STRING
  307.  
  308.     Choices(1) = "Cloned Shades" + CHR$(0)
  309.     Choices(2) = " " + CHR$(0)
  310.     Choices(3) = "Classic Mode"
  311.     HighestOfHighest = Settings.HighscoreZEN
  312.     IF Settings.HighscoreNORMAL > HighestOfHighest THEN HighestOfHighest = Settings.HighscoreNORMAL
  313.     IF Settings.HighscoreFLASH > HighestOfHighest THEN HighestOfHighest = Settings.HighscoreFLASH
  314.     IF HighestOfHighest > 0 THEN Info(3) = "Best: " + TRIM$(HighestOfHighest)
  315.     Tips(3) = _NEWIMAGE(320, 130, 32)
  316.     _DEST Tips(3)
  317.     LINE (0, 0)-(319, 129), _RGBA32(255, 255, 255, 235), BF
  318.     LINE (0, 0)-(319, 129), _RGB32(0, 0, 0), B
  319.     Tip(1) = "Your goal in Classic Mode is to make"
  320.     Tip(2) = "as many points as you can by merging"
  321.     Tip(3) = "same color blocks and by creating"
  322.     Tip(4) = "lines of four blocks of the same shade."
  323.     Tip(5) = "There are three different skills to"
  324.     Tip(6) = "choose from: ZEN, NORMAL and FLASH."
  325.     Tip(8) = "Can you believe your eyes?"
  326.     FOR i = 1 TO UBOUND(Tip)
  327.         IF LEN(Tip(i)) THEN PrintShadow _WIDTH(Tips(3)) \ 2 - _PRINTWIDTH(Tip(i)) \ 2, (i - 1) * _FONTHEIGHT, Tip(i), _RGB32(0, 0, 0)
  328.     NEXT i
  329.     _DEST GameScreen
  330.  
  331.     Choices(4) = "Fill Mode"
  332.     IF Settings.HighscoreFILL > 0 THEN Info(4) = "Best: " + TRIM$(Settings.HighscoreFILL)
  333.     Tips(4) = _NEWIMAGE(400, 130, 32)
  334.     _DEST Tips(4)
  335.     LINE (0, 0)-(399, 129), _RGBA32(255, 255, 255, 235), BF
  336.     LINE (0, 0)-(399, 129), _RGB32(0, 0, 0), B
  337.     Tip(1) = "In Fill Mode you have to tweak your brain"
  338.     Tip(2) = "to do the opposite of what you did in Classic:"
  339.     Tip(3) = "now it's time to pile blocks up and avoid"
  340.     Tip(4) = "merging them at all costs. If you happen"
  341.     Tip(5) = "to forget your goal and end up connecting"
  342.     Tip(6) = "them, an electric response is triggered."
  343.     Tip(8) = "Do you have what it takes?"
  344.     FOR i = 1 TO UBOUND(Tip)
  345.         IF LEN(Tip(i)) THEN PrintShadow _WIDTH(Tips(4)) \ 2 - _PRINTWIDTH(Tip(i)) \ 2, (i - 1) * _FONTHEIGHT, Tip(i), _RGB32(0, 0, 0)
  346.     NEXT i
  347.     _DEST GameScreen
  348.  
  349.     Choices(5) = "Settings"
  350.     Choices(6) = " " + CHR$(0)
  351.     Choices(7) = "Quit"
  352.  
  353.     IF Settings.MusicOn AND BgMusic(1) THEN _SNDVOL BgMusic(1), .2: _SNDLOOP BgMusic(1)
  354.     Choice = Menu(3, 7, Choices(), Info(), Tips(), 3)
  355.     SELECT CASE Choice
  356.         CASE PLAYGAME
  357.             REDIM Choices(1 TO 6) AS STRING
  358.             REDIM Info(1 TO 6) AS STRING
  359.             REDIM Tips(1 TO 6) AS LONG
  360.  
  361.             Choices(1) = "SKILLS" + CHR$(0)
  362.             Choices(2) = "Zen"
  363.             IF Settings.HighscoreZEN > 0 THEN Info(2) = "Best: " + TRIM$(Settings.HighscoreZEN)
  364.             Choices(3) = "Normal"
  365.             IF Settings.HighscoreNORMAL > 0 THEN Info(3) = "Best: " + TRIM$(Settings.HighscoreNORMAL)
  366.             Choices(4) = "Flash"
  367.             IF Settings.HighscoreFLASH > 0 THEN Info(4) = "Best: " + TRIM$(Settings.HighscoreFLASH)
  368.             Choices(5) = " " + CHR$(0)
  369.             Choices(6) = "Go back"
  370.  
  371.             GameMode = 1 'Default = Zen mode
  372.             SELECT CASE Menu(2, 6, Choices(), Info(), Tips(), 3)
  373.                 CASE 2: GameMode = ZENMODE: InitialIncrement = ZENINCREMENT
  374.                 CASE 3: GameMode = NORMALMODE: InitialIncrement = NORMALINCREMENT
  375.                 CASE 4: GameMode = FLASHMODE: InitialIncrement = FLASHINCREMENT
  376.                 CASE 6: GameEnded = True
  377.             END SELECT
  378.  
  379.             ERASE Board
  380.             Score = 0
  381.             PreviousScore = -1
  382.             TotalMerges = 0
  383.             TotalLines = 0
  384.             NextShade = _CEIL(RND * 3)
  385.             RedrawBoard
  386.  
  387.             IF Settings.MusicOn AND BgMusic(1) THEN _SNDSTOP BgMusic(1)
  388.             IF NOT GameEnded THEN ShowGetReady 3
  389.             IF Settings.MusicOn AND BgMusic(GameMode) THEN _SNDVOL BgMusic(GameMode), .3: _SNDLOOP BgMusic(GameMode)
  390.  
  391.             InDanger = False
  392.             InGame = True
  393.             DO WHILE NOT GameOver AND NOT GameEnded
  394.                 GenerateNewBlock
  395.                 MoveBlock
  396.                 CheckDanger
  397.                 CheckMerge
  398.                 CheckConnectedLines
  399.             LOOP
  400.             InGame = False
  401.             IF BgMusic(GameMode) THEN _SNDSTOP BgMusic(GameMode)
  402.             IF BgMusic(4) THEN _SNDSTOP BgMusic(4)
  403.             IF GameOver THEN
  404.                 IF Settings.SoundOn AND GameOverSound THEN _SNDPLAYCOPY GameOverSound
  405.                 SELECT CASE GameMode
  406.                     CASE ZENMODE: IF Settings.HighscoreZEN < Score THEN Settings.HighscoreZEN = Score
  407.                     CASE NORMALMODE: IF Settings.HighscoreNORMAL < Score THEN Settings.HighscoreNORMAL = Score
  408.                     CASE FLASHMODE: IF Settings.HighscoreFLASH < Score THEN Settings.HighscoreFLASH = Score
  409.                 END SELECT
  410.                 ShowEndScreen
  411.             END IF
  412.             GameOver = False
  413.             GameEnded = False
  414.         CASE PLAYFILL
  415.             'Fill mode is actually just a hack. We play in ZENMODE conditions, but the points are
  416.             'calculated differently. Also, DANGER mode displays a different message/color warning.
  417.             GameMode = FILLMODE
  418.             InitialIncrement = ZENINCREMENT
  419.  
  420.             ERASE Board
  421.             Score = 0
  422.             PreviousScore = -1
  423.             TotalMerges = 0
  424.             TotalLines = 0
  425.             NextShade = _CEIL(RND * 3)
  426.             RedrawBoard
  427.  
  428.             IF Settings.MusicOn AND BgMusic(1) THEN _SNDSTOP BgMusic(1)
  429.             ShowGetReady 3
  430.             IF Settings.MusicOn AND BgMusic(ZENMODE) THEN _SNDVOL BgMusic(ZENMODE), .3: _SNDLOOP BgMusic(ZENMODE)
  431.  
  432.             InDanger = False
  433.             InGame = True
  434.             IF Settings.MusicOn AND BgMusic(ZENMODE) THEN
  435.                 IF BgMusic(1) THEN _SNDSTOP BgMusic(1)
  436.                 _SNDVOL BgMusic(ZENMODE), .3: _SNDLOOP BgMusic(ZENMODE)
  437.             END IF
  438.             DO WHILE NOT GameOver AND NOT GameEnded
  439.                 GenerateNewBlock
  440.                 MoveBlock
  441.                 CheckDanger
  442.                 CheckMerge
  443.                 CheckConnectedLines
  444.             LOOP
  445.             InGame = False
  446.             IF BgMusic(ZENMODE) THEN _SNDSTOP BgMusic(ZENMODE)
  447.             IF BgMusic(4) THEN _SNDSTOP BgMusic(4)
  448.             IF GameOver THEN
  449.                 IF Settings.SoundOn AND LineSound THEN _SNDPLAYCOPY LineSound
  450.                 IF Settings.HighscoreFILL < Score THEN Settings.HighscoreFILL = Score
  451.                 ShowEndScreen
  452.             END IF
  453.             GameOver = False
  454.             GameEnded = False
  455.         CASE SETTINGSMENU
  456.             SettingChoice = 1
  457.             DO
  458.                 REDIM Choices(1 TO 5) AS STRING
  459.                 REDIM Info(1 TO 5) AS STRING
  460.                 REDIM Tips(1 TO 5) AS LONG
  461.                 IF Settings.SoundOn THEN Choices(1) = "Sound: ON" ELSE Choices(1) = "Sound: OFF"
  462.                 IF Settings.MusicOn THEN Choices(2) = "Music: ON" ELSE Choices(2) = "Music: OFF"
  463.                 SELECT CASE Settings.ColorMode
  464.                     CASE 0: Choices(3) = "Color: AUTO"
  465.                     CASE 1: Choices(3) = "Color: GREEN"
  466.                     CASE 2: Choices(3) = "Color: ORANGE"
  467.                     CASE 3: Choices(3) = "Color: BLUE"
  468.                     CASE 4: Choices(3) = "Color: PINK"
  469.                 END SELECT
  470.                 Choices(4) = "Reset Highscores"
  471.                 HighestOfHighest = Settings.HighscoreZEN
  472.                 IF Settings.HighscoreNORMAL > HighestOfHighest THEN HighestOfHighest = Settings.HighscoreNORMAL
  473.                 IF Settings.HighscoreFLASH > HighestOfHighest THEN HighestOfHighest = Settings.HighscoreFLASH
  474.                 IF Settings.HighscoreFILL > HighestOfHighest THEN HighestOfHighest = Settings.HighscoreFILL
  475.                 IF HighestOfHighest = 0 THEN Choices(4) = Choices(4) + CHR$(0)
  476.  
  477.                 Info(4) = "Can't be undone."
  478.                 Choices(5) = "Return"
  479.  
  480.                 SettingChoice = Menu(SettingChoice, 5, Choices(), Info(), Tips(), 3)
  481.                 SELECT CASE SettingChoice
  482.                     CASE SWITCHSOUND
  483.                         Settings.SoundOn = NOT Settings.SoundOn
  484.                     CASE SWITCHMUSIC
  485.                         Settings.MusicOn = NOT Settings.MusicOn
  486.                         IF Settings.MusicOn THEN
  487.                             IF BgMusic(1) THEN _SNDLOOP BgMusic(1)
  488.                         ELSE
  489.                             IF BgMusic(1) THEN _SNDSTOP BgMusic(1)
  490.                         END IF
  491.                     CASE COLORSWITCH
  492.                         Settings.ColorMode = Settings.ColorMode + 1
  493.                         IF Settings.ColorMode > 4 THEN Settings.ColorMode = 0
  494.                         SelectGlobalShade
  495.                     CASE RESETHIGHSCORES
  496.                         REDIM Choices(1 TO 2) AS STRING
  497.                         REDIM Info(1 TO 2)
  498.                         REDIM Tips(1 TO 2) AS LONG
  499.                         Choices(1) = "Reset"
  500.                         Choices(2) = "Cancel"
  501.                         IF Menu(1, 2, Choices(), Info(), Tips(), 3) = 1 THEN
  502.                             Settings.HighscoreZEN = 0
  503.                             Settings.HighscoreNORMAL = 0
  504.                             Settings.HighscoreFLASH = 0
  505.                             Settings.HighscoreFILL = 0
  506.                             SettingChoice = SWITCHSOUND
  507.                         END IF
  508.                 END SELECT
  509.             LOOP UNTIL SettingChoice = MAINMENU
  510.         CASE LEAVEGAME
  511.             QuitGame = True
  512.     END SELECT
  513. LOOP UNTIL QuitGame
  514.  
  515. ON ERROR GOTO DontSave
  516. OPEN "shades.dat" FOR BINARY AS #1
  517. PUT #1, , Settings
  518.  
  519. DontSave:
  520.  
  521. Greens:
  522. DATA 245,245,204
  523. DATA 158,255,102
  524. DATA 107,204,51
  525. DATA 58,153,0
  526. DATA 47,127,0
  527.  
  528. Oranges:
  529. DATA 255,193,153
  530. DATA 255,162,102
  531. DATA 255,115,26
  532. DATA 230,89,0
  533. DATA 128,49,0
  534.  
  535. Blues:
  536. DATA 204,229,255
  537. DATA 128,190,255
  538. DATA 26,138,255
  539. DATA 0,87,179
  540. DATA 0,50,102
  541.  
  542. Pinks:
  543. DATA 255,179,255
  544. DATA 255,128,255
  545. DATA 255,26,255
  546. DATA 179,0,178
  547. DATA 77,0,76
  548.  
  549. BlockPositions:
  550. DATA 0,151,302,453
  551.  
  552. RowCoordinates:
  553. DATA 735,670,605,540,475,410,345,280,215,150,85,20
  554.  
  555. 'SUBs and FUNCTIONs ----------------------------------------------------
  556.  
  557. SUB GenerateNewBlock
  558.     DIM LineSize AS LONG
  559.     DIM LineStart AS LONG
  560.     DIM LineEnd AS LONG
  561.     DIM TargetLineStart AS LONG
  562.     DIM TargetLineEnd AS LONG
  563.     DIM LeftSideIncrement AS LONG
  564.     DIM RightSideIncrement AS LONG
  565.  
  566.     'Randomly chooses where the next block will start falling down
  567.     CurrentColumn = _CEIL(RND * 4)
  568.     CurrentShade = NextShade
  569.  
  570.     'Randomly chooses the next shade. It is done at this point so
  571.     'that the "next" bar will be displayed correctly across the game screen.
  572.     NextShade = _CEIL(RND * 3)
  573.  
  574.     'Block's Y coordinate starts offscreen
  575.     Y = -48: PrevY = Y
  576.  
  577.     IF DemoMode THEN EXIT SUB
  578.  
  579.     'Animate the birth of a new block:
  580.     IF Whistle AND Settings.SoundOn THEN
  581.         _SNDPLAYCOPY Whistle
  582.     END IF
  583.  
  584.     LineSize = 600
  585.     LineStart = 0
  586.     LineEnd = 599
  587.     TargetLineStart = (CurrentColumn * BlockWidth) - BlockWidth
  588.     TargetLineEnd = CurrentColumn * BlockWidth
  589.     LeftSideIncrement = (TargetLineStart - LineStart) / TopAnimationSteps
  590.     RightSideIncrement = (LineEnd - TargetLineEnd) / TopAnimationSteps
  591.  
  592.     FOR i = 1 TO TopAnimationSteps
  593.         _LIMIT 120
  594.         IF BgImage < -1 THEN _PUTIMAGE (0, 0)-(599, 15), BgImage, GameScreen, (0, 0)-(599, 15) ELSE LINE (0, 0)-(599, 15), BackgroundColor, BF
  595.         LINE (LineStart, 0)-(LineEnd, 15), Shade&(CurrentShade), BF
  596.         LineStart = LineStart + LeftSideIncrement
  597.         LineEnd = LineEnd - RightSideIncrement
  598.         IF INKEY$ <> "" THEN EXIT FOR
  599.         UpdateScreen
  600.     NEXT i
  601.     IF BgImage < -1 THEN _PUTIMAGE (0, 0)-(599, 15), BgImage, GameScreen, (0, 0)-(599, 15) ELSE LINE (0, 0)-(599, 15), BackgroundColor, BF
  602.  
  603. SUB MoveBlock
  604.     DIM MX AS LONG, MY AS LONG, MB AS LONG 'Mouse X, Y and Button
  605.  
  606.     DIM k$
  607.  
  608.     FadeStep = 0
  609.     Increment = InitialIncrement
  610.     IF NOT DemoMode THEN BlockPut = False
  611.  
  612.     DO: _LIMIT 60
  613.         'Before moving the block using Increment, check if the movement will
  614.         'cause the block to move to another row. If so, check if such move will
  615.         'cause to block to be put down.
  616.         IF ConvertYtoRow(Y + Increment) <> ConvertYtoRow(Y) AND NOT AlignedWithRow THEN
  617.             Y = BlockRows(ConvertYtoRow(Y))
  618.             AlignedWithRow = True
  619.         ELSE
  620.             Y = Y + Increment
  621.             AlignedWithRow = False
  622.         END IF
  623.  
  624.         CurrentRow = ConvertYtoRow(Y)
  625.  
  626.         IF AlignedWithRow THEN
  627.             IF CurrentRow > 1 THEN
  628.                 IF Board(CurrentRow - 1, CurrentColumn).State THEN BlockPut = True
  629.             ELSEIF CurrentRow = 1 THEN
  630.                 BlockPut = True
  631.             END IF
  632.         END IF
  633.  
  634.         IF BlockPut THEN
  635.             IF GameMode = FILLMODE THEN Score = Score + 5 ELSE Score = Score + 2
  636.             DropSoundI = _CEIL(RND * 3)
  637.             IF DropSound(DropSoundI) AND Settings.SoundOn AND NOT DemoMode THEN
  638.                 _SNDPLAYCOPY DropSound(DropSoundI)
  639.             END IF
  640.             Board(CurrentRow, CurrentColumn).State = True
  641.             Board(CurrentRow, CurrentColumn).Shade = CurrentShade
  642.         END IF
  643.  
  644.         IF Board(12, CurrentColumn).State = True AND Board(12, CurrentColumn).Shade <> Board(11, CurrentColumn).Shade THEN
  645.             GameOver = True
  646.             EXIT DO
  647.         END IF
  648.  
  649.         'Erase previous block put on screen
  650.         IF BgImage < -1 THEN
  651.             _PUTIMAGE (BlockPos(CurrentColumn), PrevY)-(BlockPos(CurrentColumn) + BlockWidth, PrevY + Increment), BgImage, GameScreen, (BlockPos(CurrentColumn), PrevY)-(BlockPos(CurrentColumn) + BlockWidth, PrevY + Increment)
  652.         ELSE
  653.             LINE (BlockPos(CurrentColumn), PrevY)-(BlockPos(CurrentColumn) + BlockWidth, PrevY + Increment), BackgroundColor, BF
  654.         END IF
  655.         PrevY = Y
  656.  
  657.         'Show the next shade on the top bar unless in DemoMode
  658.         IF FadeStep < 255 AND NOT DemoMode THEN
  659.             FadeStep = FadeStep + 1
  660.             LINE (0, 0)-(599, 15), _RGBA32(Shades(NextShade).R, Shades(NextShade).G, Shades(NextShade).B, FadeStep), BF
  661.         END IF
  662.  
  663.         'Draw the current block
  664.         LINE (BlockPos(CurrentColumn), Y)-STEP(BlockWidth, BlockHeight), Shade&(CurrentShade), BF
  665.  
  666.         UpdateScreen
  667.  
  668.         IF NOT DemoMode AND Increment < BlockHeight THEN k$ = INKEY$
  669.  
  670.         'Emulate arrow keys if mouse was clicked+held+moved on screen
  671.         'Code courtesy of Steve McNeill:
  672.         WHILE _MOUSEINPUT: WEND
  673.         STATIC OldX, OldY
  674.         MX = _MOUSEX: MY = _MOUSEY: MB = _MOUSEBUTTON(1)
  675.  
  676.  
  677.         IF MB THEN
  678.             IF ABS(OldX - MX) > 100 THEN
  679.                 IF OldX < MX THEN k$ = CHR$(0) + CHR$(77) ELSE k$ = CHR$(0) + CHR$(75)
  680.                 OldX = MX
  681.             END IF
  682.             IF ABS(OldY - MY) > 100 THEN
  683.                 IF OldY < MY THEN k$ = CHR$(0) + CHR$(80)
  684.                 OldY = MY
  685.             END IF
  686.         ELSE
  687.             OldX = MX
  688.             OldY = MY
  689.         END IF
  690.  
  691.         SELECT CASE k$
  692.             CASE CHR$(0) + CHR$(80) 'Down arrow
  693.                 Increment = BlockHeight
  694.             CASE CHR$(0) + CHR$(75) 'Left arrow
  695.                 IF CurrentColumn > 1 THEN
  696.                     IF Board(CurrentRow, CurrentColumn - 1).State = False THEN
  697.                         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
  698.                         CurrentColumn = CurrentColumn - 1
  699.                     END IF
  700.                 END IF
  701.             CASE CHR$(0) + CHR$(77) 'Right arrow
  702.                 IF CurrentColumn < 4 THEN
  703.                     IF Board(CurrentRow, CurrentColumn + 1).State = False THEN
  704.                         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
  705.                         CurrentColumn = CurrentColumn + 1
  706.                     END IF
  707.                 END IF
  708.             CASE CHR$(27)
  709.                 IF GameMode <> FILLMODE THEN
  710.                     IF BgMusic(GameMode) THEN _SNDSTOP BgMusic(GameMode)
  711.                 ELSE
  712.                     IF BgMusic(ZENMODE) THEN _SNDSTOP BgMusic(ZENMODE)
  713.                 END IF
  714.                 IF BgMusic(4) THEN _SNDSTOP BgMusic(4)
  715.                 REDIM Choices(1 TO 2) AS STRING
  716.                 REDIM Info(1 TO 2) AS STRING
  717.                 REDIM Tips(1 TO 2) AS LONG
  718.                 Choices(1) = "Quit"
  719.                 Choices(2) = "Resume"
  720.                 IF Menu(1, 2, Choices(), Info(), Tips(), 3) = 1 THEN
  721.                     GameEnded = True
  722.                 ELSE
  723.                     IF GameMode <> FILLMODE THEN
  724.                         IF Settings.MusicOn AND BgMusic(GameMode) AND NOT InDanger THEN _SNDLOOP BgMusic(GameMode)
  725.                         IF Settings.MusicOn AND BgMusic(4) AND InDanger THEN _SNDLOOP BgMusic(4)
  726.                     ELSE
  727.                         IF Settings.MusicOn AND BgMusic(ZENMODE) AND NOT InDanger THEN _SNDLOOP BgMusic(ZENMODE)
  728.                         IF Settings.MusicOn AND BgMusic(4) AND InDanger THEN _SNDLOOP BgMusic(4)
  729.                     END IF
  730.                 END IF
  731.                 RedrawBoard
  732.                 'CASE " "
  733.                 '    GameOver = True
  734.         END SELECT
  735.         IF DemoMode THEN EXIT SUB
  736.     LOOP UNTIL BlockPut OR GameEnded OR GameOver
  737.  
  738. SUB CheckMerge
  739.     'Check if a block merge will be required:
  740.     DIM YStep AS LONG, AnimationLimit AS LONG
  741.     DIM WatchOutColor AS _BIT, PreviousDest AS LONG
  742.     DIM DangerMessage$
  743.  
  744.     Merged = False
  745.  
  746.     AnimationLimit = 60 'Default for NORMALINCREMENT
  747.     SELECT CASE InitialIncrement
  748.         CASE ZENINCREMENT: AnimationLimit = 30
  749.         CASE FLASHINCREMENT: AnimationLimit = 90
  750.     END SELECT
  751.  
  752.     IF BlockPut AND CurrentRow > 1 THEN
  753.         DO
  754.             IF Board(CurrentRow, CurrentColumn).Shade = Board(CurrentRow - 1, CurrentColumn).Shade THEN
  755.                 'Change block's color and the one touched to a darker shade, if it's not the darkest yet
  756.                 IF GameMode = FILLMODE THEN Score = Score - 5 - CurrentShade * 2 ELSE Score = Score + CurrentShade * 2
  757.                 IF Score < 0 THEN Score = 0
  758.                 IF CurrentShade < 5 THEN
  759.                     Merged = True
  760.                     TotalMerges = TotalMerges + 1
  761.                     i = CurrentShade
  762.                     RStep = (Shades(i).R - Shades(i + 1).R) / MergeSteps
  763.                     GStep = (Shades(i).G - Shades(i + 1).G) / MergeSteps
  764.                     BStep = (Shades(i).B - Shades(i + 1).B) / MergeSteps
  765.                     YStep = (BlockHeight) / MergeSteps
  766.  
  767.                     RToGo = Shades(i).R
  768.                     GToGo = Shades(i).G
  769.                     BToGo = Shades(i).B
  770.  
  771.                     ShrinkingHeight = BlockHeight * 2
  772.  
  773.                     IF SplashSound(CurrentShade) AND Settings.SoundOn AND NOT DemoMode AND NOT GameMode = FILLMODE THEN
  774.                         _SNDPLAYCOPY SplashSound(CurrentShade)
  775.                     ELSEIF Settings.SoundOn AND GameMode = FILLMODE THEN
  776.                         IF ShockSound THEN _SNDPLAYCOPY ShockSound
  777.                     END IF
  778.  
  779.                     FOR Merge = 0 TO MergeSteps: _LIMIT AnimationLimit
  780.                         RToGo = RToGo - RStep
  781.                         GToGo = GToGo - GStep
  782.                         BToGo = BToGo - BStep
  783.  
  784.                         ShrinkingHeight = ShrinkingHeight - YStep
  785.  
  786.                         IF BgImage < -1 THEN
  787.                             _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)
  788.                         ELSE
  789.                             LINE (BlockPos(CurrentColumn), BlockRows(CurrentRow))-STEP(BlockWidth, BlockHeight * 2 + 1), BackgroundColor, BF
  790.                         END IF
  791.  
  792.                         'Draw the merging blocks:
  793.                         LINE (BlockPos(CurrentColumn), BlockRows(CurrentRow) + (BlockHeight * 2) - ShrinkingHeight - 1)-STEP(BlockWidth, ShrinkingHeight + 2), _RGB32(RToGo, GToGo, BToGo), BF
  794.                         IF GameMode = FILLMODE THEN
  795.                             InWatchOut = True
  796.                             PreviousDest = _DEST
  797.                             _DEST OverlayGraphics
  798.                             IF WatchOutColor THEN CLS , _RGB(255, 255, 0) ELSE CLS , _RGBA32(0, 0, 0, 100)
  799.                             WatchOutColor = NOT WatchOutColor
  800.                             DangerMessage$ = "WATCH OUT!"
  801.                             PrintShadow _WIDTH \ 2 - _PRINTWIDTH(DangerMessage$) \ 2, _HEIGHT \ 2 - _FONTHEIGHT \ 2, DangerMessage$, _RGB32(255, 255, 255)
  802.                             _DEST PreviousDest
  803.                         END IF
  804.                         UpdateScreen
  805.                     NEXT Merge
  806.                     InWatchOut = False
  807.  
  808.                     Board(CurrentRow, CurrentColumn).State = False
  809.                     Board(CurrentRow - 1, CurrentColumn).Shade = i + 1
  810.                 ELSE
  811.                     EXIT DO
  812.                 END IF
  813.             ELSE
  814.                 EXIT DO
  815.             END IF
  816.             CurrentRow = CurrentRow - 1
  817.             CurrentShade = CurrentShade + 1
  818.             Y = BlockRows(CurrentRow)
  819.             PrevY = Y
  820.             CheckDanger
  821.         LOOP UNTIL CurrentRow = 1 OR CurrentShade = 5
  822.     END IF
  823.     _KEYCLEAR
  824.  
  825. SUB CheckConnectedLines
  826.     'Check for connected lines with the same shade and
  827.     'compute the new score, besides generating the disappearing
  828.     'animation:
  829.     DIM WatchOutColor AS _BIT, PreviousDest AS LONG
  830.     DIM DangerMessage$
  831.  
  832.     Matched = False
  833.     DO
  834.         CurrentMatch = CheckMatchingLine%
  835.         IF CurrentMatch = 0 THEN EXIT DO
  836.  
  837.         Matched = True
  838.         IF GameMode = FILLMODE THEN Score = Score - 40 ELSE Score = Score + 40
  839.         IF Score < 0 THEN Score = 0
  840.  
  841.         MatchLineStart = BlockRows(CurrentMatch) + BlockHeight \ 2
  842.  
  843.         IF LineSound AND Settings.SoundOn AND NOT DemoMode AND NOT GameMode = FILLMODE THEN
  844.             _SNDPLAYCOPY LineSound
  845.         ELSEIF Settings.SoundOn AND GameMode = FILLMODE THEN
  846.             IF ShockSound THEN _SNDPLAYCOPY ShockSound
  847.         END IF
  848.  
  849.         FOR i = 1 TO BlockHeight \ 2
  850.             _LIMIT 60
  851.             IF BgImage < -1 THEN
  852.                 _PUTIMAGE (0, MatchLineStart - i)-(599, MatchLineStart + i), BgImage, GameScreen, (0, MatchLineStart - i)-(599, MatchLineStart + i)
  853.             ELSE
  854.                 LINE (0, MatchLineStart - i)-(599, MatchLineStart + i), BackgroundColor, BF
  855.             END IF
  856.             IF GameMode = FILLMODE THEN
  857.                 InWatchOut = True
  858.                 PreviousDest = _DEST
  859.                 _DEST OverlayGraphics
  860.                 IF WatchOutColor THEN CLS , _RGB(255, 255, 0) ELSE CLS , _RGBA32(0, 0, 0, 100)
  861.                 WatchOutColor = NOT WatchOutColor
  862.                 DangerMessage$ = "ARE YOU CRAZY?!"
  863.                 PrintShadow _WIDTH \ 2 - _PRINTWIDTH(DangerMessage$) \ 2, _HEIGHT \ 2 - _FONTHEIGHT \ 2, DangerMessage$, _RGB32(255, 255, 255)
  864.                 _DEST PreviousDest
  865.             END IF
  866.             UpdateScreen
  867.         NEXT i
  868.         InWatchOut = False
  869.  
  870.         DestroyLine CurrentMatch
  871.         TotalLines = TotalLines + 1
  872.         RedrawBoard
  873.  
  874.         DropSoundI = _CEIL(RND * 3)
  875.         IF DropSound(DropSoundI) AND Settings.SoundOn AND NOT DemoMode THEN
  876.             _SNDPLAYCOPY DropSound(DropSoundI)
  877.         END IF
  878.         IF DemoMode THEN DemoMode = False
  879.     LOOP
  880.  
  881. FUNCTION ConvertYtoRow (CurrentY)
  882.     'Returns the row on the board through which the block is currently
  883.     'passing.
  884.  
  885.     IF CurrentY >= -48 AND CurrentY <= 20 THEN
  886.         ConvertYtoRow = 12
  887.     ELSEIF CurrentY > 20 AND CurrentY <= 85 THEN
  888.         ConvertYtoRow = 11
  889.     ELSEIF CurrentY > 85 AND CurrentY <= 150 THEN
  890.         ConvertYtoRow = 10
  891.     ELSEIF CurrentY > 150 AND CurrentY <= 215 THEN
  892.         ConvertYtoRow = 9
  893.     ELSEIF CurrentY > 215 AND CurrentY <= 280 THEN
  894.         ConvertYtoRow = 8
  895.     ELSEIF CurrentY > 280 AND CurrentY <= 345 THEN
  896.         ConvertYtoRow = 7
  897.     ELSEIF CurrentY > 345 AND CurrentY <= 410 THEN
  898.         ConvertYtoRow = 6
  899.     ELSEIF CurrentY > 410 AND CurrentY <= 475 THEN
  900.         ConvertYtoRow = 5
  901.     ELSEIF CurrentY > 475 AND CurrentY <= 540 THEN
  902.         ConvertYtoRow = 4
  903.     ELSEIF CurrentY > 540 AND CurrentY <= 605 THEN
  904.         ConvertYtoRow = 3
  905.     ELSEIF CurrentY > 605 AND CurrentY <= 670 THEN
  906.         ConvertYtoRow = 2
  907.     ELSEIF CurrentY > 670 AND CurrentY <= 735 THEN
  908.         ConvertYtoRow = 1
  909.     END IF
  910.  
  911. FUNCTION ConvertXtoCol (CurrentX)
  912.     'Returns the column on the board being currently hovered
  913.  
  914.     IF CurrentX >= BlockPos(1) AND CurrentX < BlockPos(2) THEN
  915.         ConvertXtoCol = 1
  916.     ELSEIF CurrentX >= BlockPos(2) AND CurrentX < BlockPos(3) THEN
  917.         ConvertXtoCol = 2
  918.     ELSEIF CurrentX >= BlockPos(3) AND CurrentX < BlockPos(4) THEN
  919.         ConvertXtoCol = 3
  920.     ELSEIF CurrentX >= BlockPos(4) THEN
  921.         ConvertXtoCol = 4
  922.     END IF
  923.  
  924.  
  925. FUNCTION Shade& (CurrentShade)
  926.     Shade& = _RGB32(Shades(CurrentShade).R, Shades(CurrentShade).G, Shades(CurrentShade).B)
  927.  
  928. FUNCTION CheckMatchingLine%
  929.  
  930.     DIM i AS LONG
  931.     DIM a.s AS LONG, b.s AS LONG, c.s AS LONG, d.s AS LONG
  932.     DIM a AS LONG, b AS LONG, c AS LONG, d AS LONG
  933.  
  934.     FOR i = 1 TO 12
  935.         a.s = Board(i, 1).State
  936.         b.s = Board(i, 2).State
  937.         c.s = Board(i, 3).State
  938.         d.s = Board(i, 4).State
  939.  
  940.         a = Board(i, 1).Shade
  941.         b = Board(i, 2).Shade
  942.         c = Board(i, 3).Shade
  943.         d = Board(i, 4).Shade
  944.  
  945.         IF a.s AND b.s AND c.s AND d.s THEN
  946.             IF a = b AND b = c AND c = d THEN
  947.                 CheckMatchingLine% = i
  948.                 EXIT FUNCTION
  949.             END IF
  950.         END IF
  951.  
  952.     NEXT i
  953.     CheckMatchingLine% = 0
  954.  
  955.  
  956. SUB DestroyLine (LineToDestroy AS LONG)
  957.  
  958.     DIM i AS LONG
  959.     SELECT CASE LineToDestroy
  960.         CASE 1 TO 11
  961.             FOR i = LineToDestroy TO 11
  962.                 Board(i, 1).State = Board(i + 1, 1).State
  963.                 Board(i, 2).State = Board(i + 1, 2).State
  964.                 Board(i, 3).State = Board(i + 1, 3).State
  965.                 Board(i, 4).State = Board(i + 1, 4).State
  966.  
  967.                 Board(i, 1).Shade = Board(i + 1, 1).Shade
  968.                 Board(i, 2).Shade = Board(i + 1, 2).Shade
  969.                 Board(i, 3).Shade = Board(i + 1, 3).Shade
  970.                 Board(i, 4).Shade = Board(i + 1, 4).Shade
  971.             NEXT i
  972.             FOR i = 1 TO 4
  973.                 Board(12, i).State = False
  974.                 Board(12, i).Shade = 0
  975.             NEXT i
  976.         CASE 12
  977.             FOR i = 1 TO 4
  978.                 Board(12, i).State = False
  979.                 Board(12, i).Shade = 0
  980.             NEXT i
  981.     END SELECT
  982.  
  983.  
  984. SUB RedrawBoard
  985.     DIM i AS LONG, CurrentColumn AS LONG
  986.     DIM StartY AS LONG, EndY AS LONG
  987.  
  988.     IF BgImage < -1 THEN _PUTIMAGE , BgImage, GameScreen ELSE CLS , BackgroundColor
  989.  
  990.     FOR i = 1 TO 12
  991.         FOR CurrentColumn = 4 TO 1 STEP -1
  992.             StartY = BlockRows(i)
  993.             EndY = StartY + BlockHeight
  994.  
  995.             IF Board(i, CurrentColumn).State = True THEN
  996.                 LINE (BlockPos(CurrentColumn), StartY)-(BlockPos(CurrentColumn) + BlockWidth, EndY), Shade&(Board(i, CurrentColumn).Shade), BF
  997.             END IF
  998.         NEXT CurrentColumn
  999.     NEXT i
  1000.  
  1001.  
  1002. SUB ShowScore
  1003.     DIM ScoreString AS STRING
  1004.     DIM ModeHighScore AS LONG
  1005.  
  1006.     IF Score = PreviousScore THEN EXIT SUB
  1007.     PreviousScore = Score
  1008.  
  1009.     ScoreString = "Score:" + STR$(Score)
  1010.  
  1011.     SELECT CASE GameMode
  1012.         CASE ZENMODE: ModeHighScore = Settings.HighscoreZEN
  1013.         CASE NORMALMODE: ModeHighScore = Settings.HighscoreNORMAL
  1014.         CASE FLASHMODE: ModeHighScore = Settings.HighscoreFLASH
  1015.         CASE FILLMODE: ModeHighScore = Settings.HighscoreFILL
  1016.     END SELECT
  1017.  
  1018.     _DEST InfoScreen
  1019.     CLS , _RGBA32(0, 0, 0, 0)
  1020.  
  1021.     '_FONT 16
  1022.     PrintShadow 15, 15, ScoreString, _RGB32(255, 255, 255)
  1023.  
  1024.     _FONT 8
  1025.     IF Score < ModeHighScore THEN
  1026.         PrintShadow 15, 32, "Highscore: " + TRIM$(ModeHighScore), _RGB32(255, 255, 255)
  1027.     ELSEIF Score > ModeHighScore AND ModeHighScore > 0 THEN
  1028.         PrintShadow 15, 32, "You beat the highscore!", _RGB32(255, 255, 255)
  1029.     END IF
  1030.     _FONT 16
  1031.     _DEST GameScreen
  1032.  
  1033.  
  1034. SUB MakeIcon
  1035.     'Generates the icon that will be placed on the window title of the game
  1036.     DIM Icon AS LONG
  1037.     DIM PreviousDest AS LONG
  1038.     DIM i AS LONG
  1039.     CONST IconSize = 16
  1040.  
  1041.     Icon = _NEWIMAGE(IconSize, IconSize, 32)
  1042.     PreviousDest = _DEST
  1043.     _DEST Icon
  1044.  
  1045.     FOR i = 1 TO 5
  1046.         LINE (0, i * (IconSize / 5) - (IconSize / 5))-(IconSize, i * (IconSize / 5)), Shade&(i), BF
  1047.     NEXT i
  1048.  
  1049.     _ICON Icon
  1050.     _FREEIMAGE Icon
  1051.  
  1052.     _DEST PreviousDest
  1053.  
  1054. SUB CheckDanger
  1055.     'Checks if any block pile is 11 blocks high, which
  1056.     'means danger, which means player needs to think faster,
  1057.     'which means we'll make him a little bit more nervous by
  1058.     'switching our soothing bg song to a fast paced circus
  1059.     'like melody.
  1060.     IF Board(11, 1).State OR Board(11, 2).State OR Board(11, 3).State OR Board(11, 4).State THEN
  1061.         IF Settings.SoundOn AND NOT InDanger AND NOT DemoMode THEN
  1062.             IF Alarm THEN _SNDPLAYCOPY Alarm
  1063.             IF Settings.MusicOn THEN
  1064.                 IF GameMode <> FILLMODE THEN
  1065.                     IF BgMusic(GameMode) THEN _SNDSTOP BgMusic(GameMode)
  1066.                 ELSE
  1067.                     IF BgMusic(ZENMODE) THEN _SNDSTOP BgMusic(ZENMODE)
  1068.                 END IF
  1069.                 IF BgMusic(4) THEN _SNDLOOP BgMusic(4)
  1070.             END IF
  1071.             TIMER(AlertTimer) ON
  1072.         END IF
  1073.         InDanger = True
  1074.     ELSE
  1075.         IF Settings.MusicOn AND InDanger AND NOT DemoMode THEN
  1076.             IF BgMusic(4) THEN _SNDSTOP BgMusic(4)
  1077.             IF GameMode <> FILLMODE THEN
  1078.                 IF BgMusic(GameMode) THEN _SNDLOOP BgMusic(GameMode)
  1079.             ELSE
  1080.                 IF BgMusic(ZENMODE) THEN _SNDLOOP BgMusic(ZENMODE)
  1081.             END IF
  1082.             TIMER(AlertTimer) OFF
  1083.             _DEST OverlayGraphics
  1084.             CLS , _RGBA32(0, 0, 0, 0)
  1085.             _DEST GameScreen
  1086.         END IF
  1087.         InDanger = False
  1088.     END IF
  1089.  
  1090. SUB LoadAssets
  1091.     'Loads sound files at startup.
  1092.     DIM i AS _BYTE
  1093.  
  1094.     LineSound = _SNDOPEN("line.ogg", "SYNC")
  1095.     GameOverSound = _SNDOPEN("gameover.ogg", "SYNC")
  1096.     Whistle = _SNDOPEN("whistle.ogg", "SYNC,VOL")
  1097.     IF Whistle THEN _SNDVOL Whistle, 0.02
  1098.  
  1099.     Alarm = _SNDOPEN("alarm.ogg", "SYNC")
  1100.     ShockSound = _SNDOPEN("shock.ogg", "SYNC")
  1101.  
  1102.     FOR i = 1 TO 3
  1103.         IF NOT DropSound(i) THEN DropSound(i) = _SNDOPEN("drop" + TRIM$(i) + ".ogg", "SYNC")
  1104.     NEXT i
  1105.  
  1106.     FOR i = 1 TO 4
  1107.         IF NOT SplashSound(i) THEN SplashSound(i) = _SNDOPEN("water" + TRIM$(i) + ".ogg", "SYNC")
  1108.     NEXT i
  1109.  
  1110.     BgMusic(1) = _SNDOPEN("Water_Prelude.ogg", "SYNC,VOL")
  1111.     BgMusic(2) = _SNDOPEN("Crowd_Hammer.ogg", "SYNC,VOL")
  1112.     BgMusic(3) = _SNDOPEN("Upbeat_Forever.ogg", "SYNC,VOL")
  1113.     BgMusic(4) = _SNDOPEN("quick.ogg", "SYNC,VOL")
  1114.     IF BgMusic(1) THEN _SNDVOL BgMusic(1), .2
  1115.     IF BgMusic(4) THEN _SNDVOL BgMusic(4), .8
  1116.  
  1117.  
  1118. SUB UpdateScreen
  1119.     'Display the gamescreen, overlay and score layers
  1120.     IF NOT DemoMode THEN ShowScore
  1121.  
  1122.     _PUTIMAGE , GameScreen, MainScreen
  1123.     IF InMenu OR InDanger OR InWatchOut THEN
  1124.         _PUTIMAGE , OverlayGraphics, MainScreen
  1125.         IF MenuTip THEN
  1126.             _PUTIMAGE (_WIDTH(MainScreen) \ 2 - _WIDTH(MenuTip) \ 2, _HEIGHT(MainScreen) \ 2 - _HEIGHT(MenuTip) \ 2), MenuTip, MainScreen
  1127.         END IF
  1128.     END IF
  1129.  
  1130.     IF NOT InMenu THEN _PUTIMAGE , InfoScreen, MainScreen
  1131.     _DISPLAY
  1132.  
  1133. SUB PrintShadow (x%, y%, Text$, ForeColor&)
  1134.     'Shadow:
  1135.     COLOR _RGBA32(170, 170, 170, 170), _RGBA32(0, 0, 0, 0)
  1136.     _PRINTSTRING (x% + 1, y% + 1), Text$
  1137.  
  1138.     'Text:
  1139.     COLOR ForeColor&, _RGBA32(0, 0, 0, 0)
  1140.     _PRINTSTRING (x%, y%), Text$
  1141.  
  1142. SUB SelectGlobalShade
  1143.     IF Settings.ColorMode = 0 THEN
  1144.         GlobalShade = (GlobalShade) MOD MaxShades + 1
  1145.     ELSE
  1146.         GlobalShade = Settings.ColorMode
  1147.     END IF
  1148.     SELECT CASE GlobalShade
  1149.         CASE 1: RESTORE Greens
  1150.         CASE 2: RESTORE Oranges
  1151.         CASE 3: RESTORE Blues
  1152.         CASE 4: RESTORE Pinks
  1153.     END SELECT
  1154.  
  1155.     FOR i = 1 TO 5
  1156.         READ Shades(i).R
  1157.         READ Shades(i).G
  1158.         READ Shades(i).B
  1159.     NEXT i
  1160.  
  1161.  
  1162. SUB PrepareIntro
  1163.     'The intro shows the board about to be cleared,
  1164.     'which then happens after assets are loaded. The intro
  1165.     'is generated using the game engine.
  1166.  
  1167.     'DemoMode prevents sounds to be played
  1168.     DemoMode = True
  1169.  
  1170.     _DEST InfoScreen
  1171.     _FONT 16
  1172.     LoadingMessage$ = "Cloned Shades"
  1173.     PrintShadow _WIDTH \ 2 - _PRINTWIDTH(LoadingMessage$) \ 2, _HEIGHT \ 2 - _FONTHEIGHT, LoadingMessage$, _RGB32(255, 255, 255)
  1174.  
  1175.     _FONT 8
  1176.     LoadingMessage$ = "loading..."
  1177.     PrintShadow _WIDTH \ 2 - _PRINTWIDTH(LoadingMessage$) \ 2, _HEIGHT \ 2, LoadingMessage$, _RGB32(255, 255, 255)
  1178.  
  1179.     _FONT 16
  1180.     _DEST GameScreen
  1181.  
  1182.     'Setup the board to show an "about to merge" group of blocks
  1183.     'which will end up completing a dark line at the bottom.
  1184.     Board(1, 1).State = True
  1185.     Board(1, 1).Shade = 5
  1186.     Board(1, 2).State = True
  1187.     Board(1, 2).Shade = 5
  1188.     Board(1, 3).State = True
  1189.     Board(1, 3).Shade = 4
  1190.     Board(1, 4).State = True
  1191.     Board(1, 4).Shade = 5
  1192.     Board(2, 3).State = True
  1193.     Board(2, 3).Shade = 3
  1194.     Board(3, 3).State = True
  1195.     Board(3, 3).Shade = 2
  1196.     Board(4, 3).State = True
  1197.     Board(4, 3).Shade = 2
  1198.  
  1199.     CurrentColumn = 3
  1200.     CurrentRow = 4
  1201.     CurrentShade = 2
  1202.     Y = BlockRows(CurrentRow)
  1203.     PrevY = Y
  1204.     BlockPut = True
  1205.  
  1206.     RedrawBoard
  1207.     Board(4, 3).State = False
  1208.  
  1209.     UpdateScreen
  1210.  
  1211. SUB Intro
  1212.     'The current board setup must have been prepared using PrepareIntro first.
  1213.  
  1214.     'Use the game engine to show the intro:
  1215.     CheckMerge
  1216.     CheckConnectedLines
  1217.  
  1218.     'Clear the "loading..." text
  1219.     _DEST InfoScreen
  1220.     CLS , _RGBA32(0, 0, 0, 0)
  1221.     _DEST GameScreen
  1222.  
  1223.  
  1224. SUB HighLightCol (Col AS LONG)
  1225.  
  1226.     _DEST OverlayGraphics
  1227.     CLS , _RGBA32(0, 0, 0, 0)
  1228.     LINE (BlockPos(Col), 16)-STEP(BlockWidth, _HEIGHT(0)), _RGBA32(255, 255, 255, 150), BF
  1229.     _DEST GameScreen
  1230.  
  1231.  
  1232. SUB ShowAlert
  1233.     STATIC FadeLevel
  1234.     DIM DangerMessage$
  1235.     DIM PreviousDest AS LONG
  1236.  
  1237.     IF InMenu OR InWatchOut THEN EXIT SUB
  1238.  
  1239.     IF FadeLevel > 100 THEN FadeLevel = 0
  1240.     FadeLevel = FadeLevel + 1
  1241.     PreviousDest = _DEST
  1242.     _DEST OverlayGraphics
  1243.     IF GameMode = FILLMODE THEN CLS , _RGBA32(0, 255, 0, FadeLevel) ELSE CLS , _RGBA32(255, 0, 0, FadeLevel)
  1244.     IF GameMode = FILLMODE THEN DangerMessage$ = "BE EXTRA CAREFUL!" ELSE DangerMessage$ = "DANGER!"
  1245.     PrintShadow _WIDTH \ 2 - _PRINTWIDTH(DangerMessage$) \ 2, _HEIGHT \ 2 - _FONTHEIGHT \ 2, DangerMessage$, _RGB32(255, 255, 255)
  1246.     _DEST PreviousDest
  1247.  
  1248. SUB ShowGetReady (CountDown AS _BYTE)
  1249.     DIM Message$, i AS _BYTE, i$, iSnd AS _BYTE
  1250.     DIM PreviousDest AS LONG
  1251.  
  1252.     PreviousDest = _DEST
  1253.     DemoMode = True: InMenu = True
  1254.     _DEST OverlayGraphics
  1255.     Message$ = "GET READY"
  1256.     FOR i = CountDown TO 1 STEP -1
  1257.         CLS , _RGBA32(255, 255, 255, 200)
  1258.         PrintShadow _WIDTH \ 2 - _PRINTWIDTH(Message$) \ 2, _HEIGHT \ 2 - _FONTHEIGHT \ 2, Message$, _RGB32(0, 0, 0)
  1259.         IF i = 1 THEN i$ = "GO!" ELSE i$ = TRIM$(i)
  1260.         PrintShadow _WIDTH \ 2 - _PRINTWIDTH(i$) \ 2, _HEIGHT \ 2 - _FONTHEIGHT \ 2 + _FONTHEIGHT, i$, Shade&(5)
  1261.         UpdateScreen
  1262.         iSnd = _CEIL(RND * 3): IF DropSound(iSnd) THEN _SNDPLAYCOPY DropSound(iSnd)
  1263.         _DELAY .5
  1264.     NEXT i
  1265.     _DEST PreviousDest
  1266.     DemoMode = False: InMenu = False
  1267.  
  1268. FUNCTION Menu (CurrentChoice AS _BYTE, MaxChoice AS _BYTE, Choices() AS STRING, Info() AS STRING, Tips() AS LONG, TipTime AS DOUBLE)
  1269.     'Displays Choices() on the screen and lets the player choose one.
  1270.     'Uses OverlayGraphics page to display options.
  1271.     'Player must use arrow keys to make a choice then ENTER.
  1272.  
  1273.     DIM Choice AS _BYTE, PreviousChoice AS _BYTE
  1274.     DIM ChoiceWasMade AS _BIT
  1275.     DIM k$, i AS LONG
  1276.     DIM ChooseColorTimer AS LONG
  1277.     DIM ItemShade AS LONG
  1278.     DIM ThisItemY AS LONG
  1279.     DIM ThisTime AS DOUBLE, StartTime AS DOUBLE, TipShown AS _BIT
  1280.  
  1281.     DemoMode = True
  1282.     InMenu = True
  1283.     Choice = CurrentChoice
  1284.  
  1285.     IF NOT InGame THEN
  1286.         ChooseColorTimer = _FREETIMER
  1287.         ON TIMER(ChooseColorTimer, 3.5) SelectGlobalShade
  1288.         TIMER(ChooseColorTimer) ON
  1289.     END IF
  1290.  
  1291.     IF NOT InGame THEN ERASE Board: BlockPut = True
  1292.  
  1293.     StartTime = TIMER
  1294.     DO
  1295.         _LIMIT 30
  1296.  
  1297.         'Use the game engine while the menu is displayed, except while InGame:
  1298.         IF NOT InGame THEN
  1299.             IF BlockPut THEN
  1300.                 GenerateNewBlock: BlockPut = False
  1301.             ELSE
  1302.                 MoveBlock
  1303.             END IF
  1304.         END IF
  1305.  
  1306.         GOSUB ShowCurrentChoice
  1307.         ThisTime = TIMER
  1308.         IF ThisTime - StartTime >= TipTime AND NOT TipShown THEN
  1309.             'TipTime has passed since the user selected the current choice, so
  1310.             'if Tips(Choice) contains an image, it is _PUTIMAGEd on the screen.
  1311.             IF Tips(Choice) < -1 THEN
  1312.                 MenuTip = Tips(Choice)
  1313.                 TipShown = True
  1314.             END IF
  1315.         END IF
  1316.  
  1317.         k$ = INKEY$
  1318.         SELECT CASE k$
  1319.             CASE CHR$(0) + CHR$(80) 'Down arrow
  1320.                 DO
  1321.                     Choice = (Choice) MOD MaxChoice + 1
  1322.                 LOOP WHILE RIGHT$(Choices(Choice), 1) = CHR$(0)
  1323.                 StartTime = TIMER
  1324.                 TipShown = False
  1325.                 MenuTip = False
  1326.             CASE CHR$(0) + CHR$(72) 'Up arrow
  1327.                 DO
  1328.                     Choice = (Choice + MaxChoice - 2) MOD MaxChoice + 1
  1329.                 LOOP WHILE RIGHT$(Choices(Choice), 1) = CHR$(0)
  1330.                 StartTime = TIMER
  1331.                 TipShown = False
  1332.                 MenuTip = False
  1333.             CASE CHR$(13) 'Enter
  1334.                 ChoiceWasMade = True
  1335.                 MenuTip = False
  1336.             CASE CHR$(27) 'ESC
  1337.                 ChoiceWasMade = True
  1338.                 Choice = MaxChoice
  1339.         END SELECT
  1340.     LOOP UNTIL ChoiceWasMade
  1341.  
  1342.     IF NOT InGame THEN TIMER(ChooseColorTimer) FREE
  1343.     InMenu = False
  1344.     DemoMode = False
  1345.     _DEST OverlayGraphics
  1346.     CLS , _RGBA32(255, 255, 255, 100)
  1347.     _DEST GameScreen
  1348.  
  1349.     MenuTip = False
  1350.     FOR i = 1 TO MaxChoice
  1351.         IF Tips(i) < -1 THEN _FREEIMAGE Tips(i)
  1352.     NEXT i
  1353.  
  1354.     Menu = Choice
  1355.  
  1356.     ShowCurrentChoice:
  1357.     IF Choice = PreviousChoice THEN RETURN
  1358.     _DEST OverlayGraphics
  1359.     CLS , _RGBA32(255, 255, 255, 100)
  1360.  
  1361.     'Choices ending with CHR$(0) are shown as unavailable/grey.
  1362.     ThisItemY = (_HEIGHT(OverlayGraphics) / 2) - (((_FONTHEIGHT * MaxChoice) + _FONTHEIGHT) / 2)
  1363.     FOR i = 1 TO MaxChoice
  1364.         ThisItemY = ThisItemY + _FONTHEIGHT
  1365.         IF i = Choice THEN
  1366.             ItemShade = Shade&(5)
  1367.             PrintShadow (_WIDTH(OverlayGraphics) \ 2) - (_PRINTWIDTH("> " + Choices(i)) \ 2), ThisItemY, CHR$(16) + Choices(i), ItemShade
  1368.         ELSE
  1369.             IF RIGHT$(Choices(i), 1) = CHR$(0) THEN
  1370.                 ItemShade = _RGB32(255, 255, 255)
  1371.                 PrintShadow (_WIDTH(OverlayGraphics) \ 2) - (_PRINTWIDTH(LEFT$(Choices(i), LEN(Choices(i)) - 1)) \ 2), ThisItemY, LEFT$(Choices(i), LEN(Choices(i)) - 1), ItemShade
  1372.             ELSE
  1373.                 ItemShade = Shade&(4)
  1374.                 PrintShadow (_WIDTH(OverlayGraphics) \ 2) - (_PRINTWIDTH(Choices(i)) \ 2), ThisItemY, Choices(i), ItemShade
  1375.             END IF
  1376.         END IF
  1377.         IF LEN(Info(i)) AND i = Choice THEN
  1378.             _FONT 8
  1379.             COLOR Shade&(5)
  1380.             _PRINTSTRING ((_WIDTH(OverlayGraphics) \ 2) - (_PRINTWIDTH(Info(i)) \ 2), _HEIGHT(OverlayGraphics) - 8), Info(i)
  1381.             _FONT 16
  1382.         END IF
  1383.     NEXT i
  1384.     _DEST GameScreen
  1385.     UpdateScreen
  1386.     PreviousChoice = Choice
  1387.     RETURN
  1388.  
  1389.  
  1390. SUB ShowEndScreen
  1391.     DIM Message$(1 TO 10), k$, i AS LONG
  1392.     DIM MessageColor AS LONG
  1393.  
  1394.     IF InDanger THEN
  1395.         TIMER(AlertTimer) OFF
  1396.         InDanger = False
  1397.     END IF
  1398.  
  1399.     _DEST OverlayGraphics
  1400.     CLS , _RGBA32(255, 255, 255, 150)
  1401.  
  1402.     IF GameMode = FILLMODE AND Score > 0 THEN Message$(1) = "YOU WIN!" ELSE Message$(1) = "GAME OVER"
  1403.     Message$(3) = "Your score:"
  1404.     Message$(4) = TRIM$(Score)
  1405.     Message$(5) = "Merged blocks:"
  1406.     Message$(6) = TRIM$(TotalMerges)
  1407.     Message$(7) = "Lines destroyed:"
  1408.     Message$(8) = TRIM$(TotalLines)
  1409.     Message$(10) = "Press ENTER..."
  1410.  
  1411.     MessageColor = Shade&(5)
  1412.     FOR i = 1 TO UBOUND(message$)
  1413.         IF i > 1 THEN _FONT 8: MessageColor = _RGB(0, 0, 0)
  1414.         IF i = UBOUND(message$) THEN _FONT 16: MessageColor = Shade&(5)
  1415.         PrintShadow (_WIDTH(OverlayGraphics) \ 2) - (_PRINTWIDTH(Message$(i)) \ 2), i * 16, Message$(i), MessageColor
  1416.     NEXT i
  1417.  
  1418.     _DEST MainScreen
  1419.  
  1420.     FOR i = 1 TO _HEIGHT(MainScreen) / 2 STEP BlockHeight / 2
  1421.         _LIMIT 60
  1422.         _PUTIMAGE , GameScreen
  1423.         _PUTIMAGE (0, _HEIGHT(MainScreen) / 2 - i)-(599, _HEIGHT(MainScreen) / 2 + i), OverlayGraphics
  1424.         _DISPLAY
  1425.     NEXT i
  1426.  
  1427.     _PUTIMAGE , GameScreen
  1428.     _PUTIMAGE , OverlayGraphics
  1429.     _DISPLAY
  1430.     _DEST GameScreen
  1431.  
  1432.     _KEYCLEAR
  1433.     k$ = "": WHILE k$ <> CHR$(13): _LIMIT 30: k$ = INKEY$: WEND
  1434.  
  1435.  
  1436. FUNCTION TRIM$ (Number)
  1437.     TRIM$ = LTRIM$(RTRIM$(STR$(Number)))
  1438.  

2019-04-15 00_19_25-Cloned Shades.png
2019-04-15 00_20_21-Cloned Shades.png

Offline TempodiBasic

  • Forum Resident
  • Posts: 1792
    • View Profile
Re: Cloned Shades
« Reply #1 on: April 15, 2019, 12:54:11 pm »
  • Best Answer
  • Hi Fellippe
    I find it very interesting and cool,
    I need to use massively the eyes for colors.

    Thanks to share.

    While I was playing with Cloned Shades I got this bug! An invisible block.
    Here a screenshot attached
    Shades Fellippe Bug of invisible block.jpg
    !
    Programming isn't difficult, only it's  consuming time and coffee

    FellippeHeitor

    • Guest
    Re: Cloned Shades
    « Reply #2 on: April 15, 2019, 02:49:02 pm »
  • Best Answer
  • Interesting bug, TempodiBasic! I wonder if I'll be able to ever reproduce it!

    Thanks for your interest.

    Offline bplus

    • Global Moderator
    • Forum Resident
    • Posts: 8053
    • b = b + ...
      • View Profile
    Re: Cloned Shades
    « Reply #3 on: April 15, 2019, 04:14:41 pm »
  • Best Answer
  • Well my first, second tries I had no idea what strategy to use, then I got it and played 1.25 hours and still on row 1!

    There are some nice graphic and sound effects. The score keeping is a bit funky, it keeps clearing back to 0?

     

    FellippeHeitor

    • Guest
    Re: Cloned Shades
    « Reply #4 on: April 15, 2019, 04:23:17 pm »
  • Best Answer
  • Wow, that's a long run!

    Now about the score keeping getting reset... I didn't see it happen yet. Was it at any possible overflow mark?

    Offline bplus

    • Global Moderator
    • Forum Resident
    • Posts: 8053
    • b = b + ...
      • View Profile
    Re: Cloned Shades
    « Reply #5 on: April 15, 2019, 04:34:22 pm »
  • Best Answer
  • Overflow mark? 

    The stacks never went much past half screen up... using Normal Fill mode.

    It would show a score like 5 after a line clears and then a brick or 2 later back to 0. I had to go run errands so did not get chance to read the last screen when game ends.

    Offline bplus

    • Global Moderator
    • Forum Resident
    • Posts: 8053
    • b = b + ...
      • View Profile
    Re: Cloned Shades
    « Reply #6 on: April 15, 2019, 07:19:40 pm »
  • Best Answer
  • Update:

    OK, I got to read the directions to the Fill Mode. Blah! I was playing it backwards! Sorry.

    Scoring is fine.

    FellippeHeitor

    • Guest
    Re: Cloned Shades
    « Reply #7 on: April 15, 2019, 08:06:53 pm »
  • Best Answer
  • Update:

    OK, I got to read the directions to the Fill Mode. Blah! I was playing it backwards! Sorry.

    Scoring is fine.
    2F8547F6-DF76-4B57-A634-5994FA9FDE00.gif


    Offline bplus

    • Global Moderator
    • Forum Resident
    • Posts: 8053
    • b = b + ...
      • View Profile
    Re: Cloned Shades
    « Reply #8 on: April 15, 2019, 09:09:57 pm »
  • Best Answer
  • OK the right way:
    6606 shades.PNG
    * 6606 shades.PNG (Filesize: 31.45 KB, Dimensions: 465x589, Views: 152)

    FellippeHeitor

    • Guest
    Re: Cloned Shades
    « Reply #9 on: April 15, 2019, 09:42:42 pm »
  • Best Answer
  • Great job!

    FellippeHeitor

    • Guest
    Re: Cloned Shades
    « Reply #10 on: April 15, 2019, 11:23:36 pm »
  • Best Answer
  • Did you try using the mouse to control it as well, bplus?

    Offline TempodiBasic

    • Forum Resident
    • Posts: 1792
      • View Profile
    Re: Cloned Shades
    « Reply #11 on: April 16, 2019, 03:35:59 am »
  • Best Answer
  • Hi Fellippe

    I find this game simple and amazing.


    here more my feedbacks, they're not criticism but point of view.
    1.
    Mouse works for drag and drop during the game, not in menu level.
     in main screen ,where is the menu, if I stay along on a voice menu it appears the tooltip help&comments, but it doesn't disappear both after a short time both after a long time, both after mouse input (movemouse, click by left button or right button or wheel or all together) and the user must use input keyboard to come back to menu screen (that is back to tooltip Help&comments)

    2.
    for my poor eyes something the difference between two grades of the same color are not so clear. But this is for me that I'm 51 years old.

    3.
    I have got another time the bug glitch! But this one I am not able to get screenshot because the game goes to fast and merge the block invisible with another of the same grade_color.
       4. (Do you think to implement a mouse or Key command for Pause during the game?)
    ....
    3.  (continue) however I can confirm that to get the bug_glitch you must move by keyboard to right (press right key) just before the block is to touch the stack..... (in Tetris you can match also in this manner the blocks...)

    5. this can be a feature or a bug, it depends by the rules of the game that are not avaible in the program
       6. do you think to implement a Help for dummies like me?  ie use of arrows and  mouse , grading of colors (how many grade
                      of  colors are in the level? I think this is important because you can know how blocks can merge in vertical and this one
                   depends by the feature_bug of point 5)
    5. (continue) what happens in the game is that:
     if I have a line in which putting the matching block it is completed and must be deleted BUT the block under this last block has the same color, well the program merge the two block in vertical and it doesn't clear the line. Also this can be a feature or a bug, it depends by rules of the game that I have no

    example:            B
                              |   
                              v BBBB
                              B NNNN

    Thanks to read
    Sorry for many words
    Programming isn't difficult, only it's  consuming time and coffee

    FellippeHeitor

    • Guest
    Re: Cloned Shades
    « Reply #12 on: April 16, 2019, 06:45:13 am »
  • Best Answer
  • Hi, TempodiBasic,

    I can't take credit for the idea, since it's a clone as stated, but I'm glad you enjoy playing my version.

    1- Mouse for the main menu would actually make a lot of sense. Thanks for the suggestion.
    2- The idea is that the shades are close enough in order to get you confused. The gradient background is there to help you get even more confused.
    3- I'll try to reproduce the invisible block bug with your instructions.
    4- ESC for pausing.
    5- Not having too many of the rules explicit is part of the original game's "magic", so it is by design.
     6- Because of the tetris-like nature, I feel explaining that the arrow keys are to be used is a bit overkill.
    5- (continuation) Again, it is a bit frustrating in that sense by design :-)

    Thanks for playing!

    Offline bplus

    • Global Moderator
    • Forum Resident
    • Posts: 8053
    • b = b + ...
      • View Profile
    Re: Cloned Shades
    « Reply #13 on: April 16, 2019, 10:17:08 am »
  • Best Answer
  • Did you try using the mouse to control it as well, bplus?

    I didn't know, so I just tried the mouse and did not like for block control. For once the arrow keys seem more natural control.

    So that background and shades were meant to confuse! Yeah at top it seems a shade different than when reach bottom.

    So esc key is pause, good to know. Like TempodiBasic I was expecting mouse to work on menu pages, mouse over pop-up description of Fill Mode would have saved me some confusion about the mode.

    This game is addictive! I like more than Tetris.

    FellippeHeitor

    • Guest
    Re: Cloned Shades
    « Reply #14 on: April 16, 2019, 10:25:00 am »
  • Best Answer
  • This game is addictive! I like more than Tetris.

    I agree! Back when I wrote it I was so addicted to playing it on my phone that I decided to give it a try in QB64 so that my mind could do something productive out of it! The guys at UOVO did a very good job creating the concept :-)