'Lights On
'A game by Fellippe Heitor - [member=2]FellippeHeitor[/member] - fellippe@qb64.org
'
'Original concept by Avi Olti, Gyora Benedek, Zvi Herman, Revital Bloomberg, Avi Weiner and Michael Ganor
'https://en.wikipedia.org/wiki/Lights_Out_(game)
'
'Assets sources acknowledged inside SUB GameSetup
'from p5js.bas - sound system
GameSetup
Intro
SetLevel
UpdateScore
UpdateArena
'Give time for the last set of bulbs to light up
UpdateArena
EndScreen
'Show intro
IF isLoaded
(LightOn
(1)) AND isLoaded
(LightOff
(1)) THEN
Alpha = 0
p5play Piano
'offer tutorial on the first run
m$ = "Show instructions?"
LINE (Button
(ii
).x
+ 5, Button
(ii
).y
+ 5)-STEP(Button
(ii
).w
, Button
(ii
).h
), _RGB32(0, 0, 0), BF
LINE (Button
(ii
).x
, Button
(ii
).y
)-STEP(Button
(ii
).w
, Button
(ii
).h
), _RGB32(255, 255, 255), BF
LINE (Button
(ii
).x
, Button
(ii
).y
)-STEP(Button
(ii
).w
, Button
(ii
).h
), _RGBA32(255, 255, 255, 170), BF
_PRINTSTRING (Button
(ii
).x
+ Button
(ii
).w
/ 2 - _PRINTWIDTH(Caption
(ii
)) / 2, Button
(ii
).y
+ Button
(ii
).h
/ 2 - FontHeight
/ 2), Caption
(ii
)
TutorialMode = true
ShowTutorial
TutorialMode = false
tHeight = FontHeight * 3
'Acknowledgements:
'--------------------------------------------------------------------------------------------------------------------
'Light bulb images from https://blog.1000bulbs.com/home/flip-the-switch-how-an-incandescent-light-bulb-works
'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/
'Ding sound: https://www.freesound.org/people/Flo_Rayen/sounds/191835/
'Bonus sound: http://freesound.org/people/LittleRobotSoundFactory/sounds/274183/
'Piano sound: https://www.freesound.org/people/FoolBoyMedia/sounds/352655/
'Switch sound: https://www.freesound.org/people/Mindloop/sounds/253659/
'App icon: http://www.iconarchive.com/show/small-n-flat-icons-by-paomedia/light-bulb-icon.html
'Restart icon: http://www.iconarchive.com/show/windows-8-icons-by-icons8/Computer-Hardware-Restart-icon.html
'Mouse cursor icon: http://www.iconarchive.com/show/windows-8-icons-by-icons8/Very-Basic-Cursor-icon.html
'--------------------------------------------------------------------------------------------------------------------
'Load assets:
'Arial = _LOADFONT("arial.ttf", 24)
LightOn(1) = loadImage("assets/lighton.png")
LightOn(2) = loadImage("assets/lighton300.png")
LightOn(3) = loadImage("assets/lighton120.png")
LightOn(4) = loadImage("assets/lighton86.png")
LightOn(5) = loadImage("assets/lighton67.png")
LightOn(6) = loadImage("assets/lighton60.png")
LightOn(7) = loadImage("assets/lighton55.png")
LightOn(8) = loadImage("assets/lighton35.png")
LightOn(9) = loadImage("assets/lighton30.png")
LightOff(1) = loadImage("assets/lightoff.png")
LightOff(2) = loadImage("assets/lightoff300.png")
LightOff(3) = loadImage("assets/lightoff120.png")
LightOff(4) = loadImage("assets/lightoff86.png")
LightOff(5) = loadImage("assets/lightoff67.png")
LightOff(6) = loadImage("assets/lightoff60.png")
LightOff(7) = loadImage("assets/lightoff55.png")
LightOff(8) = loadImage("assets/lightoff35.png")
LightOff(9) = loadImage("assets/lightoff30.png")
Bg = loadImage("assets/bg.jpg")
RestartIcon = loadImage("assets/restart.png")
MouseCursor = loadImage("assets/mouse.png")
Ding = loadSound("assets/ding.wav")
Piano = loadSound("assets/piano.ogg")
Switch = loadSound("assets/switch.wav")
Bonus = loadSound("assets/bonus.wav")
'Screen setup:
'Set buttons:
b = b + 1: Caption(b) = "Try again"
Button
(b
).y
= _HEIGHT / 2 + FontHeight
* 11.5 Button
(b
).x
= _WIDTH / 2 - 10 - Button
(b
).w
Button(b).h = 40
b = b + 1: Caption(b) = "Next level"
Button
(b
).y
= _HEIGHT / 2 + FontHeight
* 11.5 Button(b).h = 40
b = b + 1: Caption(b) = "Restart level"
Button
(b
).w
= _WIDTH(RestartIcon
) + 20 Button(b).h = FontHeight * 2
Button
(b
).x
= _WIDTH - Button
(b
).w
- 10 Button
(b
).y
= _HEIGHT - FontHeight
- Button
(b
).h
/ 2 Button(b).h = FontHeight * 2
Button
(b
).x
= _WIDTH - 10 - Button
(b
).w
Button
(b
).y
= _HEIGHT - Button
(b
).h
b = b + 1: Caption(b) = "Yes"
Button
(b
).y
= _HEIGHT / 2 - FontHeight
/ 2 Button
(b
).x
= _WIDTH / 2 - 10 - Button
(b
).w
Button(b).h = 40
b = b + 1: Caption(b) = "No"
Button
(b
).y
= _HEIGHT / 2 - FontHeight
/ 2 Button(b).h = 40
IF tempHandle&
= -1 THEN 'load failed tempHandle& = 0
loadImage& = tempHandle&
isLoaded%% = imgHandle& < -1
maxGridW = 1
maxGridH = 2
MinMoves = 2
lightID = 2
maxGridW = 2
maxGridH = 2
MinMoves = 1
lightID = 2
maxGridW = 4
maxGridH = 5
MinMoves = 11
lightID = 3
maxGridW = 5
maxGridH = 7
MinMoves = 65
lightID = 4
maxGridW = 10
maxGridH = 10
MinMoves = 65
lightID = 6
maxGridW = 7
maxGridH = 9
MinMoves = 90
lightID = 5
maxGridW = 7
maxGridH = 11
MinMoves = 130
lightID = 7
maxGridW = 9
maxGridH = 11
MinMoves = 90
lightID = 7
maxGridW = 11
maxGridH = 17
MinMoves = 180
lightID = 8
maxGridW = 20
maxGridH = 20
MinMoves = 230
lightID = 9
maxW
= _WIDTH(Arena
) / maxGridW
lights(i, j).x = i * maxW - maxW
lights(i, j).y = j * maxH - maxH
lights(i, j).w = maxW - 1
lights(i, j).h = maxH - 1
lights(i, j).i = i
lights(i, j).j = j
lights(i, j).IsOn = false
FOR rndState
= 1 TO maxGridW
/ 3 SetState lights(i, j)
moves = 0
UpdateArena
DIM FinalLamp1!
, FinalLamp2!
, FinalLamp3!
Snd1 = false: Snd2 = false: Snd3 = false
FinalBonus = false
Alpha = 0
TryAgain = false
EndAnimationStep = 1
SkipEndAnimation = false
BgXSpeed = .5
BgYSpeed = .3
p5play Piano
m$
= "Level" + STR$(Level
) + " - All Lights On!"
m$
= "Moves used:" + STR$(moves
)
m$
= "Score:" + STR$(Score
)
BgXOffset = BgXOffset + BgXSpeed
BgYOffset = BgYOffset + BgYSpeed
IF Alpha
< 255 THEN Alpha
= Alpha
+ 10 ELSE EndAnimationStep
= 2: SlideOpen
= 0: SlideVelocity
= 30: Alpha
= 0 SlideVelocity = SlideVelocity - .2
IF SlideVelocity
< 1 THEN SlideVelocity
= 1 SlideOpen = SlideOpen + SlideVelocity
SlideOpen = 600
EndAnimationStep = 3
i
= _WIDTH / 2 - (SlideOpen
/ 3.5) j
= _HEIGHT / 2 - SlideOpen
/ 5 + FontHeight
* 1.5
b = map(SlideOpen, 0, 600, 255, 0)
LINE (0, _HEIGHT / 2 - 120 + FontHeight
* 1.5)-STEP(SlideOpen
, 120), _RGB32(b
* 1.5, b
* 1.5 - 50, 0), BF
EndAnimationStep = EndAnimationStep + 1
_PUTIMAGE (i
+ SlideOpen
/ 5, j
), LightOff
(3) _PUTIMAGE (i
+ (SlideOpen
/ 5) * 2, j
), LightOff
(3)
IF MinMoves
<= MinMoves
* 3 THEN IF Snd1
= false
THEN p5play Ding: Snd1
= true
IF EndAnimationStep
= 4 THEN FinalLamp1!
= TIMER: Score
= Score
+ 20
IF EndAnimationStep
<= 20 THEN Score = Score + 10
_SETALPHA constrain
(map
(TIMER - FinalLamp1!
, 0, .3, 0, 255), 0, 255), , LightOn
(3) LINE (i
, j
)-STEP(SlideOpen
/ 5, SlideOpen
/ 5), _RGB32(111, 227, 39), BF
IF Snd2
= false
THEN p5play Ding: Snd2
= true
IF EndAnimationStep
= 21 THEN FinalLamp2!
= TIMER: Score
= Score
+ 20
IF EndAnimationStep
<= 40 THEN Score = Score + 10
_SETALPHA constrain
(map
(TIMER - FinalLamp2!
, 0, .3, 0, 255), 0, 255), , LightOn
(3) LINE (i
+ SlideOpen
/ 5, j
)-STEP(SlideOpen
/ 5, SlideOpen
/ 5), _RGB32(111, 227, 39), BF
LINE (i
+ SlideOpen
/ 5, j
)-STEP(SlideOpen
/ 5, SlideOpen
/ 5), _RGB32(0, 0, 0), B
IF Snd3
= false
THEN p5play Ding: Snd3
= true
IF EndAnimationStep
= 41 THEN FinalLamp3!
= TIMER: Score
= Score
+ 20
IF EndAnimationStep
<= 60 THEN Score = Score + 10
_SETALPHA constrain
(map
(TIMER - FinalLamp3!
, 0, .3, 0, 255), 0, 255), , LightOn
(3) _PUTIMAGE (i
+ (SlideOpen
/ 5) * 2, j
), LightOn
(3) LINE (i
+ (SlideOpen
/ 5) * 2, j
)-STEP(SlideOpen
/ 5, SlideOpen
/ 5), _RGB32(111, 227, 39), BF
LINE (i
+ (SlideOpen
/ 5) * 2, j
)-STEP(SlideOpen
/ 5, SlideOpen
/ 5), _RGB32(0, 0, 0), B
FinalBonus = true
Score = Score + 50
p5play Bonus
m$ = "Strategy master! +50 bonus points!"
'Buttons
LINE (Button
(ii
).x
+ 5, Button
(ii
).y
+ 5)-STEP(Button
(ii
).w
, Button
(ii
).h
), _RGB32(0, 0, 0), BF
LINE (Button
(ii
).x
, Button
(ii
).y
)-STEP(Button
(ii
).w
, Button
(ii
).h
), _RGB32(255, 255, 255), BF
LINE (Button
(ii
).x
, Button
(ii
).y
)-STEP(Button
(ii
).w
, Button
(ii
).h
), _RGBA32(255, 255, 255, 20), BF
'COLOR _RGB32(255, 255, 255), 0
'_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)
_PRINTSTRING (Button
(ii
).x
+ Button
(ii
).w
/ 2 - _PRINTWIDTH(Caption
(ii
)) / 2, Button
(ii
).y
+ Button
(ii
).h
/ 2 - FontHeight
/ 2), Caption
(ii
)
TryAgain = true
SkipEndAnimation = true
imgHeight = lights(1, 1).h
imgWidth = imgHeight
IF isLoaded
(LightOff
(lightID
)) THEN _PUTIMAGE (lights
(i
, j
).x
+ lights
(i
, j
).w
/ 2 - imgWidth
/ 2, lights
(i
, j
).y
), LightOff
(lightID
) IF isLoaded
(LightOn
(lightID
)) THEN _SETALPHA constrain
(map
(TIMER - lights
(i
, j
).lastSwitch
, 0, .3, 0, 255), 0, 255), , LightOn
(lightID
) _PUTIMAGE (lights
(i
, j
).x
+ lights
(i
, j
).w
/ 2 - imgWidth
/ 2, lights
(i
, j
).y
), LightOn
(lightID
) LINE (lights
(i
, j
).x
, lights
(i
, j
).y
)-STEP(lights
(i
, j
).w
, lights
(i
, j
).h
), _RGB32(111, 227, 39), BF
IF Hovering
(lights
(i
, j
)) AND FoundHover
= false
AND TutorialMode
= false
THEN FoundHover = true
LINE (lights
(i
, j
).x
, lights
(i
, j
).y
)-STEP(lights
(i
, j
).w
, lights
(i
, j
).h
), _RGBA32(255, 255, 255, 100), BF
CheckState lights(i, j)
LINE (lights
(i
, j
).x
, lights
(i
, j
).y
)-STEP(lights
(i
, j
).w
, lights
(i
, j
).h
), , B
seconds%
= TIMER - start!
seconds%
= 86400 - start: start!
= TIMER
LINE (Button
(3).x
, Button
(3).y
)-STEP(Button
(3).w
- 1, Button
(3).h
- 1), _RGB32(127, 127, 127), BF
TryAgain = true: SetLevel
_PUTIMAGE (Button
(3).x
+ Button
(3).w
/ 2 - _WIDTH(RestartIcon
) / 2, Button
(3).y
+ Button
(3).h
/ 2 - _HEIGHT(RestartIcon
) / 2), RestartIcon
_PRINTSTRING (Button
(3).x
+ Button
(3).w
/ 2 - _PRINTWIDTH(Caption
(3)) / 2, Button
(3).y
+ Button
(3).h
/ 2 - FontHeight
/ 2), Caption
(3)
Victory%% = true
SUB CheckState
(object
AS obj
)
p5play Switch
moves = moves + 1
SetState object
SUB SetState
(object
AS obj
) ioff = -1
joff = 0
IF object.i
+ ioff
> 0 AND object.i
+ ioff
< maxGridW
+ 1 AND object.j
+ joff
> 0 AND object.j
+ joff
< maxGridH
+ 1 THEN lights
(object.i
+ ioff
, object.j
+ joff
).IsOn
= NOT lights
(object.i
+ ioff
, object.j
+ joff
).IsOn
lights
(object.i
+ ioff
, object.j
+ joff
).lastSwitch
= TIMER
ioff = 1
joff = 0
IF object.i
+ ioff
> 0 AND object.i
+ ioff
< maxGridW
+ 1 AND object.j
+ joff
> 0 AND object.j
+ joff
< maxGridH
+ 1 THEN lights
(object.i
+ ioff
, object.j
+ joff
).IsOn
= NOT lights
(object.i
+ ioff
, object.j
+ joff
).IsOn
lights
(object.i
+ ioff
, object.j
+ joff
).lastSwitch
= TIMER
ioff = 0
joff = -1
IF object.i
+ ioff
> 0 AND object.i
+ ioff
< maxGridW
+ 1 AND object.j
+ joff
> 0 AND object.j
+ joff
< maxGridH
+ 1 THEN lights
(object.i
+ ioff
, object.j
+ joff
).IsOn
= NOT lights
(object.i
+ ioff
, object.j
+ joff
).IsOn
lights
(object.i
+ ioff
, object.j
+ joff
).lastSwitch
= TIMER
ioff = 0
joff = 1
IF object.i
+ ioff
> 0 AND object.i
+ ioff
< maxGridW
+ 1 AND object.j
+ joff
> 0 AND object.j
+ joff
< maxGridH
+ 1 THEN lights
(object.i
+ ioff
, object.j
+ joff
).IsOn
= NOT lights
(object.i
+ ioff
, object.j
+ joff
).IsOn
lights
(object.i
+ ioff
, object.j
+ joff
).lastSwitch
= TIMER
stepX = (dx - sx) / maxSteps
stepY = (dy - sy) / maxSteps
sx = sx + stepX
sy = sy + stepY
UpdateArena
Level = 2
SetLevel
TotalSteps = 5
StatusText "Tutorial Mode - Click to proceed"
UpdateArena
StepNumber = StepNumber + 1
CenteredText
"(" + LTRIM$(STR$(StepNumber
)) + "/" + LTRIM$(STR$(TotalSteps
)) + ") Your goal is to turn all light bulbs on."
mx = 400
my = 400
ClickPause
lights(i, j).IsOn = false
UpdateArena
StepNumber = StepNumber + 1
CenteredText
"(" + LTRIM$(STR$(StepNumber
)) + "/" + LTRIM$(STR$(TotalSteps
)) + ") However, you can't simply switch a light bulb on or off directly."
mx = 400
my = 400
ClickPause
UpdateArena
StepNumber = StepNumber + 1
CenteredText
"(" + LTRIM$(STR$(StepNumber
)) + "/" + LTRIM$(STR$(TotalSteps
)) + ") You click a light bulb to turn the surrounding ones on/off."
ClickPause
MoveMouse mx, my, lights(2, 2).x + lights(2, 2).w / 2, lights(2, 2).y + lights(2, 2).h / 2
SetState lights(2, 2)
p5play Switch
UpdateArena
UpdateArena
StepNumber = StepNumber + 1
CenteredText
"(" + LTRIM$(STR$(StepNumber
)) + "/" + LTRIM$(STR$(TotalSteps
)) + ") Continue until all light bulbs are on."
ClickPause
MoveMouse mx, my, lights(3, 2).x + lights(3, 2).w / 2, lights(3, 2).y + lights(3, 2).h / 2
SetState lights(3, 2)
p5play Switch
UpdateArena
UpdateArena
StepNumber = StepNumber + 1
CenteredText
"(" + LTRIM$(STR$(StepNumber
)) + "/" + LTRIM$(STR$(TotalSteps
)) + ") Simple right? Click to start."
ClickPause
Level = 0
'functions below are borrowed from p5js.bas:
FUNCTION map!
(value!
, minRange!
, maxRange!
, newMinRange!
, newMaxRange!
) map! = ((value! - minRange!) / (maxRange! - minRange!)) * (newMaxRange! - newMinRange!) + newMinRange!
constrain! = max(min(n!, high!), low!)
DIM tempHandle&
, setting$
setting$ = "vol"
CASE ".WAV", ".OGG", ".AIF", ".RIF", ".VOC" setting$ = "vol,sync,len,pause"
setting$ = "vol,pause,setpos"
totalLoadedSounds = totalLoadedSounds + 1
loadedSounds(totalLoadedSounds).handle = tempHandle&
loadedSounds
(totalLoadedSounds
).sync
= INSTR(setting$
, "sync") > 0 loadSound& = tempHandle&
SUB p5play
(soundHandle&
) IF loadedSounds
(i
).handle
= soundHandle&
THEN