QB64.org Forum

Active Forums => Games => Topic started by: bplus on March 06, 2020, 12:52:26 PM

Title: Lights On by FellippeHeitor
Post by: bplus on March 06, 2020, 12:52:26 PM
Lights On

Author: @FellippeHeitor
Source: github/FellippeHeitor
URL: https://github.com/FellippeHeitor/LightsOn/releases
Version: 1.0 Sept, 2018
Tags: [Puzzle]

Description:
This is a logic puzzle, the goal is to get all the lights on. Starts out easy enough... :-))

Source Code:
Code: QB64: [Select]
  1. 'Lights On
  2. 'A game by Fellippe Heitor - [member=2]FellippeHeitor[/member] - fellippe@qb64.org
  3. '
  4. 'Original concept by Avi Olti, Gyora Benedek, Zvi Herman, Revital Bloomberg, Avi Weiner and Michael Ganor
  5. 'https://en.wikipedia.org/wiki/Lights_Out_(game)
  6. '
  7. 'Assets sources acknowledged inside SUB GameSetup
  8.  
  9.  
  10. $EXEICON:'./assets/lightson.ico'
  11.  
  12. CONST true = -1, false = NOT true
  13.  
  14. TYPE obj
  15.     i AS INTEGER
  16.     j AS INTEGER
  17.     x AS INTEGER
  18.     y AS INTEGER
  19.     w AS INTEGER
  20.     h AS INTEGER
  21.     IsOn AS _BYTE
  22.     lastSwitch AS SINGLE
  23.     lastHint AS SINGLE
  24.  
  25.  
  26. DIM SHARED Arena AS LONG, OverlayScreen AS LONG, Bg AS LONG
  27. DIM SHARED LightOn(1 TO 9) AS LONG, LightOff(1 TO 9) AS LONG
  28. DIM SHARED RestartIcon AS LONG, MouseCursor AS LONG
  29. DIM SHARED Ding AS LONG, Piano AS LONG, Switch AS LONG, Bonus AS LONG
  30. DIM SHARED Arial AS LONG, FontHeight AS INTEGER
  31. DIM SHARED maxGridW AS INTEGER, maxGridH AS INTEGER
  32. DIM SHARED lights(1 TO 20, 1 TO 20) AS obj
  33. DIM SHARED start!, moves AS INTEGER, m$
  34. DIM SHARED MinMoves AS INTEGER, Score AS _UNSIGNED LONG
  35. DIM SHARED TryAgain AS _BYTE, TutorialMode AS _BYTE
  36. REDIM SHARED Button(1 TO 1) AS obj, Caption(1 TO UBOUND(Button)) AS STRING
  37.  
  38. 'from p5js.bas - sound system
  39. TYPE new_SoundHandle
  40.     handle AS LONG
  41.     sync AS _BYTE
  42. REDIM SHARED loadedSounds(0) AS new_SoundHandle
  43.  
  44. GameSetup
  45. Intro
  46.  
  47.     SetLevel
  48.     DO
  49.         UpdateScore
  50.         UpdateArena
  51.  
  52.         _DISPLAY
  53.  
  54.         k = _KEYHIT
  55.  
  56.         IF k = 27 THEN SYSTEM
  57.  
  58.         _LIMIT 30
  59.     LOOP UNTIL Victory
  60.  
  61.     'Give time for the last set of bulbs to light up
  62.     DIM LastBulbs AS SINGLE
  63.     LastBulbs = TIMER
  64.     DO
  65.         UpdateArena
  66.         _DISPLAY
  67.     LOOP UNTIL TIMER - LastBulbs > .3
  68.  
  69.     EndScreen
  70.  
  71. SUB Intro
  72.     'Show intro
  73.     IF isLoaded(LightOn(1)) AND isLoaded(LightOff(1)) THEN
  74.         _DEST OverlayScreen
  75.         CLS , 0
  76.         COLOR _RGB32(255, 255, 255), 0
  77.         _PRINTSTRING (_WIDTH / 2 - _PRINTWIDTH("Lights On!") / 2, _HEIGHT - FontHeight * 2), "Lights On!"
  78.         _DEST 0
  79.  
  80.         _PUTIMAGE (_WIDTH / 2 - _WIDTH(LightOff(1)) / 2, 0), LightOff(1)
  81.         _DELAY .5
  82.         Alpha = 0
  83.         p5play Piano
  84.         _FONT 8
  85.         DO
  86.             IF Alpha < 255 THEN Alpha = Alpha + 5 ELSE EXIT DO
  87.             _SETALPHA Alpha, , OverlayScreen
  88.             _CLEARCOLOR _RGB32(0, 0, 0), OverlayScreen
  89.             _SETALPHA Alpha, , LightOn(1)
  90.  
  91.             _PUTIMAGE (_WIDTH / 2 - _WIDTH(LightOn(1)) / 2, 0), LightOn(1)
  92.             _PUTIMAGE , OverlayScreen
  93.             COLOR _RGBA32(255, 255, 255, Alpha), 0
  94.             _PRINTSTRING (_WIDTH / 2 - _PRINTWIDTH("Fellippe Heitor, 2017") / 2, _HEIGHT - FontHeight * 1.5), "Fellippe Heitor, 2017"
  95.  
  96.             _DISPLAY
  97.             _LIMIT 20
  98.         LOOP
  99.  
  100.         _FONT 16
  101.  
  102.         IF _FILEEXISTS("lightson.dat") = false AND isLoaded(MouseCursor) THEN
  103.             'offer tutorial on the first run
  104.             DIM ii AS INTEGER
  105.  
  106.             _DEST OverlayScreen
  107.             CLS , 0
  108.             m$ = "Show instructions?"
  109.             COLOR _RGB32(0, 0, 0), 0
  110.             _PRINTSTRING (_WIDTH / 2 - _PRINTWIDTH(m$) / 2 + 1, _HEIGHT / 2 - FontHeight * 2 + 1), m$
  111.             COLOR _RGB32(255, 255, 255), 0
  112.             _PRINTSTRING (_WIDTH / 2 - _PRINTWIDTH(m$) / 2, _HEIGHT / 2 - FontHeight * 2), m$
  113.             _DEST 0
  114.  
  115.             DO
  116.                 _PUTIMAGE (_WIDTH / 2 - _WIDTH(LightOn(1)) / 2, 0), LightOn(1)
  117.                 _PUTIMAGE , OverlayScreen
  118.  
  119.                 FOR ii = 4 TO 5
  120.                     IF Hovering(Button(ii)) THEN
  121.                         LINE (Button(ii).x + 5, Button(ii).y + 5)-STEP(Button(ii).w, Button(ii).h), _RGB32(0, 0, 0), BF
  122.                         LINE (Button(ii).x, Button(ii).y)-STEP(Button(ii).w, Button(ii).h), _RGB32(255, 255, 255), BF
  123.                     ELSE
  124.                         LINE (Button(ii).x, Button(ii).y)-STEP(Button(ii).w, Button(ii).h), _RGBA32(255, 255, 255, 170), BF
  125.                     END IF
  126.                     COLOR _RGB32(0, 0, 0), 0
  127.                     _PRINTSTRING (Button(ii).x + Button(ii).w / 2 - _PRINTWIDTH(Caption(ii)) / 2, Button(ii).y + Button(ii).h / 2 - FontHeight / 2), Caption(ii)
  128.                 NEXT
  129.  
  130.                 IF _MOUSEBUTTON(1) THEN
  131.                     WHILE _MOUSEBUTTON(1): ii = _MOUSEINPUT: WEND
  132.                     IF Hovering(Button(5)) THEN
  133.                         EXIT DO
  134.                     ELSEIF Hovering(Button(4)) THEN
  135.                         TutorialMode = true
  136.                         ShowTutorial
  137.                         OPEN "lightson.dat" FOR OUTPUT AS #1
  138.                         CLOSE #1
  139.                         TutorialMode = false
  140.                         EXIT DO
  141.                     END IF
  142.                 END IF
  143.  
  144.                 _DISPLAY
  145.                 _LIMIT 30
  146.             LOOP
  147.         END IF
  148.     END IF
  149.  
  150. SUB ClickPause
  151.     DO
  152.         k = _KEYHIT
  153.  
  154.         WHILE _MOUSEINPUT: WEND
  155.         IF _MOUSEBUTTON(1) THEN
  156.             WHILE _MOUSEBUTTON(1): i = _MOUSEINPUT: WEND
  157.             EXIT DO
  158.         END IF
  159.  
  160.         _DISPLAY
  161.         _LIMIT 30
  162.     LOOP UNTIL k = 27 OR k = 13
  163.  
  164. SUB CenteredText (Text$)
  165.     DIM tWidth AS INTEGER, tHeight AS INTEGER
  166.  
  167.     tWidth = _PRINTWIDTH(Text$) + 20
  168.     tHeight = FontHeight * 3
  169.  
  170.     LINE (_WIDTH / 2 - tWidth / 2, _HEIGHT / 2 - tHeight / 2)-STEP(tWidth - 1, tHeight - 1), _RGBA32(255, 255, 255, 200), BF
  171.     COLOR _RGB32(255, 255, 255), 0
  172.     _PRINTSTRING (_WIDTH / 2 - _PRINTWIDTH(Text$) / 2 + 1, _HEIGHT / 2 - FontHeight / 2 + 1), Text$
  173.     COLOR _RGB32(0, 0, 0), 0
  174.     _PRINTSTRING (_WIDTH / 2 - _PRINTWIDTH(Text$) / 2, _HEIGHT / 2 - FontHeight / 2), Text$
  175.  
  176. SUB StatusText (Text$)
  177.     COLOR _RGB32(0, 0, 0), _RGB32(255, 255, 255)
  178.     CLS
  179.  
  180.     _PRINTSTRING (_WIDTH / 2 - _PRINTWIDTH(Text$) / 2, _HEIGHT - FontHeight * 1.5), Text$
  181.  
  182. SUB GameSetup
  183.     'Acknowledgements:
  184.     '--------------------------------------------------------------------------------------------------------------------
  185.     'Light bulb images from https://blog.1000bulbs.com/home/flip-the-switch-how-an-incandescent-light-bulb-works
  186.     'End level bg from http://blog-sap.com/analytics/2013/06/14/sap-lumira-new-software-update-general-availability-of-cloud-version-and-emeauk-flash-sale-at-bi2013/
  187.     'Ding sound: https://www.freesound.org/people/Flo_Rayen/sounds/191835/
  188.     'Bonus sound: http://freesound.org/people/LittleRobotSoundFactory/sounds/274183/
  189.     'Piano sound: https://www.freesound.org/people/FoolBoyMedia/sounds/352655/
  190.     'Switch sound: https://www.freesound.org/people/Mindloop/sounds/253659/
  191.     'App icon: http://www.iconarchive.com/show/small-n-flat-icons-by-paomedia/light-bulb-icon.html
  192.     'Restart icon: http://www.iconarchive.com/show/windows-8-icons-by-icons8/Computer-Hardware-Restart-icon.html
  193.     'Mouse cursor icon: http://www.iconarchive.com/show/windows-8-icons-by-icons8/Very-Basic-Cursor-icon.html
  194.     '--------------------------------------------------------------------------------------------------------------------
  195.  
  196.     'Load assets:
  197.     Arena = _NEWIMAGE(600, 600, 32)
  198.  
  199.     'Arial = _LOADFONT("arial.ttf", 24)
  200.     LightOn(1) = loadImage("assets/lighton.png")
  201.     LightOn(2) = loadImage("assets/lighton300.png")
  202.     LightOn(3) = loadImage("assets/lighton120.png")
  203.     LightOn(4) = loadImage("assets/lighton86.png")
  204.     LightOn(5) = loadImage("assets/lighton67.png")
  205.     LightOn(6) = loadImage("assets/lighton60.png")
  206.     LightOn(7) = loadImage("assets/lighton55.png")
  207.     LightOn(8) = loadImage("assets/lighton35.png")
  208.     LightOn(9) = loadImage("assets/lighton30.png")
  209.  
  210.     LightOff(1) = loadImage("assets/lightoff.png")
  211.     LightOff(2) = loadImage("assets/lightoff300.png")
  212.     LightOff(3) = loadImage("assets/lightoff120.png")
  213.     LightOff(4) = loadImage("assets/lightoff86.png")
  214.     LightOff(5) = loadImage("assets/lightoff67.png")
  215.     LightOff(6) = loadImage("assets/lightoff60.png")
  216.     LightOff(7) = loadImage("assets/lightoff55.png")
  217.     LightOff(8) = loadImage("assets/lightoff35.png")
  218.     LightOff(9) = loadImage("assets/lightoff30.png")
  219.  
  220.     Bg = loadImage("assets/bg.jpg")
  221.     RestartIcon = loadImage("assets/restart.png")
  222.     MouseCursor = loadImage("assets/mouse.png")
  223.  
  224.     Ding = loadSound("assets/ding.wav")
  225.     Piano = loadSound("assets/piano.ogg")
  226.     Switch = loadSound("assets/switch.wav")
  227.     Bonus = loadSound("assets/bonus.wav")
  228.  
  229.     IF isLoaded(Bg) THEN _SETALPHA 30, , Bg
  230.     IF Arial > 0 THEN FontHeight = _FONTHEIGHT(Arial) ELSE FontHeight = 16
  231.  
  232.     IF Arial > 0 THEN
  233.         _FONT Arial
  234.         _DEST OverlayScreen
  235.         _FONT Arial
  236.         _DEST 0
  237.     END IF
  238.  
  239.     'Screen setup:
  240.     SCREEN _NEWIMAGE(600, 600 + FontHeight * 2, 32)
  241.     _TITLE "Lights On" + CHR$(0)
  242.  
  243.     OverlayScreen = _NEWIMAGE(_WIDTH / 2, _HEIGHT / 2, 32)
  244.  
  245.     'Set buttons:
  246.     REDIM Button(1 TO 5) AS obj, Caption(1 TO UBOUND(Button)) AS STRING
  247.  
  248.     DIM b AS INTEGER
  249.     b = b + 1: Caption(b) = "Try again"
  250.     Button(b).y = _HEIGHT / 2 + FontHeight * 11.5
  251.     Button(b).w = _PRINTWIDTH(Caption(b)) + 40
  252.     Button(b).x = _WIDTH / 2 - 10 - Button(b).w
  253.     Button(b).h = 40
  254.  
  255.     b = b + 1: Caption(b) = "Next level"
  256.     Button(b).y = _HEIGHT / 2 + FontHeight * 11.5
  257.     Button(b).w = _PRINTWIDTH(Caption(b)) + 40
  258.     Button(b).x = _WIDTH / 2 + 10
  259.     Button(b).h = 40
  260.  
  261.     b = b + 1: Caption(b) = "Restart level"
  262.     IF isLoaded(RestartIcon) THEN
  263.         Button(b).w = _WIDTH(RestartIcon) + 20
  264.         Button(b).h = FontHeight * 2
  265.         Button(b).x = _WIDTH - Button(b).w - 10
  266.         Button(b).y = _HEIGHT - FontHeight - Button(b).h / 2
  267.     ELSE
  268.         Button(b).h = FontHeight * 2
  269.         Button(b).w = _PRINTWIDTH(Caption(b)) + 20
  270.         Button(b).x = _WIDTH - 10 - Button(b).w
  271.         Button(b).y = _HEIGHT - Button(b).h
  272.     END IF
  273.  
  274.     b = b + 1: Caption(b) = "Yes"
  275.     Button(b).y = _HEIGHT / 2 - FontHeight / 2
  276.     Button(b).w = _PRINTWIDTH(Caption(b)) + 40
  277.     Button(b).x = _WIDTH / 2 - 10 - Button(b).w
  278.     Button(b).h = 40
  279.  
  280.     b = b + 1: Caption(b) = "No"
  281.     Button(b).y = _HEIGHT / 2 - FontHeight / 2
  282.     Button(b).w = _PRINTWIDTH(Caption(b)) + 40
  283.     Button(b).x = _WIDTH / 2 + 10
  284.     Button(b).h = 40
  285.  
  286. FUNCTION loadImage& (file$)
  287.     DIM tempHandle&
  288.  
  289.     IF _FILEEXISTS(file$) = 0 THEN EXIT FUNCTION
  290.  
  291.     tempHandle& = _LOADIMAGE(file$, 32)
  292.     IF tempHandle& = -1 THEN 'load failed
  293.         tempHandle& = 0
  294.     END IF
  295.  
  296.     loadImage& = tempHandle&
  297.  
  298. FUNCTION isLoaded%% (imgHandle&)
  299.     isLoaded%% = imgHandle& < -1
  300.  
  301. SUB SetLevel
  302.     IF NOT TryAgain THEN Level = Level + 1
  303.  
  304.     DIM LevelSettings AS INTEGER
  305.     IF Level <= 15 THEN LevelSettings = Level ELSE LevelSettings = _CEIL(RND * 13) + 2
  306.  
  307.     SELECT CASE LevelSettings
  308.         CASE 1
  309.             maxGridW = 1
  310.             maxGridH = 2
  311.             MinMoves = 2
  312.             lightID = 2
  313.         CASE 2
  314.             maxGridW = 2
  315.             maxGridH = 2
  316.             MinMoves = 1
  317.             lightID = 2
  318.         CASE 3, 4
  319.             maxGridW = 4
  320.             maxGridH = 5
  321.             MinMoves = 11
  322.             lightID = 3
  323.         CASE 5
  324.             maxGridW = 5
  325.             maxGridH = 7
  326.             MinMoves = 65
  327.             lightID = 4
  328.         CASE 6
  329.             maxGridW = 10
  330.             maxGridH = 10
  331.             MinMoves = 65
  332.             lightID = 6
  333.         CASE 7, 8
  334.             maxGridW = 7
  335.             maxGridH = 9
  336.             MinMoves = 90
  337.             lightID = 5
  338.         CASE 9, 10
  339.             maxGridW = 7
  340.             maxGridH = 11
  341.             MinMoves = 130
  342.             lightID = 7
  343.         CASE 11, 12
  344.             maxGridW = 9
  345.             maxGridH = 11
  346.             MinMoves = 90
  347.             lightID = 7
  348.         CASE 13, 14
  349.             maxGridW = 11
  350.             maxGridH = 17
  351.             MinMoves = 180
  352.             lightID = 8
  353.         CASE ELSE
  354.             maxGridW = 20
  355.             maxGridH = 20
  356.             MinMoves = 230
  357.             lightID = 9
  358.     END SELECT
  359.  
  360.     maxW = _WIDTH(Arena) / maxGridW
  361.     maxH = _HEIGHT(Arena) / maxGridH
  362.  
  363.     FOR i = 1 TO maxGridW
  364.         FOR j = 1 TO maxGridH
  365.             lights(i, j).x = i * maxW - maxW
  366.             lights(i, j).y = j * maxH - maxH
  367.             lights(i, j).w = maxW - 1
  368.             lights(i, j).h = maxH - 1
  369.             lights(i, j).i = i
  370.             lights(i, j).j = j
  371.             lights(i, j).IsOn = false
  372.         NEXT
  373.     NEXT
  374.  
  375.     DIM rndState AS INTEGER
  376.     FOR rndState = 1 TO maxGridW / 3
  377.         i = _CEIL(RND * maxGridW)
  378.         j = _CEIL(RND * maxGridH)
  379.         SetState lights(i, j)
  380.     NEXT
  381.  
  382.     start! = TIMER
  383.     moves = 0
  384.  
  385. SUB EndScreen
  386.     UpdateArena
  387.     _DEST 0
  388.     _PUTIMAGE (0, 0), Arena
  389.  
  390.     DIM EndAnimationStep AS INTEGER, FinalBonus AS _BYTE
  391.     DIM SlideOpen AS INTEGER, SlideVelocity AS SINGLE
  392.     DIM Snd1 AS _BYTE, Snd2 AS _BYTE, Snd3 AS _BYTE
  393.     DIM FinalLamp1!, FinalLamp2!, FinalLamp3!
  394.     DIM SkipEndAnimation AS _BYTE
  395.     DIM BgXOffset AS SINGLE, BgYOffset AS SINGLE
  396.     DIM BgXSpeed AS SINGLE, BgYSpeed AS SINGLE
  397.  
  398.     Snd1 = false: Snd2 = false: Snd3 = false
  399.     FinalBonus = false
  400.  
  401.     IF isLoaded(LightOn(3)) THEN _SETALPHA 255, , LightOn(3)
  402.  
  403.     Alpha = 0
  404.     TryAgain = false
  405.     EndAnimationStep = 1
  406.     SkipEndAnimation = false
  407.  
  408.     BgXSpeed = .5
  409.     BgYSpeed = .3
  410.     IF isLoaded(Bg) THEN
  411.         BgXOffset = _WIDTH(Bg) - _WIDTH * 1.5
  412.         BgYOffset = _HEIGHT(Bg) - _HEIGHT * 1.5
  413.     END IF
  414.  
  415.     p5play Piano
  416.     DO
  417.         WHILE _MOUSEINPUT: WEND
  418.  
  419.         IF EndAnimationStep < 70 THEN
  420.             _DEST OverlayScreen
  421.             CLS , 0
  422.             m$ = "Level" + STR$(Level) + " - All Lights On!"
  423.             COLOR _RGB32(0, 0, 0), 0
  424.             _PRINTSTRING (_WIDTH / 2 - _PRINTWIDTH(m$) / 2 + 1, _HEIGHT / 2 - 80 - FontHeight + 1), m$
  425.             COLOR _RGB32(255, 255, 255), 0
  426.             _PRINTSTRING (_WIDTH / 2 - _PRINTWIDTH(m$) / 2, _HEIGHT / 2 - 80 - FontHeight), m$
  427.  
  428.             m$ = "Moves used:" + STR$(moves)
  429.             COLOR _RGB32(0, 0, 0), 0
  430.             _PRINTSTRING (_WIDTH / 2 - _PRINTWIDTH(m$) / 2 + 1, _HEIGHT / 2 + FontHeight * 2.5 + 1), m$
  431.             COLOR _RGB32(255, 255, 255), 0
  432.             _PRINTSTRING (_WIDTH / 2 - _PRINTWIDTH(m$) / 2, _HEIGHT / 2 + FontHeight * 2.5), m$
  433.  
  434.             m$ = "Score:" + STR$(Score)
  435.             COLOR _RGB32(0, 0, 0), 0
  436.             _PRINTSTRING (_WIDTH / 2 - _PRINTWIDTH(m$) / 2 + 1, _HEIGHT / 2 + FontHeight * 3.5 + 1), m$
  437.             COLOR _RGB32(255, 255, 255), 0
  438.             _PRINTSTRING (_WIDTH / 2 - _PRINTWIDTH(m$) / 2, _HEIGHT / 2 + FontHeight * 3.5), m$
  439.         END IF
  440.  
  441.         _DEST 0
  442.  
  443.         BgXOffset = BgXOffset + BgXSpeed
  444.         BgYOffset = BgYOffset + BgYSpeed
  445.         IF isLoaded(Bg) THEN
  446.             IF BgXOffset < 0 OR BgXOffset + _WIDTH - 1 > _WIDTH(Bg) THEN BgXSpeed = BgXSpeed * -1
  447.             IF BgYOffset < 0 OR BgYOffset + _HEIGHT - 1 > _HEIGHT(Bg) THEN BgYSpeed = BgYSpeed * -1
  448.             _PUTIMAGE (0, 0)-STEP(_WIDTH - 1, _HEIGHT - 1), Bg, , (BgXOffset, BgYOffset)-STEP(_WIDTH - 1, _HEIGHT - 1)
  449.         END IF
  450.         SELECT CASE EndAnimationStep
  451.             CASE 1
  452.                 IF Alpha < 255 THEN Alpha = Alpha + 10 ELSE EndAnimationStep = 2: SlideOpen = 0: SlideVelocity = 30: Alpha = 0
  453.                 IF NOT isLoaded(Bg) THEN
  454.                     LINE (0, 0)-(_WIDTH, _HEIGHT), _RGBA32(255, 255, 0, Alpha), BF
  455.                     LINE (0, 0)-(_WIDTH, _HEIGHT), _RGBA32(255, 255, 255, Alpha), BF
  456.                 END IF
  457.                 _PUTIMAGE , OverlayScreen
  458.             CASE 2
  459.                 IF NOT isLoaded(Bg) THEN LINE (0, 0)-(_WIDTH, _HEIGHT), _RGBA32(255, 255, 255, 30), BF
  460.                 SlideVelocity = SlideVelocity - .2
  461.                 IF SlideVelocity < 1 THEN SlideVelocity = 1
  462.                 IF SlideOpen < 600 THEN
  463.                     SlideOpen = SlideOpen + SlideVelocity
  464.                 ELSE
  465.                     SlideOpen = 600
  466.                     EndAnimationStep = 3
  467.                     i = _WIDTH / 2 - (SlideOpen / 3.5)
  468.                     j = _HEIGHT / 2 - SlideOpen / 5 + FontHeight * 1.5
  469.                 END IF
  470.  
  471.                 _PUTIMAGE , OverlayScreen
  472.                 DIM b AS INTEGER
  473.                 b = map(SlideOpen, 0, 600, 255, 0)
  474.                 LINE (0, _HEIGHT / 2 - 125 + FontHeight * 1.5)-STEP(SlideOpen, 130), _RGB32(255, 255, 255), BF
  475.                 LINE (0, _HEIGHT / 2 - 120 + FontHeight * 1.5)-STEP(SlideOpen, 120), _RGB32(b * 1.5, b * 1.5 - 50, 0), BF
  476.             CASE IS >= 3
  477.                 EndAnimationStep = EndAnimationStep + 1
  478.                 IF NOT isLoaded(Bg) THEN LINE (0, 0)-(_WIDTH, _HEIGHT), _RGBA32(255, 255, 255, 40), BF
  479.                 _PUTIMAGE , OverlayScreen
  480.                 LINE (0, _HEIGHT / 2 - 125 + FontHeight * 1.5)-STEP(SlideOpen, 130), _RGB32(255, 255, 255), BF
  481.                 LINE (0, _HEIGHT / 2 - 120 + FontHeight * 1.5)-STEP(SlideOpen, 120), _RGB32(b, b - 20, 0), BF
  482.  
  483.                 IF isLoaded(LightOff(3)) THEN
  484.                     _PUTIMAGE (i, j), LightOff(3)
  485.                     _PUTIMAGE (i + SlideOpen / 5, j), LightOff(3)
  486.                     _PUTIMAGE (i + (SlideOpen / 5) * 2, j), LightOff(3)
  487.                 END IF
  488.  
  489.                 IF EndAnimationStep >= 3 THEN
  490.                     IF MinMoves <= MinMoves * 3 THEN
  491.                         IF Snd1 = false THEN p5play Ding: Snd1 = true
  492.                         IF EndAnimationStep = 4 THEN FinalLamp1! = TIMER: Score = Score + 20
  493.  
  494.                         IF EndAnimationStep <= 20 THEN
  495.                             Score = Score + 10
  496.                             IF NOT SkipEndAnimation THEN p5play Switch
  497.                         END IF
  498.  
  499.                         IF isLoaded(LightOn(3)) THEN
  500.                             _SETALPHA constrain(map(TIMER - FinalLamp1!, 0, .3, 0, 255), 0, 255), , LightOn(3)
  501.                             _PUTIMAGE (i, j), LightOn(3)
  502.                         ELSE
  503.                             LINE (i, j)-STEP(SlideOpen / 5, SlideOpen / 5), _RGB32(111, 227, 39), BF
  504.                             LINE (i, j)-STEP(SlideOpen / 5, SlideOpen / 5), _RGB32(0, 0, 0), B
  505.                         END IF
  506.                     END IF
  507.                 END IF
  508.  
  509.                 IF EndAnimationStep > 20 THEN
  510.                     IF moves <= MinMoves * 2 THEN
  511.                         IF Snd2 = false THEN p5play Ding: Snd2 = true
  512.                         IF EndAnimationStep = 21 THEN FinalLamp2! = TIMER: Score = Score + 20
  513.  
  514.                         IF EndAnimationStep <= 40 THEN
  515.                             Score = Score + 10
  516.                             IF NOT SkipEndAnimation THEN p5play Switch
  517.                         END IF
  518.  
  519.                         IF isLoaded(LightOn(3)) THEN
  520.                             _SETALPHA constrain(map(TIMER - FinalLamp2!, 0, .3, 0, 255), 0, 255), , LightOn(3)
  521.                             _PUTIMAGE (i + SlideOpen / 5, j), LightOn(3)
  522.                         ELSE
  523.                             LINE (i + SlideOpen / 5, j)-STEP(SlideOpen / 5, SlideOpen / 5), _RGB32(111, 227, 39), BF
  524.                             LINE (i + SlideOpen / 5, j)-STEP(SlideOpen / 5, SlideOpen / 5), _RGB32(0, 0, 0), B
  525.                         END IF
  526.                     END IF
  527.                 END IF
  528.  
  529.                 IF EndAnimationStep > 40 THEN
  530.                     IF moves <= MinMoves THEN
  531.                         IF Snd3 = false THEN p5play Ding: Snd3 = true
  532.                         IF EndAnimationStep = 41 THEN FinalLamp3! = TIMER: Score = Score + 20
  533.  
  534.                         IF EndAnimationStep <= 60 THEN
  535.                             Score = Score + 10
  536.                             IF NOT SkipEndAnimation THEN p5play Switch
  537.                         END IF
  538.  
  539.                         IF isLoaded(LightOn(3)) THEN
  540.                             _SETALPHA constrain(map(TIMER - FinalLamp3!, 0, .3, 0, 255), 0, 255), , LightOn(3)
  541.                             _PUTIMAGE (i + (SlideOpen / 5) * 2, j), LightOn(3)
  542.                         ELSE
  543.                             LINE (i + (SlideOpen / 5) * 2, j)-STEP(SlideOpen / 5, SlideOpen / 5), _RGB32(111, 227, 39), BF
  544.                             LINE (i + (SlideOpen / 5) * 2, j)-STEP(SlideOpen / 5, SlideOpen / 5), _RGB32(0, 0, 0), B
  545.                         END IF
  546.                     END IF
  547.                 END IF
  548.  
  549.                 IF EndAnimationStep > 60 THEN
  550.                     IF FinalBonus = false THEN
  551.                         FinalBonus = true
  552.                         IF moves < MinMoves THEN
  553.                             Score = Score + 50
  554.                             p5play Bonus
  555.                         END IF
  556.                     ELSE
  557.                         IF moves < MinMoves THEN
  558.                             m$ = "Strategy master! +50 bonus points!"
  559.                             COLOR _RGB32(0, 0, 0), 0
  560.                             _PRINTSTRING (_WIDTH / 2 - _PRINTWIDTH(m$) / 2 + 1, _HEIGHT / 2 + FontHeight * 9.5 + 1), m$
  561.                             COLOR _RGB32(255, 255, 255), 0
  562.                             _PRINTSTRING (_WIDTH / 2 - _PRINTWIDTH(m$) / 2, _HEIGHT / 2 + FontHeight * 9.5), m$
  563.                         END IF
  564.                     END IF
  565.                 END IF
  566.         END SELECT
  567.  
  568.         'Buttons
  569.         IF EndAnimationStep > 60 THEN
  570.             DIM ii AS INTEGER
  571.             FOR ii = 1 TO 2
  572.                 IF Hovering(Button(ii)) THEN
  573.                     LINE (Button(ii).x + 5, Button(ii).y + 5)-STEP(Button(ii).w, Button(ii).h), _RGB32(0, 0, 0), BF
  574.                     LINE (Button(ii).x, Button(ii).y)-STEP(Button(ii).w, Button(ii).h), _RGB32(255, 255, 255), BF
  575.                 ELSE
  576.                     LINE (Button(ii).x, Button(ii).y)-STEP(Button(ii).w, Button(ii).h), _RGBA32(255, 255, 255, 20), BF
  577.                 END IF
  578.                 'COLOR _RGB32(255, 255, 255), 0
  579.                 '_PRINTSTRING (Button(ii).x + Button(ii).w / 2 - _PRINTWIDTH(Caption(ii)) / 2 + 1, Button(ii).y + Button(ii).h / 2 - FontHeight / 2 + 1), Caption(ii)
  580.                 COLOR _RGB32(0, 0, 0), 0
  581.                 _PRINTSTRING (Button(ii).x + Button(ii).w / 2 - _PRINTWIDTH(Caption(ii)) / 2, Button(ii).y + Button(ii).h / 2 - FontHeight / 2), Caption(ii)
  582.             NEXT
  583.         END IF
  584.  
  585.         _DISPLAY
  586.  
  587.         k = _KEYHIT
  588.  
  589.         IF k = 13 AND EndAnimationStep > 60 THEN EXIT DO
  590.         IF k = 27 THEN SYSTEM
  591.  
  592.         IF _MOUSEBUTTON(1) AND EndAnimationStep > 60 THEN
  593.             WHILE _MOUSEBUTTON(1): ii = _MOUSEINPUT: WEND
  594.             IF Hovering(Button(1)) THEN
  595.                 TryAgain = true
  596.                 EXIT DO
  597.             ELSEIF Hovering(Button(2)) THEN
  598.                 EXIT DO
  599.             END IF
  600.         ELSEIF _MOUSEBUTTON(1) THEN
  601.             SkipEndAnimation = true
  602.         END IF
  603.  
  604.         IF NOT SkipEndAnimation THEN _LIMIT 30
  605.     LOOP
  606.  
  607. SUB UpdateArena
  608.     DIM imgWidth AS INTEGER, imgHeight AS INTEGER
  609.     DIM FoundHover AS _BYTE
  610.  
  611.     imgHeight = lights(1, 1).h
  612.     imgWidth = imgHeight
  613.  
  614.     _DEST Arena
  615.     CLS
  616.     FOR i = 1 TO maxGridW
  617.         FOR j = 1 TO maxGridH
  618.             IF isLoaded(LightOff(lightID)) THEN
  619.                 _PUTIMAGE (lights(i, j).x + lights(i, j).w / 2 - imgWidth / 2, lights(i, j).y), LightOff(lightID)
  620.             END IF
  621.             IF lights(i, j).IsOn THEN
  622.                 IF isLoaded(LightOn(lightID)) THEN
  623.                     IF TIMER - lights(i, j).lastSwitch < .3 THEN
  624.                         _SETALPHA constrain(map(TIMER - lights(i, j).lastSwitch, 0, .3, 0, 255), 0, 255), , LightOn(lightID)
  625.                     ELSE
  626.                         _SETALPHA 255, , LightOn(lightID)
  627.                     END IF
  628.                     _PUTIMAGE (lights(i, j).x + lights(i, j).w / 2 - imgWidth / 2, lights(i, j).y), LightOn(lightID)
  629.                 ELSE
  630.                     LINE (lights(i, j).x, lights(i, j).y)-STEP(lights(i, j).w, lights(i, j).h), _RGB32(111, 227, 39), BF
  631.                 END IF
  632.             END IF
  633.             IF Hovering(lights(i, j)) AND FoundHover = false AND TutorialMode = false THEN
  634.                 FoundHover = true
  635.                 LINE (lights(i, j).x, lights(i, j).y)-STEP(lights(i, j).w, lights(i, j).h), _RGBA32(255, 255, 255, 100), BF
  636.                 CheckState lights(i, j)
  637.             END IF
  638.             LINE (lights(i, j).x, lights(i, j).y)-STEP(lights(i, j).w, lights(i, j).h), , B
  639.         NEXT
  640.     NEXT
  641.     _DEST 0
  642.     _PUTIMAGE (0, 0), Arena
  643.  
  644. SUB UpdateScore
  645.     DIM seconds%
  646.     COLOR _RGB32(0, 0, 0), _RGB32(255, 255, 255)
  647.     CLS
  648.  
  649.     IF TIMER > start! THEN
  650.         seconds% = TIMER - start!
  651.     ELSE
  652.         seconds% = 86400 - start: start! = TIMER
  653.     END IF
  654.  
  655.     m$ = "Level:" + STR$(Level) + " (" + LTRIM$(STR$(maxGridW)) + "x" + LTRIM$(STR$(maxGridH)) + ")    Moves:" + STR$(moves) + "    Time elapsed:" + STR$(seconds%) + "s"
  656.     _PRINTSTRING (10, _HEIGHT - FontHeight * 1.5), m$
  657.  
  658.     IF Hovering(Button(3)) THEN
  659.         LINE (Button(3).x, Button(3).y)-STEP(Button(3).w - 1, Button(3).h - 1), _RGB32(127, 127, 127), BF
  660.         IF _MOUSEBUTTON(1) THEN
  661.             WHILE _MOUSEBUTTON(1): i = _MOUSEINPUT: WEND
  662.             IF Hovering(Button(3)) THEN
  663.                 TryAgain = true: SetLevel
  664.             END IF
  665.         END IF
  666.     END IF
  667.  
  668.     IF isLoaded(RestartIcon) THEN
  669.         _PUTIMAGE (Button(3).x + Button(3).w / 2 - _WIDTH(RestartIcon) / 2, Button(3).y + Button(3).h / 2 - _HEIGHT(RestartIcon) / 2), RestartIcon
  670.     ELSE
  671.         COLOR _RGB32(0, 0, 0), 0
  672.         _PRINTSTRING (Button(3).x + Button(3).w / 2 - _PRINTWIDTH(Caption(3)) / 2, Button(3).y + Button(3).h / 2 - FontHeight / 2), Caption(3)
  673.     END IF
  674.  
  675. FUNCTION Victory%%
  676.     DIM i AS INTEGER, j AS INTEGER
  677.     FOR i = 1 TO maxGridW
  678.         FOR j = 1 TO maxGridH
  679.             IF lights(i, j).IsOn = false THEN EXIT FUNCTION
  680.         NEXT
  681.     NEXT
  682.  
  683.     Victory%% = true
  684.  
  685. SUB CheckState (object AS obj)
  686.     DIM i AS INTEGER
  687.  
  688.         WHILE _MOUSEBUTTON(1): i = _MOUSEINPUT: WEND
  689.         IF Hovering(object) THEN
  690.             p5play Switch
  691.             moves = moves + 1
  692.             SetState object
  693.         END IF
  694.     END IF
  695.  
  696. SUB SetState (object AS obj)
  697.     DIM ioff AS INTEGER, joff AS INTEGER
  698.     ioff = -1
  699.     joff = 0
  700.     IF object.i + ioff > 0 AND object.i + ioff < maxGridW + 1 AND object.j + joff > 0 AND object.j + joff < maxGridH + 1 THEN
  701.         lights(object.i + ioff, object.j + joff).IsOn = NOT lights(object.i + ioff, object.j + joff).IsOn
  702.         lights(object.i + ioff, object.j + joff).lastSwitch = TIMER
  703.     END IF
  704.  
  705.     ioff = 1
  706.     joff = 0
  707.     IF object.i + ioff > 0 AND object.i + ioff < maxGridW + 1 AND object.j + joff > 0 AND object.j + joff < maxGridH + 1 THEN
  708.         lights(object.i + ioff, object.j + joff).IsOn = NOT lights(object.i + ioff, object.j + joff).IsOn
  709.         lights(object.i + ioff, object.j + joff).lastSwitch = TIMER
  710.     END IF
  711.  
  712.     ioff = 0
  713.     joff = -1
  714.     IF object.i + ioff > 0 AND object.i + ioff < maxGridW + 1 AND object.j + joff > 0 AND object.j + joff < maxGridH + 1 THEN
  715.         lights(object.i + ioff, object.j + joff).IsOn = NOT lights(object.i + ioff, object.j + joff).IsOn
  716.         lights(object.i + ioff, object.j + joff).lastSwitch = TIMER
  717.     END IF
  718.  
  719.     ioff = 0
  720.     joff = 1
  721.     IF object.i + ioff > 0 AND object.i + ioff < maxGridW + 1 AND object.j + joff > 0 AND object.j + joff < maxGridH + 1 THEN
  722.         lights(object.i + ioff, object.j + joff).IsOn = NOT lights(object.i + ioff, object.j + joff).IsOn
  723.         lights(object.i + ioff, object.j + joff).lastSwitch = TIMER
  724.     END IF
  725.  
  726. FUNCTION Hovering%% (object AS obj)
  727.     Hovering%% = _MOUSEX > object.x AND _MOUSEX < object.x + object.w AND _MOUSEY > object.y AND _MOUSEY < object.y + object.h
  728.  
  729. SUB MoveMouse (sx AS INTEGER, sy AS INTEGER, dx AS INTEGER, dy AS INTEGER)
  730.     DIM stepX AS SINGLE, stepY AS SINGLE
  731.     DIM i AS _BYTE
  732.  
  733.     CONST maxSteps = 30
  734.  
  735.     stepX = (dx - sx) / maxSteps
  736.     stepY = (dy - sy) / maxSteps
  737.  
  738.     FOR i = 1 TO maxSteps
  739.         sx = sx + stepX
  740.         sy = sy + stepY
  741.         UpdateArena
  742.         _PUTIMAGE (sx, sy), MouseCursor
  743.         _DISPLAY
  744.         _LIMIT 30
  745.     NEXT
  746.  
  747. SUB ShowTutorial
  748.     DIM i AS INTEGER, j AS INTEGER
  749.     DIM mx AS INTEGER, my AS INTEGER
  750.     DIM StepNumber AS INTEGER, TotalSteps AS INTEGER
  751.  
  752.     Level = 2
  753.     SetLevel
  754.     TotalSteps = 5
  755.  
  756.     StatusText "Tutorial Mode - Click to proceed"
  757.     UpdateArena
  758.     StepNumber = StepNumber + 1
  759.     CenteredText "(" + LTRIM$(STR$(StepNumber)) + "/" + LTRIM$(STR$(TotalSteps)) + ") Your goal is to turn all light bulbs on."
  760.  
  761.     mx = 400
  762.     my = 400
  763.     _PUTIMAGE (mx, my), MouseCursor
  764.  
  765.     _DISPLAY
  766.     ClickPause
  767.     IF k = 27 THEN Level = 0: EXIT SUB
  768.  
  769.     FOR i = 1 TO maxGridW
  770.         FOR j = 1 TO maxGridH
  771.             lights(i, j).IsOn = false
  772.         NEXT
  773.     NEXT
  774.  
  775.     UpdateArena
  776.     StepNumber = StepNumber + 1
  777.     CenteredText "(" + LTRIM$(STR$(StepNumber)) + "/" + LTRIM$(STR$(TotalSteps)) + ") However, you can't simply switch a light bulb on or off directly."
  778.  
  779.     mx = 400
  780.     my = 400
  781.     _PUTIMAGE (mx, my), MouseCursor
  782.  
  783.     _DISPLAY
  784.  
  785.     ClickPause
  786.     IF k = 27 THEN Level = 0: EXIT SUB
  787.  
  788.     UpdateArena
  789.     StepNumber = StepNumber + 1
  790.     CenteredText "(" + LTRIM$(STR$(StepNumber)) + "/" + LTRIM$(STR$(TotalSteps)) + ") You click a light bulb to turn the surrounding ones on/off."
  791.     _PUTIMAGE (mx, my), MouseCursor
  792.     _DISPLAY
  793.  
  794.     ClickPause
  795.     IF k = 27 THEN Level = 0: EXIT SUB
  796.  
  797.     MoveMouse mx, my, lights(2, 2).x + lights(2, 2).w / 2, lights(2, 2).y + lights(2, 2).h / 2
  798.     SetState lights(2, 2)
  799.     p5play Switch
  800.     DO
  801.         UpdateArena
  802.         _PUTIMAGE (mx, my), MouseCursor
  803.         _DISPLAY
  804.     LOOP UNTIL TIMER - lights(2, 1).lastSwitch > .3
  805.  
  806.     UpdateArena
  807.     _PUTIMAGE (mx, my), MouseCursor
  808.     StepNumber = StepNumber + 1
  809.     CenteredText "(" + LTRIM$(STR$(StepNumber)) + "/" + LTRIM$(STR$(TotalSteps)) + ") Continue until all light bulbs are on."
  810.     _DISPLAY
  811.  
  812.     ClickPause
  813.     IF k = 27 THEN Level = 0: EXIT SUB
  814.  
  815.     MoveMouse mx, my, lights(3, 2).x + lights(3, 2).w / 2, lights(3, 2).y + lights(3, 2).h / 2
  816.     SetState lights(3, 2)
  817.     p5play Switch
  818.     DO
  819.         UpdateArena
  820.         _PUTIMAGE (mx, my), MouseCursor
  821.         _DISPLAY
  822.     LOOP UNTIL TIMER - lights(3, 1).lastSwitch > .3
  823.  
  824.     UpdateArena
  825.     StepNumber = StepNumber + 1
  826.     _PUTIMAGE (mx, my), MouseCursor
  827.     CenteredText "(" + LTRIM$(STR$(StepNumber)) + "/" + LTRIM$(STR$(TotalSteps)) + ") Simple right? Click to start."
  828.     _DISPLAY
  829.  
  830.     ClickPause
  831.  
  832.     Level = 0
  833.     EXIT SUB
  834.  
  835. 'functions below are borrowed from p5js.bas:
  836. FUNCTION map! (value!, minRange!, maxRange!, newMinRange!, newMaxRange!)
  837.     map! = ((value! - minRange!) / (maxRange! - minRange!)) * (newMaxRange! - newMinRange!) + newMinRange!
  838.  
  839. FUNCTION min! (a!, b!)
  840.     IF a! < b! THEN min! = a! ELSE min! = b!
  841.  
  842. FUNCTION max! (a!, b!)
  843.     IF a! > b! THEN max! = a! ELSE max! = b!
  844.  
  845. FUNCTION constrain! (n!, low!, high!)
  846.     constrain! = max(min(n!, high!), low!)
  847.  
  848. FUNCTION loadSound& (file$)
  849.     IF _FILEEXISTS(file$) = 0 THEN EXIT FUNCTION
  850.     DIM tempHandle&, setting$
  851.     STATIC totalLoadedSounds AS LONG
  852.  
  853.     setting$ = "vol"
  854.  
  855.     SELECT CASE UCASE$(RIGHT$(file$, 4))
  856.         CASE ".WAV", ".OGG", ".AIF", ".RIF", ".VOC"
  857.             setting$ = "vol,sync,len,pause"
  858.         CASE ".MP3"
  859.             setting$ = "vol,pause,setpos"
  860.     END SELECT
  861.  
  862.     tempHandle& = _SNDOPEN(file$, setting$)
  863.     IF tempHandle& > 0 THEN
  864.         totalLoadedSounds = totalLoadedSounds + 1
  865.         REDIM _PRESERVE loadedSounds(totalLoadedSounds) AS new_SoundHandle
  866.         loadedSounds(totalLoadedSounds).handle = tempHandle&
  867.         loadedSounds(totalLoadedSounds).sync = INSTR(setting$, "sync") > 0
  868.         loadSound& = tempHandle&
  869.     END IF
  870.  
  871. SUB p5play (soundHandle&)
  872.     DIM i AS LONG
  873.     FOR i = 1 TO UBOUND(loadedSounds)
  874.         IF loadedSounds(i).handle = soundHandle& THEN
  875.             IF loadedSounds(i).sync THEN
  876.                 _SNDPLAYCOPY soundHandle&
  877.             ELSE
  878.                 IF NOT _SNDPLAYING(soundHandle&) THEN _SNDPLAY soundHandle&
  879.             END IF
  880.         END IF
  881.     NEXT
  882.  
  883.