diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..018b4dac Binary files /dev/null and b/.DS_Store differ diff --git a/.github/.keep b/.github/.keep new file mode 100644 index 00000000..e69de29b diff --git a/README.md b/README.md index e240001b..78e198c2 100644 --- a/README.md +++ b/README.md @@ -1 +1,40 @@ -REPLACE THIS WITH A DESCRIPTION OF YOUR GAME (in the README.md file). +# StackPack Journey + +# Team Color + +Yellow + +# Developers + +- Aparna Roy (aparnar@udel.edu) +- Emilie Barniak (emiliea@udel.edu) +- Samantha Glover (sjglover@udel.edu) + +# Blurb + +StackPack Journey is a puzzle platform game designed to teach players about the stack data structure and its Last-In-First-Out (LIFO) principle. Players embark on a journey to reunite with their lost soulmate in a strange world. Equipped with a StackPack (a backpack that works like a stack), players must strategically collect (push) and use (pop) items scattered throughout the world to overcome obstacles and ultimately reach their soulmate. Every decision matters, as players must carefully determine exactly when to push and pop items to move forward. By mastering stack manipulation, players progress through the game while gaining a deeper understanding of the stack. + +# Basic Instructions + +- Use the arrow keys to move and jump. +- Press 'E' to PUSH (collect) an item into your StackPack. All collectable items pulsate. +- Press 'F' to POP (use) an item from your StackPack. You can only use items in areas that light up when you go near them! +- You can only POP (use) items from the **top** of your StackPack, so think carefully about the order you'll need to use items when you PUSH and POP. Remember: the **L**ast item you PUSH **I**n is the **F**irst item you can POP **O**ut (**LIFO**). +- If you make a mistake, click the 'Free Pop' button in the top left to POP out items from your StackPack, but use your Free Pops sparingly, as you have a limited number of them. +- Your goal in each level is to reach and unlock the door. Good luck! + +# Screenshot + +![Game Screenshot](https://github.com/UD-S24-CISC374/final-project-yellow/blob/b2d73da0925f4531316612307ef84bf841d3e7a1/docs/large.png?raw=true) + +# Gameplay Video + + + +# Educational Game Design Document + +Link to our [EGDD](https://github.com/UD-S24-CISC374/final-project-yellow/blob/main/docs/egdd.md) + +# Credits + +Link to our [sources](https://github.com/UD-S24-CISC374/final-project-yellow/blob/main/docs/sources.md) diff --git a/assets/.DS_Store b/assets/.DS_Store new file mode 100644 index 00000000..60391a6b Binary files /dev/null and b/assets/.DS_Store differ diff --git a/assets/1Star.png b/assets/1Star.png new file mode 100644 index 00000000..c6f01981 Binary files /dev/null and b/assets/1Star.png differ diff --git a/assets/2Stars.png b/assets/2Stars.png new file mode 100644 index 00000000..81ec63f3 Binary files /dev/null and b/assets/2Stars.png differ diff --git a/assets/Dream.mp3 b/assets/Dream.mp3 new file mode 100644 index 00000000..fa1e8805 Binary files /dev/null and b/assets/Dream.mp3 differ diff --git a/assets/Dude_Monster_Idle_4.png b/assets/Dude_Monster_Idle_4.png new file mode 100644 index 00000000..55fa1561 Binary files /dev/null and b/assets/Dude_Monster_Idle_4.png differ diff --git a/assets/Dude_Monster_Idle_Left4.png b/assets/Dude_Monster_Idle_Left4.png new file mode 100644 index 00000000..9c3cf8fa Binary files /dev/null and b/assets/Dude_Monster_Idle_Left4.png differ diff --git a/assets/Dude_Monster_Run_6.png b/assets/Dude_Monster_Run_6.png new file mode 100644 index 00000000..9c31bc47 Binary files /dev/null and b/assets/Dude_Monster_Run_6.png differ diff --git a/assets/Dude_Monster_Run_Left6.png b/assets/Dude_Monster_Run_Left6.png new file mode 100644 index 00000000..4cb7069d Binary files /dev/null and b/assets/Dude_Monster_Run_Left6.png differ diff --git a/assets/Dude_Monster_Walk_6.png b/assets/Dude_Monster_Walk_6.png new file mode 100644 index 00000000..cfde3122 Binary files /dev/null and b/assets/Dude_Monster_Walk_6.png differ diff --git a/assets/Dude_Monster_Walk_Left6.png b/assets/Dude_Monster_Walk_Left6.png new file mode 100644 index 00000000..13104764 Binary files /dev/null and b/assets/Dude_Monster_Walk_Left6.png differ diff --git a/assets/EF-keys-black.png b/assets/EF-keys-black.png new file mode 100644 index 00000000..bcaa1ca4 Binary files /dev/null and b/assets/EF-keys-black.png differ diff --git a/assets/EF-keys-white.png b/assets/EF-keys-white.png new file mode 100644 index 00000000..41221676 Binary files /dev/null and b/assets/EF-keys-white.png differ diff --git a/assets/FullStars.png b/assets/FullStars.png new file mode 100644 index 00000000..f06924f5 Binary files /dev/null and b/assets/FullStars.png differ diff --git a/assets/Pink_Monster_Climb_4.png b/assets/Pink_Monster_Climb_4.png new file mode 100644 index 00000000..18f9769d Binary files /dev/null and b/assets/Pink_Monster_Climb_4.png differ diff --git a/assets/Pink_Monster_Hurt_4.png b/assets/Pink_Monster_Hurt_4.png new file mode 100644 index 00000000..f65dca6f Binary files /dev/null and b/assets/Pink_Monster_Hurt_4.png differ diff --git a/assets/Pink_Monster_Hurt_Left4.png b/assets/Pink_Monster_Hurt_Left4.png new file mode 100644 index 00000000..5e1096d6 Binary files /dev/null and b/assets/Pink_Monster_Hurt_Left4.png differ diff --git a/assets/Pink_Monster_Idle_4.png b/assets/Pink_Monster_Idle_4.png new file mode 100644 index 00000000..acbd8a53 Binary files /dev/null and b/assets/Pink_Monster_Idle_4.png differ diff --git a/assets/Pink_Monster_Idle_Left4.png b/assets/Pink_Monster_Idle_Left4.png new file mode 100644 index 00000000..7848fd31 Binary files /dev/null and b/assets/Pink_Monster_Idle_Left4.png differ diff --git a/assets/Pink_Monster_Jump_8.png b/assets/Pink_Monster_Jump_8.png new file mode 100644 index 00000000..55506c0f Binary files /dev/null and b/assets/Pink_Monster_Jump_8.png differ diff --git a/assets/Pink_Monster_Run_6.png b/assets/Pink_Monster_Run_6.png new file mode 100644 index 00000000..129a27e6 Binary files /dev/null and b/assets/Pink_Monster_Run_6.png differ diff --git a/assets/Pink_Monster_Run_Left6.png b/assets/Pink_Monster_Run_Left6.png new file mode 100644 index 00000000..ddf545bf Binary files /dev/null and b/assets/Pink_Monster_Run_Left6.png differ diff --git a/assets/Pink_Monster_Walk_6.png b/assets/Pink_Monster_Walk_6.png new file mode 100644 index 00000000..11f6aba4 Binary files /dev/null and b/assets/Pink_Monster_Walk_6.png differ diff --git a/assets/Pink_Monster_Walk_Left6.png b/assets/Pink_Monster_Walk_Left6.png new file mode 100644 index 00000000..5bdc8a7f Binary files /dev/null and b/assets/Pink_Monster_Walk_Left6.png differ diff --git a/assets/SkipButton.png b/assets/SkipButton.png new file mode 100644 index 00000000..a2ed85d8 Binary files /dev/null and b/assets/SkipButton.png differ diff --git a/assets/backpack.png b/assets/backpack.png new file mode 100644 index 00000000..5b712d5b Binary files /dev/null and b/assets/backpack.png differ diff --git a/assets/end-cutscene/end-background1.jpeg b/assets/end-cutscene/end-background1.jpeg new file mode 100644 index 00000000..1a812c7f Binary files /dev/null and b/assets/end-cutscene/end-background1.jpeg differ diff --git a/assets/end-cutscene/final-background.jpg b/assets/end-cutscene/final-background.jpg new file mode 100644 index 00000000..4e3f497a Binary files /dev/null and b/assets/end-cutscene/final-background.jpg differ diff --git a/assets/end-cutscene/heart.png b/assets/end-cutscene/heart.png new file mode 100644 index 00000000..a27dcbef Binary files /dev/null and b/assets/end-cutscene/heart.png differ diff --git a/assets/end-cutscene/play-again-button.png b/assets/end-cutscene/play-again-button.png new file mode 100644 index 00000000..3d144ccc Binary files /dev/null and b/assets/end-cutscene/play-again-button.png differ diff --git a/assets/end-cutscene/sweet-love.mp3 b/assets/end-cutscene/sweet-love.mp3 new file mode 100644 index 00000000..48132104 Binary files /dev/null and b/assets/end-cutscene/sweet-love.mp3 differ diff --git a/assets/end-cutscene/world-map-button.png b/assets/end-cutscene/world-map-button.png new file mode 100644 index 00000000..3cb72c75 Binary files /dev/null and b/assets/end-cutscene/world-map-button.png differ diff --git a/assets/favicon.ico b/assets/favicon.ico index f268a177..5b712d5b 100644 Binary files a/assets/favicon.ico and b/assets/favicon.ico differ diff --git a/assets/favicon2.ico b/assets/favicon2.ico new file mode 100644 index 00000000..906c7f36 Binary files /dev/null and b/assets/favicon2.ico differ diff --git a/assets/flower.png b/assets/flower.png new file mode 100644 index 00000000..ac6e5f30 Binary files /dev/null and b/assets/flower.png differ diff --git a/assets/freePop2.png b/assets/freePop2.png new file mode 100644 index 00000000..7a601385 Binary files /dev/null and b/assets/freePop2.png differ diff --git a/assets/gameMap/game-map.png b/assets/gameMap/game-map.png new file mode 100644 index 00000000..8ef881e7 Binary files /dev/null and b/assets/gameMap/game-map.png differ diff --git a/assets/gameMap/level0-button-completed.png b/assets/gameMap/level0-button-completed.png new file mode 100644 index 00000000..ee594480 Binary files /dev/null and b/assets/gameMap/level0-button-completed.png differ diff --git a/assets/gameMap/level0-button-unlocked.png b/assets/gameMap/level0-button-unlocked.png new file mode 100644 index 00000000..580f3d52 Binary files /dev/null and b/assets/gameMap/level0-button-unlocked.png differ diff --git a/assets/gameMap/level1-button-completed.png b/assets/gameMap/level1-button-completed.png new file mode 100644 index 00000000..f7714d96 Binary files /dev/null and b/assets/gameMap/level1-button-completed.png differ diff --git a/assets/gameMap/level1-button-locked.png b/assets/gameMap/level1-button-locked.png new file mode 100644 index 00000000..c66a271c Binary files /dev/null and b/assets/gameMap/level1-button-locked.png differ diff --git a/assets/gameMap/level1-button-unlocked.png b/assets/gameMap/level1-button-unlocked.png new file mode 100644 index 00000000..264bf9ac Binary files /dev/null and b/assets/gameMap/level1-button-unlocked.png differ diff --git a/assets/gameMap/level2-button-completed.png b/assets/gameMap/level2-button-completed.png new file mode 100644 index 00000000..85db72b6 Binary files /dev/null and b/assets/gameMap/level2-button-completed.png differ diff --git a/assets/gameMap/level2-button-locked.png b/assets/gameMap/level2-button-locked.png new file mode 100644 index 00000000..3537d16b Binary files /dev/null and b/assets/gameMap/level2-button-locked.png differ diff --git a/assets/gameMap/level2-button-unlocked.png b/assets/gameMap/level2-button-unlocked.png new file mode 100644 index 00000000..db099641 Binary files /dev/null and b/assets/gameMap/level2-button-unlocked.png differ diff --git a/assets/gameMap/level3-button-completed.png b/assets/gameMap/level3-button-completed.png new file mode 100644 index 00000000..62d8a7a1 Binary files /dev/null and b/assets/gameMap/level3-button-completed.png differ diff --git a/assets/gameMap/level3-button-locked.png b/assets/gameMap/level3-button-locked.png new file mode 100644 index 00000000..12a35ddf Binary files /dev/null and b/assets/gameMap/level3-button-locked.png differ diff --git a/assets/gameMap/level3-button-unlocked.png b/assets/gameMap/level3-button-unlocked.png new file mode 100644 index 00000000..fba413fd Binary files /dev/null and b/assets/gameMap/level3-button-unlocked.png differ diff --git a/assets/gameMap/stars/0starsDown.png b/assets/gameMap/stars/0starsDown.png new file mode 100644 index 00000000..57e85387 Binary files /dev/null and b/assets/gameMap/stars/0starsDown.png differ diff --git a/assets/gameMap/stars/0starsUp.png b/assets/gameMap/stars/0starsUp.png new file mode 100644 index 00000000..e2b051ba Binary files /dev/null and b/assets/gameMap/stars/0starsUp.png differ diff --git a/assets/gameMap/stars/1starsDown.png b/assets/gameMap/stars/1starsDown.png new file mode 100644 index 00000000..bf126f3d Binary files /dev/null and b/assets/gameMap/stars/1starsDown.png differ diff --git a/assets/gameMap/stars/1starsUp.png b/assets/gameMap/stars/1starsUp.png new file mode 100644 index 00000000..5f1823f0 Binary files /dev/null and b/assets/gameMap/stars/1starsUp.png differ diff --git a/assets/gameMap/stars/2starsDown.png b/assets/gameMap/stars/2starsDown.png new file mode 100644 index 00000000..9a19f842 Binary files /dev/null and b/assets/gameMap/stars/2starsDown.png differ diff --git a/assets/gameMap/stars/2starsUp.png b/assets/gameMap/stars/2starsUp.png new file mode 100644 index 00000000..bbae64d9 Binary files /dev/null and b/assets/gameMap/stars/2starsUp.png differ diff --git a/assets/gameMap/stars/3starsDown.png b/assets/gameMap/stars/3starsDown.png new file mode 100644 index 00000000..7b6cbc83 Binary files /dev/null and b/assets/gameMap/stars/3starsDown.png differ diff --git a/assets/gameMap/stars/3starsUp.png b/assets/gameMap/stars/3starsUp.png new file mode 100644 index 00000000..704ca6f9 Binary files /dev/null and b/assets/gameMap/stars/3starsUp.png differ diff --git a/assets/grassStrip.png b/assets/grassStrip.png new file mode 100644 index 00000000..ad4ae3f3 Binary files /dev/null and b/assets/grassStrip.png differ diff --git a/assets/heart_16.png b/assets/heart_16.png new file mode 100644 index 00000000..ff368ed5 Binary files /dev/null and b/assets/heart_16.png differ diff --git a/assets/img/phaser-logo.png b/assets/img/phaser-logo.png deleted file mode 100644 index d1fc105c..00000000 Binary files a/assets/img/phaser-logo.png and /dev/null differ diff --git a/assets/index.html b/assets/index.html index c7ffa8cc..4a99484a 100644 --- a/assets/index.html +++ b/assets/index.html @@ -1,53 +1,82 @@ - - - - - - - - - - - - - - - - - - - - - - - - <%= htmlWebpackPlugin.options.gameName %> - + + + + + + + + + + + + + + + + + + + + + + + + StackPack Journey + - + - - + - - -
- + + +
+ diff --git a/assets/key.png b/assets/key.png new file mode 100644 index 00000000..f6fd21cb Binary files /dev/null and b/assets/key.png differ diff --git a/assets/level0/EButton.png b/assets/level0/EButton.png new file mode 100644 index 00000000..67b22526 Binary files /dev/null and b/assets/level0/EButton.png differ diff --git a/assets/level0/EtoPush.png b/assets/level0/EtoPush.png new file mode 100644 index 00000000..95854645 Binary files /dev/null and b/assets/level0/EtoPush.png differ diff --git a/assets/level0/FButton.png b/assets/level0/FButton.png new file mode 100644 index 00000000..4072897f Binary files /dev/null and b/assets/level0/FButton.png differ diff --git a/assets/level0/FreePop-Instructions.png b/assets/level0/FreePop-Instructions.png new file mode 100644 index 00000000..6887238c Binary files /dev/null and b/assets/level0/FreePop-Instructions.png differ diff --git a/assets/level0/FtoPop.png b/assets/level0/FtoPop.png new file mode 100644 index 00000000..9a0e7326 Binary files /dev/null and b/assets/level0/FtoPop.png differ diff --git a/assets/level0/Hint-Instructions.png b/assets/level0/Hint-Instructions.png new file mode 100644 index 00000000..b3a8b2e3 Binary files /dev/null and b/assets/level0/Hint-Instructions.png differ diff --git a/assets/level0/LIFO-Instructions.png b/assets/level0/LIFO-Instructions.png new file mode 100644 index 00000000..a4c9284d Binary files /dev/null and b/assets/level0/LIFO-Instructions.png differ diff --git a/assets/level0/Movement-Instructions.png b/assets/level0/Movement-Instructions.png new file mode 100644 index 00000000..6815640c Binary files /dev/null and b/assets/level0/Movement-Instructions.png differ diff --git a/assets/level0/Order-Instructions.png b/assets/level0/Order-Instructions.png new file mode 100644 index 00000000..d55eb8d4 Binary files /dev/null and b/assets/level0/Order-Instructions.png differ diff --git a/assets/level0/Top-Instructions.png b/assets/level0/Top-Instructions.png new file mode 100644 index 00000000..5f1a7c08 Binary files /dev/null and b/assets/level0/Top-Instructions.png differ diff --git a/assets/level0/climbingladder.mp3 b/assets/level0/climbingladder.mp3 new file mode 100644 index 00000000..b1c2c354 Binary files /dev/null and b/assets/level0/climbingladder.mp3 differ diff --git a/assets/level0/door.png b/assets/level0/door.png new file mode 100644 index 00000000..5eb64f52 Binary files /dev/null and b/assets/level0/door.png differ diff --git a/assets/level0/down-arrow.png b/assets/level0/down-arrow.png new file mode 100644 index 00000000..c64ccad1 Binary files /dev/null and b/assets/level0/down-arrow.png differ diff --git a/assets/level0/glowingSpot.png b/assets/level0/glowingSpot.png new file mode 100644 index 00000000..8ce907ac Binary files /dev/null and b/assets/level0/glowingSpot.png differ diff --git a/assets/level0/ladder.png b/assets/level0/ladder.png new file mode 100644 index 00000000..ea3c0a0f Binary files /dev/null and b/assets/level0/ladder.png differ diff --git a/assets/level0/laddersound.mp3 b/assets/level0/laddersound.mp3 new file mode 100644 index 00000000..9059dcd0 Binary files /dev/null and b/assets/level0/laddersound.mp3 differ diff --git a/assets/level0/level0-background.jpg b/assets/level0/level0-background.jpg new file mode 100644 index 00000000..39521f12 Binary files /dev/null and b/assets/level0/level0-background.jpg differ diff --git a/assets/level0/open-door.png b/assets/level0/open-door.png new file mode 100644 index 00000000..318f933e Binary files /dev/null and b/assets/level0/open-door.png differ diff --git a/assets/level0/plank.png b/assets/level0/plank.png new file mode 100644 index 00000000..6b8f9cad Binary files /dev/null and b/assets/level0/plank.png differ diff --git a/assets/level0/planksound.mp3 b/assets/level0/planksound.mp3 new file mode 100644 index 00000000..d9939fa0 Binary files /dev/null and b/assets/level0/planksound.mp3 differ diff --git a/assets/level0/platform.png b/assets/level0/platform.png new file mode 100644 index 00000000..68b80005 Binary files /dev/null and b/assets/level0/platform.png differ diff --git a/assets/level0/spikes1/Chainlinklong.png b/assets/level0/spikes1/Chainlinklong.png new file mode 100644 index 00000000..16370548 Binary files /dev/null and b/assets/level0/spikes1/Chainlinklong.png differ diff --git a/assets/level0/spikes1/Chainlinkshort.png b/assets/level0/spikes1/Chainlinkshort.png new file mode 100644 index 00000000..9736fb00 Binary files /dev/null and b/assets/level0/spikes1/Chainlinkshort.png differ diff --git a/assets/level0/spikes1/FourspikesWood.png b/assets/level0/spikes1/FourspikesWood.png new file mode 100644 index 00000000..b33fb534 Binary files /dev/null and b/assets/level0/spikes1/FourspikesWood.png differ diff --git a/assets/level0/spikes1/SawBig.png b/assets/level0/spikes1/SawBig.png new file mode 100644 index 00000000..51cae99a Binary files /dev/null and b/assets/level0/spikes1/SawBig.png differ diff --git a/assets/level0/spikes1/SawSmall.png b/assets/level0/spikes1/SawSmall.png new file mode 100644 index 00000000..b068d79a Binary files /dev/null and b/assets/level0/spikes1/SawSmall.png differ diff --git a/assets/level0/spikes1/SpikeGroundTrap.png b/assets/level0/spikes1/SpikeGroundTrap.png new file mode 100644 index 00000000..7bf05ea6 Binary files /dev/null and b/assets/level0/spikes1/SpikeGroundTrap.png differ diff --git a/assets/level0/spikes1/SpikeblockSteel.png b/assets/level0/spikes1/SpikeblockSteel.png new file mode 100644 index 00000000..aff0f55d Binary files /dev/null and b/assets/level0/spikes1/SpikeblockSteel.png differ diff --git a/assets/level0/spikes1/SpikeblockWood.png b/assets/level0/spikes1/SpikeblockWood.png new file mode 100644 index 00000000..86c5b367 Binary files /dev/null and b/assets/level0/spikes1/SpikeblockWood.png differ diff --git a/assets/level0/spikes1/SteelspikeRight.png b/assets/level0/spikes1/SteelspikeRight.png new file mode 100644 index 00000000..824a0101 Binary files /dev/null and b/assets/level0/spikes1/SteelspikeRight.png differ diff --git a/assets/level0/spikes1/SteelspikeUp.png b/assets/level0/spikes1/SteelspikeUp.png new file mode 100644 index 00000000..76494731 Binary files /dev/null and b/assets/level0/spikes1/SteelspikeUp.png differ diff --git a/assets/level0/spikes1/WoodspikeRight.png b/assets/level0/spikes1/WoodspikeRight.png new file mode 100644 index 00000000..9b3de3c8 Binary files /dev/null and b/assets/level0/spikes1/WoodspikeRight.png differ diff --git a/assets/level0/spikes1/WoodspikeUp.png b/assets/level0/spikes1/WoodspikeUp.png new file mode 100644 index 00000000..565d9b26 Binary files /dev/null and b/assets/level0/spikes1/WoodspikeUp.png differ diff --git a/assets/level0/spikes1/fourspikesSteel.png b/assets/level0/spikes1/fourspikesSteel.png new file mode 100644 index 00000000..5f3aa4b7 Binary files /dev/null and b/assets/level0/spikes1/fourspikesSteel.png differ diff --git a/assets/level0/spikes1/spikedball.png b/assets/level0/spikes1/spikedball.png new file mode 100644 index 00000000..e82cdab2 Binary files /dev/null and b/assets/level0/spikes1/spikedball.png differ diff --git a/assets/level0/spikes2/.DS_Store b/assets/level0/spikes2/.DS_Store new file mode 100644 index 00000000..fd3b65d8 Binary files /dev/null and b/assets/level0/spikes2/.DS_Store differ diff --git a/assets/level0/spikes2/floor spikes.svg b/assets/level0/spikes2/floor spikes.svg new file mode 100644 index 00000000..ff15fb2c --- /dev/null +++ b/assets/level0/spikes2/floor spikes.svg @@ -0,0 +1,1670 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + Robert Brooks + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/level0/spikes2/info.TXT b/assets/level0/spikes2/info.TXT new file mode 100644 index 00000000..9d85464a --- /dev/null +++ b/assets/level0/spikes2/info.TXT @@ -0,0 +1,17 @@ +Thank you for using and supporting gamedeveloperstudio.com with your help and support I can continue to make the site grow and continue producing assets like this at affordable prices for everyone. + +In brief: + +- You have non exclusive rights to use this asset in commercial or monetized derivative works which you create. + +- The license also allows you to edit the asset + +- Attribution is not requiered + +For full licence information consult the site http://www.gamedeveloperstudio.com/license.php + +If you accidentally overwirte or delete a file remember you can redownload this asset from your profile page at www.gamedeveloperstudio.com + +Thank you + +Robert Brooks \ No newline at end of file diff --git a/assets/level0/spikes2/keyframes/.DS_Store b/assets/level0/spikes2/keyframes/.DS_Store new file mode 100644 index 00000000..79af1dd0 Binary files /dev/null and b/assets/level0/spikes2/keyframes/.DS_Store differ diff --git a/assets/level0/spikes2/keyframes/long_metal/long_metal_spike_01.png b/assets/level0/spikes2/keyframes/long_metal/long_metal_spike_01.png new file mode 100644 index 00000000..be132b86 Binary files /dev/null and b/assets/level0/spikes2/keyframes/long_metal/long_metal_spike_01.png differ diff --git a/assets/level0/spikes2/keyframes/long_metal/long_metal_spike_02.png b/assets/level0/spikes2/keyframes/long_metal/long_metal_spike_02.png new file mode 100644 index 00000000..497ae43a Binary files /dev/null and b/assets/level0/spikes2/keyframes/long_metal/long_metal_spike_02.png differ diff --git a/assets/level0/spikes2/keyframes/long_metal/long_metal_spike_03.png b/assets/level0/spikes2/keyframes/long_metal/long_metal_spike_03.png new file mode 100644 index 00000000..086095fa Binary files /dev/null and b/assets/level0/spikes2/keyframes/long_metal/long_metal_spike_03.png differ diff --git a/assets/level0/spikes2/keyframes/long_metal/long_metal_spike_04.png b/assets/level0/spikes2/keyframes/long_metal/long_metal_spike_04.png new file mode 100644 index 00000000..bcabf95f Binary files /dev/null and b/assets/level0/spikes2/keyframes/long_metal/long_metal_spike_04.png differ diff --git a/assets/level0/spikes2/keyframes/long_metal_spike.png b/assets/level0/spikes2/keyframes/long_metal_spike.png new file mode 100644 index 00000000..6a4d529d Binary files /dev/null and b/assets/level0/spikes2/keyframes/long_metal_spike.png differ diff --git a/assets/level0/spikes2/keyframes/long_wood/long_wood_spike_01.png b/assets/level0/spikes2/keyframes/long_wood/long_wood_spike_01.png new file mode 100644 index 00000000..1ecf4ff1 Binary files /dev/null and b/assets/level0/spikes2/keyframes/long_wood/long_wood_spike_01.png differ diff --git a/assets/level0/spikes2/keyframes/long_wood/long_wood_spike_02.png b/assets/level0/spikes2/keyframes/long_wood/long_wood_spike_02.png new file mode 100644 index 00000000..68244130 Binary files /dev/null and b/assets/level0/spikes2/keyframes/long_wood/long_wood_spike_02.png differ diff --git a/assets/level0/spikes2/keyframes/long_wood/long_wood_spike_03.png b/assets/level0/spikes2/keyframes/long_wood/long_wood_spike_03.png new file mode 100644 index 00000000..6de51a0b Binary files /dev/null and b/assets/level0/spikes2/keyframes/long_wood/long_wood_spike_03.png differ diff --git a/assets/level0/spikes2/keyframes/long_wood/long_wood_spike_04.png b/assets/level0/spikes2/keyframes/long_wood/long_wood_spike_04.png new file mode 100644 index 00000000..74a3f380 Binary files /dev/null and b/assets/level0/spikes2/keyframes/long_wood/long_wood_spike_04.png differ diff --git a/assets/level0/spikes2/keyframes/long_wood/long_wood_spike_05.png b/assets/level0/spikes2/keyframes/long_wood/long_wood_spike_05.png new file mode 100644 index 00000000..d784b6b3 Binary files /dev/null and b/assets/level0/spikes2/keyframes/long_wood/long_wood_spike_05.png differ diff --git a/assets/level0/spikes2/keyframes/long_wood_spike.png b/assets/level0/spikes2/keyframes/long_wood_spike.png new file mode 100644 index 00000000..0a197c66 Binary files /dev/null and b/assets/level0/spikes2/keyframes/long_wood_spike.png differ diff --git a/assets/level0/spikes2/keyframes/small_metal/small_metal_spike_01.png b/assets/level0/spikes2/keyframes/small_metal/small_metal_spike_01.png new file mode 100644 index 00000000..237bb03d Binary files /dev/null and b/assets/level0/spikes2/keyframes/small_metal/small_metal_spike_01.png differ diff --git a/assets/level0/spikes2/keyframes/small_metal/small_metal_spike_02.png b/assets/level0/spikes2/keyframes/small_metal/small_metal_spike_02.png new file mode 100644 index 00000000..b123460a Binary files /dev/null and b/assets/level0/spikes2/keyframes/small_metal/small_metal_spike_02.png differ diff --git a/assets/level0/spikes2/keyframes/small_metal/small_metal_spike_03.png b/assets/level0/spikes2/keyframes/small_metal/small_metal_spike_03.png new file mode 100644 index 00000000..2bc930eb Binary files /dev/null and b/assets/level0/spikes2/keyframes/small_metal/small_metal_spike_03.png differ diff --git a/assets/level0/spikes2/keyframes/small_metal_spike.png b/assets/level0/spikes2/keyframes/small_metal_spike.png new file mode 100644 index 00000000..5c5a7206 Binary files /dev/null and b/assets/level0/spikes2/keyframes/small_metal_spike.png differ diff --git a/assets/level0/spikes2/keyframes/small_wood/small_wood_spike_01.png b/assets/level0/spikes2/keyframes/small_wood/small_wood_spike_01.png new file mode 100644 index 00000000..a3cdefcd Binary files /dev/null and b/assets/level0/spikes2/keyframes/small_wood/small_wood_spike_01.png differ diff --git a/assets/level0/spikes2/keyframes/small_wood/small_wood_spike_02.png b/assets/level0/spikes2/keyframes/small_wood/small_wood_spike_02.png new file mode 100644 index 00000000..ac5a50be Binary files /dev/null and b/assets/level0/spikes2/keyframes/small_wood/small_wood_spike_02.png differ diff --git a/assets/level0/spikes2/keyframes/small_wood/small_wood_spike_03.png b/assets/level0/spikes2/keyframes/small_wood/small_wood_spike_03.png new file mode 100644 index 00000000..b003ccba Binary files /dev/null and b/assets/level0/spikes2/keyframes/small_wood/small_wood_spike_03.png differ diff --git a/assets/level0/spikes2/keyframes/small_wood/small_wood_spike_04.png b/assets/level0/spikes2/keyframes/small_wood/small_wood_spike_04.png new file mode 100644 index 00000000..3956c1b9 Binary files /dev/null and b/assets/level0/spikes2/keyframes/small_wood/small_wood_spike_04.png differ diff --git a/assets/level0/spikes2/keyframes/small_wood_spike.png b/assets/level0/spikes2/keyframes/small_wood_spike.png new file mode 100644 index 00000000..4c48a9bd Binary files /dev/null and b/assets/level0/spikes2/keyframes/small_wood_spike.png differ diff --git a/assets/level0/tutorialmusic.mp3 b/assets/level0/tutorialmusic.mp3 new file mode 100644 index 00000000..a9b8a5e7 Binary files /dev/null and b/assets/level0/tutorialmusic.mp3 differ diff --git a/assets/level1/Jungle.wav b/assets/level1/Jungle.wav new file mode 100644 index 00000000..12dd772a Binary files /dev/null and b/assets/level1/Jungle.wav differ diff --git a/assets/level1/Level1Background.jpg b/assets/level1/Level1Background.jpg new file mode 100644 index 00000000..689387fd Binary files /dev/null and b/assets/level1/Level1Background.jpg differ diff --git a/assets/level1/LgPlatform.png b/assets/level1/LgPlatform.png new file mode 100644 index 00000000..09b12f63 Binary files /dev/null and b/assets/level1/LgPlatform.png differ diff --git a/assets/level1/MdPlatform.png b/assets/level1/MdPlatform.png new file mode 100644 index 00000000..fcf795d4 Binary files /dev/null and b/assets/level1/MdPlatform.png differ diff --git a/assets/level1/SmPlatform.png b/assets/level1/SmPlatform.png new file mode 100644 index 00000000..e3364741 Binary files /dev/null and b/assets/level1/SmPlatform.png differ diff --git a/assets/level1/banana.png b/assets/level1/banana.png new file mode 100644 index 00000000..876bd89a Binary files /dev/null and b/assets/level1/banana.png differ diff --git a/assets/level1/bananaBubble.png b/assets/level1/bananaBubble.png new file mode 100644 index 00000000..8804870d Binary files /dev/null and b/assets/level1/bananaBubble.png differ diff --git a/assets/level1/boing.mp3 b/assets/level1/boing.mp3 new file mode 100644 index 00000000..d18e5add Binary files /dev/null and b/assets/level1/boing.mp3 differ diff --git a/assets/level1/brown-door-open.png b/assets/level1/brown-door-open.png new file mode 100644 index 00000000..e18c2321 Binary files /dev/null and b/assets/level1/brown-door-open.png differ diff --git a/assets/level1/brown-door.png b/assets/level1/brown-door.png new file mode 100644 index 00000000..7ed6e988 Binary files /dev/null and b/assets/level1/brown-door.png differ diff --git a/assets/level1/monkey.png b/assets/level1/monkey.png new file mode 100644 index 00000000..4fcaf8b1 Binary files /dev/null and b/assets/level1/monkey.png differ diff --git a/assets/level1/monkeysound.wav b/assets/level1/monkeysound.wav new file mode 100644 index 00000000..c092c234 Binary files /dev/null and b/assets/level1/monkeysound.wav differ diff --git a/assets/level1/mushroom.png b/assets/level1/mushroom.png new file mode 100644 index 00000000..d7d11090 Binary files /dev/null and b/assets/level1/mushroom.png differ diff --git a/assets/level1/mushroomSign.png b/assets/level1/mushroomSign.png new file mode 100644 index 00000000..e999fe98 Binary files /dev/null and b/assets/level1/mushroomSign.png differ diff --git a/assets/level1/river.png b/assets/level1/river.png new file mode 100644 index 00000000..0cd68abf Binary files /dev/null and b/assets/level1/river.png differ diff --git a/assets/level1/stackKey.png b/assets/level1/stackKey.png new file mode 100644 index 00000000..7791657a Binary files /dev/null and b/assets/level1/stackKey.png differ diff --git a/assets/level1/stone.png b/assets/level1/stone.png new file mode 100644 index 00000000..43c32b12 Binary files /dev/null and b/assets/level1/stone.png differ diff --git a/assets/level1/swingsound.mp3 b/assets/level1/swingsound.mp3 new file mode 100644 index 00000000..31068170 Binary files /dev/null and b/assets/level1/swingsound.mp3 differ diff --git a/assets/level1/vine.png b/assets/level1/vine.png new file mode 100644 index 00000000..be9c2d63 Binary files /dev/null and b/assets/level1/vine.png differ diff --git a/assets/level1/vineHook.png b/assets/level1/vineHook.png new file mode 100644 index 00000000..511e3bd7 Binary files /dev/null and b/assets/level1/vineHook.png differ diff --git a/assets/level1/vineItem.png b/assets/level1/vineItem.png new file mode 100644 index 00000000..a170e80a Binary files /dev/null and b/assets/level1/vineItem.png differ diff --git a/assets/level1/watersplash.mp3 b/assets/level1/watersplash.mp3 new file mode 100644 index 00000000..f92a8e64 Binary files /dev/null and b/assets/level1/watersplash.mp3 differ diff --git a/assets/level2/Cloud.mp3 b/assets/level2/Cloud.mp3 new file mode 100644 index 00000000..fd1d03c6 Binary files /dev/null and b/assets/level2/Cloud.mp3 differ diff --git a/assets/level2/bird-1.png b/assets/level2/bird-1.png new file mode 100644 index 00000000..7d2add5d Binary files /dev/null and b/assets/level2/bird-1.png differ diff --git a/assets/level2/bird.png b/assets/level2/bird.png new file mode 100644 index 00000000..4c956c2c Binary files /dev/null and b/assets/level2/bird.png differ diff --git a/assets/level2/bushes_png.png b/assets/level2/bushes_png.png new file mode 100644 index 00000000..e50e32f3 Binary files /dev/null and b/assets/level2/bushes_png.png differ diff --git a/assets/level2/cloud-platform.png b/assets/level2/cloud-platform.png new file mode 100644 index 00000000..416cc1d3 Binary files /dev/null and b/assets/level2/cloud-platform.png differ diff --git a/assets/level2/club.webp b/assets/level2/club.webp new file mode 100644 index 00000000..a5aadcce Binary files /dev/null and b/assets/level2/club.webp differ diff --git a/assets/level2/garden-sign.png b/assets/level2/garden-sign.png new file mode 100644 index 00000000..e4accec1 Binary files /dev/null and b/assets/level2/garden-sign.png differ diff --git a/assets/level2/level2-background.png b/assets/level2/level2-background.png new file mode 100644 index 00000000..34ccf35e Binary files /dev/null and b/assets/level2/level2-background.png differ diff --git a/assets/level2/pink-door-open.png b/assets/level2/pink-door-open.png new file mode 100644 index 00000000..4aa109ed Binary files /dev/null and b/assets/level2/pink-door-open.png differ diff --git a/assets/level2/pink-door.png b/assets/level2/pink-door.png new file mode 100644 index 00000000..2fbd6171 Binary files /dev/null and b/assets/level2/pink-door.png differ diff --git a/assets/level2/plant-1.png b/assets/level2/plant-1.png new file mode 100644 index 00000000..aab2d2bb Binary files /dev/null and b/assets/level2/plant-1.png differ diff --git a/assets/level2/plant-2.png b/assets/level2/plant-2.png new file mode 100644 index 00000000..5e8ffd3a Binary files /dev/null and b/assets/level2/plant-2.png differ diff --git a/assets/level2/plant.png b/assets/level2/plant.png new file mode 100644 index 00000000..327f80fd Binary files /dev/null and b/assets/level2/plant.png differ diff --git a/assets/level2/pot.png b/assets/level2/pot.png new file mode 100644 index 00000000..68abd534 Binary files /dev/null and b/assets/level2/pot.png differ diff --git a/assets/level2/seeds.png b/assets/level2/seeds.png new file mode 100644 index 00000000..069afead Binary files /dev/null and b/assets/level2/seeds.png differ diff --git a/assets/level2/smog.png b/assets/level2/smog.png new file mode 100644 index 00000000..371ed9ac Binary files /dev/null and b/assets/level2/smog.png differ diff --git a/assets/level2/toxic-gas.png b/assets/level2/toxic-gas.png new file mode 100644 index 00000000..83e48b20 Binary files /dev/null and b/assets/level2/toxic-gas.png differ diff --git a/assets/level2/troll.png b/assets/level2/troll.png new file mode 100644 index 00000000..d13c3389 Binary files /dev/null and b/assets/level2/troll.png differ diff --git a/assets/level2/wand.png b/assets/level2/wand.png new file mode 100644 index 00000000..a3b52412 Binary files /dev/null and b/assets/level2/wand.png differ diff --git a/assets/level2/wandsound.mp3 b/assets/level2/wandsound.mp3 new file mode 100644 index 00000000..8bf880c6 Binary files /dev/null and b/assets/level2/wandsound.mp3 differ diff --git a/assets/level2/watering-can.png b/assets/level2/watering-can.png new file mode 100644 index 00000000..33c3f353 Binary files /dev/null and b/assets/level2/watering-can.png differ diff --git a/assets/level2/wateringsound.mp3 b/assets/level2/wateringsound.mp3 new file mode 100644 index 00000000..3e263aac Binary files /dev/null and b/assets/level2/wateringsound.mp3 differ diff --git a/assets/level3/.DS_Store b/assets/level3/.DS_Store new file mode 100644 index 00000000..b9509dd2 Binary files /dev/null and b/assets/level3/.DS_Store differ diff --git a/assets/level3/Dark-chamber.mp3 b/assets/level3/Dark-chamber.mp3 new file mode 100644 index 00000000..045043cf Binary files /dev/null and b/assets/level3/Dark-chamber.mp3 differ diff --git a/assets/level3/Skeleton_With_VFX/.DS_Store b/assets/level3/Skeleton_With_VFX/.DS_Store new file mode 100644 index 00000000..96e09939 Binary files /dev/null and b/assets/level3/Skeleton_With_VFX/.DS_Store differ diff --git a/assets/level3/Skeleton_With_VFX/Skeleton_01_White_Attack1.png b/assets/level3/Skeleton_With_VFX/Skeleton_01_White_Attack1.png new file mode 100644 index 00000000..66a37655 Binary files /dev/null and b/assets/level3/Skeleton_With_VFX/Skeleton_01_White_Attack1.png differ diff --git a/assets/level3/Skeleton_With_VFX/Skeleton_01_White_Attack1_left.png b/assets/level3/Skeleton_With_VFX/Skeleton_01_White_Attack1_left.png new file mode 100644 index 00000000..aafeea0b Binary files /dev/null and b/assets/level3/Skeleton_With_VFX/Skeleton_01_White_Attack1_left.png differ diff --git a/assets/level3/Skeleton_With_VFX/Skeleton_01_White_Attack2.png b/assets/level3/Skeleton_With_VFX/Skeleton_01_White_Attack2.png new file mode 100644 index 00000000..f225abcb Binary files /dev/null and b/assets/level3/Skeleton_With_VFX/Skeleton_01_White_Attack2.png differ diff --git a/assets/level3/Skeleton_With_VFX/Skeleton_01_White_Die.png b/assets/level3/Skeleton_With_VFX/Skeleton_01_White_Die.png new file mode 100644 index 00000000..70d0d45e Binary files /dev/null and b/assets/level3/Skeleton_With_VFX/Skeleton_01_White_Die.png differ diff --git a/assets/level3/Skeleton_With_VFX/Skeleton_01_White_Die_left.png b/assets/level3/Skeleton_With_VFX/Skeleton_01_White_Die_left.png new file mode 100644 index 00000000..baa613db Binary files /dev/null and b/assets/level3/Skeleton_With_VFX/Skeleton_01_White_Die_left.png differ diff --git a/assets/level3/Skeleton_With_VFX/Skeleton_01_White_Hurt.png b/assets/level3/Skeleton_With_VFX/Skeleton_01_White_Hurt.png new file mode 100644 index 00000000..3327581c Binary files /dev/null and b/assets/level3/Skeleton_With_VFX/Skeleton_01_White_Hurt.png differ diff --git a/assets/level3/Skeleton_With_VFX/Skeleton_01_White_Idle.png b/assets/level3/Skeleton_With_VFX/Skeleton_01_White_Idle.png new file mode 100644 index 00000000..4c1a0bbf Binary files /dev/null and b/assets/level3/Skeleton_With_VFX/Skeleton_01_White_Idle.png differ diff --git a/assets/level3/Skeleton_With_VFX/Skeleton_01_White_Walk.png b/assets/level3/Skeleton_With_VFX/Skeleton_01_White_Walk.png new file mode 100644 index 00000000..dd9544c3 Binary files /dev/null and b/assets/level3/Skeleton_With_VFX/Skeleton_01_White_Walk.png differ diff --git a/assets/level3/Skeleton_With_VFX/Skeleton_01_White_Walk_left.png b/assets/level3/Skeleton_With_VFX/Skeleton_01_White_Walk_left.png new file mode 100644 index 00000000..84ac08d2 Binary files /dev/null and b/assets/level3/Skeleton_With_VFX/Skeleton_01_White_Walk_left.png differ diff --git a/assets/level3/chainsaw.png b/assets/level3/chainsaw.png new file mode 100644 index 00000000..bbfb9ed1 Binary files /dev/null and b/assets/level3/chainsaw.png differ diff --git a/assets/level3/dead-tree-cut.png b/assets/level3/dead-tree-cut.png new file mode 100644 index 00000000..989b8045 Binary files /dev/null and b/assets/level3/dead-tree-cut.png differ diff --git a/assets/level3/dead-tree.png b/assets/level3/dead-tree.png new file mode 100644 index 00000000..b9224ab4 Binary files /dev/null and b/assets/level3/dead-tree.png differ diff --git a/assets/level3/electric-danger-sign.png b/assets/level3/electric-danger-sign.png new file mode 100644 index 00000000..8435140e Binary files /dev/null and b/assets/level3/electric-danger-sign.png differ diff --git a/assets/level3/fire.png b/assets/level3/fire.png new file mode 100644 index 00000000..52ad5f91 Binary files /dev/null and b/assets/level3/fire.png differ diff --git a/assets/level3/fireball.png b/assets/level3/fireball.png new file mode 100644 index 00000000..1d3fff55 Binary files /dev/null and b/assets/level3/fireball.png differ diff --git a/assets/level3/first-platform.png b/assets/level3/first-platform.png new file mode 100644 index 00000000..8f8b8243 Binary files /dev/null and b/assets/level3/first-platform.png differ diff --git a/assets/level3/gas-mask.png b/assets/level3/gas-mask.png new file mode 100644 index 00000000..332ac927 Binary files /dev/null and b/assets/level3/gas-mask.png differ diff --git a/assets/level3/lava-platform-med.png b/assets/level3/lava-platform-med.png new file mode 100644 index 00000000..a9196412 Binary files /dev/null and b/assets/level3/lava-platform-med.png differ diff --git a/assets/level3/lava-platform-small.png b/assets/level3/lava-platform-small.png new file mode 100644 index 00000000..4dc21bd2 Binary files /dev/null and b/assets/level3/lava-platform-small.png differ diff --git a/assets/level3/lava-platform.png b/assets/level3/lava-platform.png new file mode 100644 index 00000000..430d247a Binary files /dev/null and b/assets/level3/lava-platform.png differ diff --git a/assets/level3/lava.png b/assets/level3/lava.png new file mode 100644 index 00000000..6dae1037 Binary files /dev/null and b/assets/level3/lava.png differ diff --git a/assets/level3/level3-background-1.jpg b/assets/level3/level3-background-1.jpg new file mode 100644 index 00000000..634dfce3 Binary files /dev/null and b/assets/level3/level3-background-1.jpg differ diff --git a/assets/level3/level3-background.jpg b/assets/level3/level3-background.jpg new file mode 100644 index 00000000..a0d26c0f Binary files /dev/null and b/assets/level3/level3-background.jpg differ diff --git a/assets/level3/lift-off.png b/assets/level3/lift-off.png new file mode 100644 index 00000000..c0668cd0 Binary files /dev/null and b/assets/level3/lift-off.png differ diff --git a/assets/level3/lift-on.png b/assets/level3/lift-on.png new file mode 100644 index 00000000..14fec903 Binary files /dev/null and b/assets/level3/lift-on.png differ diff --git a/assets/level3/red-door-open-1.png b/assets/level3/red-door-open-1.png new file mode 100644 index 00000000..d3491612 Binary files /dev/null and b/assets/level3/red-door-open-1.png differ diff --git a/assets/level3/red-door-open.png b/assets/level3/red-door-open.png new file mode 100644 index 00000000..a4c83b54 Binary files /dev/null and b/assets/level3/red-door-open.png differ diff --git a/assets/level3/red-door.png b/assets/level3/red-door.png new file mode 100644 index 00000000..add59ddf Binary files /dev/null and b/assets/level3/red-door.png differ diff --git a/assets/level3/skeleton-man.png b/assets/level3/skeleton-man.png new file mode 100644 index 00000000..6c4ecef6 Binary files /dev/null and b/assets/level3/skeleton-man.png differ diff --git a/assets/level3/stone1.png b/assets/level3/stone1.png new file mode 100644 index 00000000..db3aaefb Binary files /dev/null and b/assets/level3/stone1.png differ diff --git a/assets/level3/stone2.png b/assets/level3/stone2.png new file mode 100644 index 00000000..fd37e331 Binary files /dev/null and b/assets/level3/stone2.png differ diff --git a/assets/level3/stone3.png b/assets/level3/stone3.png new file mode 100644 index 00000000..9054e816 Binary files /dev/null and b/assets/level3/stone3.png differ diff --git a/assets/level3/sword.png b/assets/level3/sword.png new file mode 100644 index 00000000..fa0fd484 Binary files /dev/null and b/assets/level3/sword.png differ diff --git a/assets/level3/toolbox.png b/assets/level3/toolbox.png new file mode 100644 index 00000000..527c8819 Binary files /dev/null and b/assets/level3/toolbox.png differ diff --git a/assets/level3/toxic-gas-1.png b/assets/level3/toxic-gas-1.png new file mode 100644 index 00000000..c3ac0da5 Binary files /dev/null and b/assets/level3/toxic-gas-1.png differ diff --git a/assets/level3/toxic-gas-barrel.png b/assets/level3/toxic-gas-barrel.png new file mode 100644 index 00000000..5bf7ba72 Binary files /dev/null and b/assets/level3/toxic-gas-barrel.png differ diff --git a/assets/level3/toxic-gas.png b/assets/level3/toxic-gas.png new file mode 100644 index 00000000..8b21e2a5 Binary files /dev/null and b/assets/level3/toxic-gas.png differ diff --git a/assets/level3/water-bucket.png b/assets/level3/water-bucket.png new file mode 100644 index 00000000..0563d36d Binary files /dev/null and b/assets/level3/water-bucket.png differ diff --git a/assets/pause2.png b/assets/pause2.png new file mode 100644 index 00000000..2922a0eb Binary files /dev/null and b/assets/pause2.png differ diff --git a/assets/paused-popup.png b/assets/paused-popup.png new file mode 100644 index 00000000..8f1f277d Binary files /dev/null and b/assets/paused-popup.png differ diff --git a/assets/picnic.png b/assets/picnic.png new file mode 100644 index 00000000..a9f6590c Binary files /dev/null and b/assets/picnic.png differ diff --git a/assets/play-button.png b/assets/play-button.png new file mode 100644 index 00000000..51474d28 Binary files /dev/null and b/assets/play-button.png differ diff --git a/assets/portal.png b/assets/portal.png new file mode 100644 index 00000000..f62c5f10 Binary files /dev/null and b/assets/portal.png differ diff --git a/assets/semitransparent-square.png b/assets/semitransparent-square.png new file mode 100644 index 00000000..b8c287e3 Binary files /dev/null and b/assets/semitransparent-square.png differ diff --git a/assets/sounds/collectsound.mp3 b/assets/sounds/collectsound.mp3 new file mode 100644 index 00000000..03a322a1 Binary files /dev/null and b/assets/sounds/collectsound.mp3 differ diff --git a/assets/sounds/dooropensound.mp3 b/assets/sounds/dooropensound.mp3 new file mode 100644 index 00000000..1c460fda Binary files /dev/null and b/assets/sounds/dooropensound.mp3 differ diff --git a/assets/sounds/injuresound.mp3 b/assets/sounds/injuresound.mp3 new file mode 100644 index 00000000..3258c9d5 Binary files /dev/null and b/assets/sounds/injuresound.mp3 differ diff --git a/assets/sounds/injuresound1.mp3 b/assets/sounds/injuresound1.mp3 new file mode 100644 index 00000000..f988a0f6 Binary files /dev/null and b/assets/sounds/injuresound1.mp3 differ diff --git a/assets/sounds/menusound.mp3 b/assets/sounds/menusound.mp3 new file mode 100644 index 00000000..fff033c3 Binary files /dev/null and b/assets/sounds/menusound.mp3 differ diff --git a/assets/sounds/playerdiesound.mp3 b/assets/sounds/playerdiesound.mp3 new file mode 100644 index 00000000..058dbf46 Binary files /dev/null and b/assets/sounds/playerdiesound.mp3 differ diff --git a/assets/sounds/playerdiesound1.mp3 b/assets/sounds/playerdiesound1.mp3 new file mode 100644 index 00000000..91cbd7fc Binary files /dev/null and b/assets/sounds/playerdiesound1.mp3 differ diff --git a/assets/sounds/popsound.mp3 b/assets/sounds/popsound.mp3 new file mode 100644 index 00000000..212a89eb Binary files /dev/null and b/assets/sounds/popsound.mp3 differ diff --git a/assets/sounds/winsound.mp3 b/assets/sounds/winsound.mp3 new file mode 100644 index 00000000..438914ac Binary files /dev/null and b/assets/sounds/winsound.mp3 differ diff --git a/assets/sounds/wrongsound.mp3 b/assets/sounds/wrongsound.mp3 new file mode 100644 index 00000000..78951e8f Binary files /dev/null and b/assets/sounds/wrongsound.mp3 differ diff --git a/assets/stackpack-mockup.png b/assets/stackpack-mockup.png new file mode 100644 index 00000000..dd1834ef Binary files /dev/null and b/assets/stackpack-mockup.png differ diff --git a/assets/stackpack-storyboard.png b/assets/stackpack-storyboard.png new file mode 100644 index 00000000..d49713de Binary files /dev/null and b/assets/stackpack-storyboard.png differ diff --git a/assets/stackpack.png b/assets/stackpack.png new file mode 100644 index 00000000..feea8350 Binary files /dev/null and b/assets/stackpack.png differ diff --git a/assets/star.png b/assets/star.png new file mode 100644 index 00000000..711e2b68 Binary files /dev/null and b/assets/star.png differ diff --git a/assets/title-screen.png b/assets/title-screen.png new file mode 100644 index 00000000..7ab6a267 Binary files /dev/null and b/assets/title-screen.png differ diff --git a/docs/egdd.md b/docs/egdd.md index 51ed6536..2ee85a76 100644 --- a/docs/egdd.md +++ b/docs/egdd.md @@ -1 +1,215 @@ -REPLACE THIS TEXT WITH YOUR EGDD MARKDOWN. +# StackPack Journey + +## Elevator Pitch + +StackPack Journey is a game that aims to teach students about the stack; the premise is that you have lost your soulmate and your goal is to use your StackPack (a backpack that works like a stack) to navigate through the world by picking up objects to overcome various obstacles to reunite with your soulmate. + +## Influences (Brief) + +- Super Mario Bros.: + + - Medium: Game + - Explanation: In Super Mario Bros., when you go through the levels, you see a map view of the world with the different levels and checkpoints. The player goes through this map while completing the levels to reach the end objective. Our game will have a similar map view of the different levels. Each level in Super Mario Bros. also has a different theme focusing on a different part of the world. There are levels with lava, levels that take place in the sky, etc. Our game will have similar themes to that. + +- Farafalla: + + - Medium: Game + - Explanation: In this game, players need to reach the end of a level, but there are different colored barriers preventing them from just walking through. Matching colored balloons are scattered throughout the map, and once you collect a certain color, you can pass through that boundary. Needing specific items to pass obstacles in a level was the inspiration we took from this game. + +- Love You to Bits: + - Medium: Game + - Explanation: In this mobile game, you are a space explorer looking for the pieces of your robot girlfriend that have gotten spread throughout outer space. The game has a cute, sci-fi aesthetic with visual backgrounds and a tiny main character in this huge world. Our game is taking inspiration from that, as our character will be tiny going through this world trying to find their soulmate by unlocking doors and traveling through the world. + +## Core Gameplay Mechanics (Brief) + +- Brief walkthrough tutorial to demonstrate how to play (Level 0) +- Push objects into the stack using ‘E’ on the keyboard +- Pop objects off of the stack using ‘F’ on the keyboard +- Player moves around the world using arrow keys +- When you obtain the key and use it on the door, you advance to the next level +- After you complete all of the levels, it displays a (victory) scene of finding your soulmate. + +# Learning Aspects + +## Learning Domains + +- Introduction to Data Structures (specifically the stack) + +## Target Audiences + +- Our learners are students who are taking a data structures course where stacks are a topic of discussion. +- For those who enjoy adventure/puzzle games + +## Target Contexts + +- This would be assigned as an additional (optional) resource to help students grasp the idea of a stack and how it works in a course formally teaching about stacks. +- This game would not be appropriate as a classroom activity since it will have many levels and, therefore, will take a long time to complete. + +## Learning Objectives + +- LIFO: By the end of the game, students will be able to explain the concept of LIFO (Last in first out) +- Push/Pop: By the end of the game, students will be able to define the terms “push” and “pop.” +- Visual Representation: By the end of the game, students will be able to illustrate a visual representation of a stack and the process of pushing/popping from that stack. + +## Prerequisite Knowledge + +- Prior to the game, players need to be able to follow simple instructions. +- Prior to the game, players need to be able to define the concept of a stack. +- Prior to the game, players need to be able to explain the general concept of what push() and pop() operations on a stack are. + +## Assessment Measures + +We can give a short pre-test and post-test to assess how much the student has learned from playing the game. + +- Given a stack and a list of push and pop operations that are to be performed on the stack, draw how the stack will look after running those operations. + +# What sets this project apart? + +- Most introductory computer science games focus on teaching how to code with code blocks, etc; however, this game focuses on teaching an important data structure that many beginner computer science students struggle with. +- The aesthetics of this game will appeal to the users and make them want to continue playing/learning. +- Most introductory computer science games also lack a storyline, which makes it unappealing for students to play. This game has a unique and interesting storyline that will compel students to keep playing and learning. +- The real-time visualization of the stack as the user plays will drive home the point of how pushing and popping works to the user. + +# Player Interaction Patterns and Modes + +## Player Interaction Pattern + +- This is a one-player game using the keyboard to play, including arrow keys and a few other specified buttons. + +## Player Modes + +- Single-player: One player will advance through multiple levels where there will be more items than the previous one. + +# Gameplay Objectives + +- Get past an obstacle in a level. + + - Description: When a player has the correct item at the top of their stack and they use it on an obstacle (ex. A boat in the stack and they use it on a lake) they will be able to get past that obstacle (cross the lake). + - Alignment: Doing this successfully shows effective planning, since that item needs to be at the top of the stack to use it. + +- Successfully complete a level. + + - Description: When the player successfully gets to the end door and unlocks it with the key, they have completed the level. + - Alignment: Completing a level shows understanding with the LIFO and Push/Pop concepts, since those are required to use the items in their correct order. + +- Find your Soulmate. + - Description: Completing all the levels will lead you to your soulmate, who you lost at the beginning of the game. + - Alignment: Since each level has more items than the last, getting past all levels shows a true deep understanding of how a stack works. + +# Procedures/Actions + +- You can press ‘E’ when you’re near an item to pick it up and put it into your stack-pack. +- You can press ‘F’ when you’re near a place where you can use an item to use the item there and overcome the obstacle / open the door. +- You can move the main character around with the arrow keys. + +# Rules + +- If the player tries to use an object in an area that is not permitted, the player will not be able to do so. +- If the player uses the correct object in the right area, the player will be able to advance through the level. +- If the player obtains the key and uses it on the door, the player will advance to the next level. +- Over time, the level of difficulty will increase and evolve to incorporate the following: + - Increased number of objects needed to push and pop + - More planning needed to complete the levels as the number of obstacles increases + +# Objects/Entities + +In a Level: + +- There will be a main character that the player can control +- There will be a visible stack that shows your current “inventory” of items in the stack +- There will be platforms across the screen that the player can walk across +- There will be objects players can collect +- There will be obstacles throughout the level +- There will be a highlighted box that shows up when the player gets close enough to an obstacle, letting them know they can use an item on that obstacle + +On Map (showing all levels): + +- There will be a spot for each level in the game, with completed levels colored green, incomplete/inaccessible levels grayed out, and the current level colored blue. + +## Core Gameplay Mechanics (Detailed) + +- “Level 0”: Players will have to complete Level 0 - a short introductory walkthrough that demonstrates the basic mechanics of the game. It will force players to pick objects up and push or pop them off the stack. + +- “Pushing”: (aka. Collecting items) Players will be able to push objects to their stack using the letter ‘E’ when standing over an object. When that prompt is shown and E is pressed, the object will visually appear in the player’s stack-pack. + +- “Popping”: (aka. Using items) Players will be able to pop objects from their stack using the letter ‘F’ when standing over an obstacle to use the objects throughout the levels. When that prompt is shown and the valid object is at the top of the stack, the action is performed (Ex. unlocking the door at the end of the level if the key is at the top of the stack). + +- “Movement”: Players will be able to move around the 2D world using all four arrow keys. Left moves left, right moves right, and up will be used to climb things like ladders. + +- “Next Level”: Players will be able to advance to the next level only if they use the key on the door. The door will swing open and players will go through the door. + +- “All Levels Complete”: When the player completes all of the levels, a victory cutscene will display showing the player finally reconnecting with their soulmate. + +## Feedback + +- When you try to use an object in an area where it’s not supposed to be used, the area will flash red indicating that you can’t do that with a sound effect. +- When you correctly use an object to overcome an area’s obstacle, the area will visibly change (animations) to show the object being used and appropriate sound effects will play based on what the item is. +- When you complete a level by using the correct key on the door, the door will swing open and text will show up saying you did it. A victory sound effect will also play. +- When you complete a level, the next level will “unlock” in the game map (it will be colored blue). Also, the level you just completed will become green showing that you finished it. +- When you win the game by finishing the last level and getting to your soulmate, a big victory message will appear and you will see an animation showing the main character reunite with the soulmate, and sound effects will also play accompanying it. + +# Story and Gameplay + +## Presentation of Rules + +Text shown on the main game screen explains the objective and interaction instructions. Also, Level 0 will interactively show how the game works and how to control the character. + +## Presentation of Content + +- There will be a “Level 0” which will be a walkthrough of the gameplay and core mechanics. The player controls movement, and small boxes show up to guide the player into making the right choices. + - Ex: “-> to walk left”; “walk over this key”; “e to push key onto stack”; “walk over the door”; “e to pop key and unlock door” + - Covers core mechanics movement, pushing, and popping + +## Story (Brief) + +While traveling through this strange, new world together, you and your soulmate suddenly get separated. You must finish traveling throughout the world solo, collecting objects and completing obstacles to reconnect with your lost soulmate. + +## Storyboarding + +![Stack Pack Story Board](https://github.com/UD-S24-CISC374/final-project-yellow/blob/main/assets/stackpack-storyboard.png?raw=true "Story Board") + +Example of a Potential Harder Level: (the numbers represent the order in which you would pick up or use items) +![Stack Pack Harder Level](https://github.com/UD-S24-CISC374/final-project-yellow/blob/241ec885bdf1db4cf54fa24d2dbbc6e61d8eed40/assets/stackpack-mockup.png?raw=true "Harder Level") + +# Assets Needed + +## Aesthetics + +- The aesthetics of this game will be cartoonish/cutesy for a stress-free environment. Players should not feel pressure when playing the game, but rather relaxed. + +## Graphical + +- Characters List + + - Main player: A female non-scary (cute) creature, with animations for movement + - The soulmate: A non-scary (cute) creature + - Note: Might add bad-guy NPC characters to later levels + +- Textures: N/A + +- Environment Art/Textures: + - Level 0 Background: Basic grassy background + - Level 1 Background: Forest-y background with trees and sun + - Level 2 Background: Cloud background, up in the air + - Level 3 Background: Lava/Volcano background + - Note: Aiming for 3 really good levels, after that is if we have time + - Victory Scene: Cutesy background + +## Audio + +- Music List (Ambient sound) + - Level 0: Basic pleasant gameplay music that you normally hear in the background of tutorials + - Level 1: Leaves rustling, wind, forest-vibe music + - Level 2: Dreamy, idyllic music as you are in a cloud dreamworld + - Level 3: Intense, fast-paced music, since this is the final level + - Victory Scene: Dramatic, upbeat, congratulatory music as you just found your soulmate +- Sound List (SFX) + - Jumping + - Door Unlock (door swinging sound) + - Using objects/items (different for each object) + - Hitting an enemy (the enemy’s response to getting hit) + +# Metadata + +- Template created by Austin Cory Bart , Mark Sheriff, Alec Markarian, and Benjamin Stanley. +- Version 0.0.3 diff --git a/docs/large.png b/docs/large.png new file mode 100644 index 00000000..d1ac5614 Binary files /dev/null and b/docs/large.png differ diff --git a/docs/small.png b/docs/small.png new file mode 100644 index 00000000..5f899f12 Binary files /dev/null and b/docs/small.png differ diff --git a/docs/sources.md b/docs/sources.md new file mode 100644 index 00000000..82779a3e --- /dev/null +++ b/docs/sources.md @@ -0,0 +1,94 @@ +# Sources + +### General: + +- StackPack: https://m.media-amazon.com/images/I/71kHEc5wwLL._AC_UY1000_.jpg +- Pink Gal & Blue Guy: https://craftpix.net/freebies/free-pixel-art-tiny-hero-sprites/?num=1&count=215&sq=pink%20monster&pos=4 +- Hearts: https://opengameart.org/content/heart-full-and-empty-for-health +- Level Complete & Pause: https://www.freepik.com/free-vector/set-game-menu-selection-rpg-adventure-game-including-menu-level-selection-options_14245132.htm#fromView=search&page=1&position=7&uuid=041f1bcf-fd58-4f6b-a720-a753efcecd7a +- Level Pause: https://pzuh.itch.io/free-game-gui +- Pause Button: png image from pngtree.com/ +- Key: https://frakassets.itch.io/free-rotating-key +- Collecting Items Sound: https://www.zapsplat.com/music/game-sound-blip-x-1/ +- Unlocking Door Sound: https://www.zapsplat.com/music/game-sound-door-open-unlock-magical-secret-level-could-be-level-up-bonus-award-or-other-positive-action-2/ +- Character Injured Noise: https://www.zapsplat.com/music/game-sound-fantasy-hit-injure-critter-or-weird-insect-negative-tone/ + - OR https://www.zapsplat.com/music/cartoon-voice-high-pitched-grunt-in-pain-5/ +- Incorrect Item Pop Noise: https://www.zapsplat.com/music/game-error-tone-2/ +- Free Pop Noise: https://www.zapsplat.com/music/game-sound-designed-bubble-pop-24/ +- Player Death Noise: https://www.zapsplat.com/music/retro-8-bit-arcade-style-game-sound-negative-death-die-or-lose-life-4/ +- Win Level Noise: https://www.zapsplat.com/music/game-sound-bell-keys-win-or-success-tone/ +- Menu Click: https://www.zapsplat.com/music/game-sound-menu-button-click-2/ +- Other: Canva Graphics + +### Title Scene: + +- Background: https://www.vecteezy.com/photo/30493204-fantasy-landscape-with-trees-and-rocks-3d-render-illustration-ai-generated +- Background Music: https://opengameart.org/content/titlemenu-screen-bgm + +### Game Map Scene: + +- Volcano: https://png.pngtree.com/png-vector/20230803/ourmid/pngtree-erupting-volcano-clipart-cartoon-volcano-isolated-on-white-background-vector-illustration-png-image_6868764.png + +### Level 0 Scene: + +- Platforms: https://opengameart.org/content/platforms +- Background: Image by brgfx on Freepik +- Door: https://www.artstation.com/artwork/BmOoPz +- Spikes 1: https://opengameart.org/content/spikey-stuff +- Spikes 2: https://www.gamedeveloperstudio.com/graphics/viewgraphic.php?page-name=Animated-floor-spikes-game-asset&item=1u5v5h4w8i7s7t806o +- Background Music: https://opengameart.org/content/calm-tutorial +- Plank Noise: https://www.zapsplat.com/music/1x4-plank-of-wood-bash-on-ground/ +- Ladder Noise: https://www.zapsplat.com/music/1x4-plank-of-wood-drop-or-set-down-3/ +- Climbing Noise: https://www.zapsplat.com/music/cartoon-ascend-wood-knocks-delicate-tip-toes-1/ + +### Level 1 Scene: + +- Background: https://www.freepik.com/free-vector/flat-tropical-jungle-background_13643324.htm#fromView=search&page=4&position=11&uuid=ee0e5a5e-ee27-4310-b2b4-27eafb48eec2 +- Platforms + stone + river: https://dinosdouisen.itch.io/jungle-platformer-game-tileset#google_vignette +- Banana, Mushroom, Vine, Monkey, Speech Bubble: Free elements on Canva +- Door: https://www.artstation.com/artwork/BmOoPz +- Background Music: https://opengameart.org/content/jungle +- Water Splash: https://www.zapsplat.com/music/arrow-whoosh-and-hit-water-with-a-splash-21/ +- Boing: https://www.zapsplat.com/music/cartoon-boing-ascend-bounce-jaw-harp-1/ +- Monkey Sound: https://mixkit.co/free-sound-effects/monkey/ +- Vine Swing Sound: https://www.zapsplat.com/music/badminton-or-tennis-racket-fast-whoosh-swoosh-swing-6/ + +### Level 2 Scene: + +- Background: https://craftpix.net/freebies/free-sky-with-clouds-background-pixel-art-set/ +- Cloud Platforms: https://phaser.io/tutorials/coding-tips-004 +- Club: https://clubpenguin.fandom.com/wiki/Ugg_Club +- Pot: [Flowerpot Vectors by Vecteezy](https://www.vecteezy.com/vector-art/14150763-plant-pot-icon-cartoon-vector-farm-tool) +- Bird: https://rmazanek.itch.io/bird-small +- Wand: https://pngimg.com/image/104781 +- Troll: https://free-game-assets.itch.io/2d-trolls-free-sprite +- Plant: https://opengameart.org/content/plant-growing-sprite +- Gardening Sign: https://www.vecteezy.com/png/16548364-watercolor-garden-sign +- Background Music: https://opengameart.org/content/a-lucid-dream +- Wand Sound: https://www.zapsplat.com/music/magic-glissando-bright-and-magical-wand-cast-spell-etc-4/ +- Pouring Sound: https://www.zapsplat.com/music/bucket-load-of-water-sudden-pour-into-deep-water-6/ + +### Level 3 Scene: + +- Background: Image by upklyak on Freepik +- Platforms: https://ludicarts.itch.io/volcano-free-platform-tileset +- Stones: Cartoon Stone Png vectors by Lovepik.com +- Water Bucket: https://cdn.create.vista.com/api/media/small/9995143/stock-vector-clipart-style-cartoon-of-a-bucket +- Fire: https://www.pngitem.com/pimgs/m/152-1526389_long-flame-hd-png-download.png +- Chainsaw: https://freepngimg.com/save/159816-chainsaw-vector-free-download-image/1882x1920 +- Sword: https://img.freepik.com/premium-vector/sword-icon-cartoon-sword-vector-icon-web-design-isolated-white-background_258706-351.jpg +- Skeleton Man: https://monopixelart.itch.io/skeletons-pack +- Green Light: https://www.citypng.com/photo/13282/hd-green-neon-line-light-png +- Red Light: HD Red Neon Glowing Line Effect Transparent PNG +- Toxic Gas Barrel: https://img.freepik.com/premium-vector/barrel-with-nuclear-pollution-biohazard-radioactive-toxic-waste_288189-802.jpg +- Toxic Gas Cloud: https://img.freepik.com/premium-vector/green-smoke-cloud-toxic-smell-cartoon-scent_81894-8958.jpg +- Gas Mask: https://img.freepik.com/premium-vector/stencil-gas-mask-icon-flat-vector-chemical-air-toxic-army-isolated_98396-65151.jpg +- Fireball: https://static.vecteezy.com/system/resources/previews/012/629/333/original/fireball-drop-effect-png.png +- Background Music: https://opengameart.org/content/dark-chamber + +### Ending Cutscene: + +- 1st Background: https://t4.ftcdn.net/jpg/01/01/05/97/360_F_101059744_v3iOQuoEiSyUxcgiILvDzWprTkShqd7c.jpg +- Heart: https://img.freepik.com/free-vector/bright-red-heart_25030-68313.jpg?size=626&ext=jpg&ga=GA1.1.553209589.1715126400&semt=ais +- 2nd Background: Image by Freepik +- Background Music: Music by DayFox from Pixabay diff --git a/package-lock.json b/package-lock.json index 279e7740..4b4d31a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "coding-3-phaser-scenes", + "name": "final-project", "version": "4.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "coding-3-phaser-scenes", + "name": "final-project", "version": "4.0.0", "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index e285b2ae..0ff18a54 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { - "name": "final-project", + "name": "stackpack-journey", "version": "4.0.0", - "description": "Phaser 3 Final Project", - "homepage": "https://github.com/UD-S24-CISC374/final-project-template#readme", + "description": "CISC 374 Final Project", + "homepage": "https://github.com/UD-S24-CISC374/final-project-yellow#readme", "main": "index.js", "scripts": { "start": "webpack serve --config webpack/webpack.dev.js", @@ -21,15 +21,23 @@ "html5 game", "TypeScript", "webpack", - "starter" + "starter", + "stack", + "stackpack", + "journey", + "LIFO", + "push", + "pop", + "education", + "game" ], "author": { - "name": "Austin Cory Bart", - "url": "https://github.com/acbart" + "name": "Aparna Roy, Emilie Barniak, Sam Glover", + "url": "https://github.com/UD-S24-CISC374/final-project-yellow" }, "repository": { "type": "git", - "url": "https://github.com/UD-S24-CISC374/final-project-template.git" + "url": "https://github.com/UD-S24-CISC374/final-project-yellow.git" }, "engines": { "node": ">=12" diff --git a/pwa/icons/icons-192-1.png b/pwa/icons/icons-192-1.png new file mode 100644 index 00000000..16bebab8 Binary files /dev/null and b/pwa/icons/icons-192-1.png differ diff --git a/pwa/icons/icons-192.png b/pwa/icons/icons-192.png index 16bebab8..5b712d5b 100644 Binary files a/pwa/icons/icons-192.png and b/pwa/icons/icons-192.png differ diff --git a/pwa/icons/icons-512-1.png b/pwa/icons/icons-512-1.png new file mode 100644 index 00000000..0e536097 Binary files /dev/null and b/pwa/icons/icons-512-1.png differ diff --git a/pwa/icons/icons-512.png b/pwa/icons/icons-512.png index 0e536097..5b712d5b 100644 Binary files a/pwa/icons/icons-512.png and b/pwa/icons/icons-512.png differ diff --git a/pwa/manifest.json b/pwa/manifest.json index fa202a58..bfeb6e15 100644 --- a/pwa/manifest.json +++ b/pwa/manifest.json @@ -1,6 +1,6 @@ { - "short_name": "Phaser Game", - "name": "My Cool Phaser 3 Game", + "short_name": "StackPack", + "name": "StackPack Journey", "icons": [ { "src": "./icons/icons-192.png", diff --git a/src/config.ts b/src/config.ts index 9776bc5c..4eb2d6ef 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,12 +1,24 @@ import Phaser from "phaser"; import MainScene from "./scenes/mainScene"; import PreloadScene from "./scenes/preloadScene"; +import TitleScreen from "./scenes/titleScreen"; +import GameMap from "./scenes/gameMap"; +import LevelZero from "./scenes/levelZero"; +import youDiedScene from "./scenes/youDiedScene"; +import LevelOne from "./scenes/levelOne"; +import LevelThree from "./scenes/levelThree"; +import LevelTwo from "./scenes/levelTwo"; +import youDiedScene3 from "./scenes/youDiedScene3"; +import youDiedScene1 from "./scenes/youDiedScene1"; +import youDiedScene2 from "./scenes/youDiedScene2"; +import EndCutScene from "./scenes/endCutScene"; +import StartCutScene from "./scenes/startCutScene"; const DEFAULT_WIDTH = 1280; const DEFAULT_HEIGHT = 720; export const CONFIG = { - title: "My Untitled Phaser 3 Game", + title: "StackPack Journey", version: "0.0.1", type: Phaser.AUTO, backgroundColor: "#ffffff", @@ -17,12 +29,27 @@ export const CONFIG = { width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT, }, - scene: [PreloadScene, MainScene], + scene: [ + PreloadScene, + MainScene, + TitleScreen, + GameMap, + LevelZero, + youDiedScene, + LevelOne, + LevelThree, + LevelTwo, + youDiedScene3, + youDiedScene1, + youDiedScene2, + EndCutScene, + StartCutScene, + ], physics: { default: "arcade", arcade: { debug: false, - gravity: { y: 300 }, + gravity: { y: 900 }, }, }, input: { diff --git a/src/scenes/endCutScene.ts b/src/scenes/endCutScene.ts new file mode 100644 index 00000000..3601dc85 --- /dev/null +++ b/src/scenes/endCutScene.ts @@ -0,0 +1,640 @@ +import Phaser from "phaser"; + +interface GameMapData { + level0State: number; + level1State: number; + level2State: number; + level3State: number; + level0Stars: number; + level1Stars: number; + level2Stars: number; + level3Stars: number; +} + +export default class EndCutScene extends Phaser.Scene { + private player?: Phaser.Physics.Arcade.Sprite; + private dude?: Phaser.Physics.Arcade.Sprite; + private cursors?: Phaser.Types.Input.Keyboard.CursorKeys; + private platforms?: Phaser.Physics.Arcade.StaticGroup; + private door?: Phaser.Physics.Arcade.Image; + private ground?: Phaser.Physics.Arcade.Image; + private stackpack?: Phaser.GameObjects.Image; + private heart?: Phaser.GameObjects.Image; + + private galMove: string = "right"; + private dudeMove: string = "left"; + private galLastDirection: string = "right"; + private dudeLastDirection: string = "right"; + + private delay: number; + + private level0State: number; + private level1State: number; + private level2State: number; + private level3State: number; + private level0Stars: number; + private level1Stars: number; + private level2Stars: number; + private level3Stars: number; + + constructor() { + super({ key: "EndCutScene" }); + } + + preload() { + this.load.image( + "end-cutscene-background", + "assets/end-cutscene/end-background1.jpeg" + ); + this.load.image( + "final-background", + "assets/end-cutscene/final-background.jpg" + ); + this.load.image("just-stackpack", "assets/backpack.png"); + + this.load.image("cutscene-heart", "assets/end-cutscene/heart.png"); + + this.load.image( + "play-again-button", + "assets/end-cutscene/play-again-button.png" + ); + this.load.image( + "world-map-button", + "assets/end-cutscene/world-map-button.png" + ); + + this.load.spritesheet( + "gal_idle_right", + "assets/Pink_Monster_Idle_4.png", + { frameWidth: 128, frameHeight: 128 } + ); + this.load.spritesheet( + "gal_idle_left", + "assets/Pink_Monster_Idle_Left4.png", + { frameWidth: 128, frameHeight: 128 } + ); + this.load.spritesheet( + "gal_walk_right", + "assets/Pink_Monster_Walk_6.png", + { + frameWidth: 128, + frameHeight: 128, + } + ); + this.load.spritesheet( + "gal_walk_left", + "assets/Pink_Monster_Walk_Left6.png", + { frameWidth: 128, frameHeight: 128 } + ); + + this.load.spritesheet( + "dude_idle_right", + "assets/Dude_Monster_Idle_4.png", + { frameWidth: 128, frameHeight: 128 } + ); + this.load.spritesheet( + "dude_idle_left", + "assets/Dude_Monster_Idle_Left4.png", + { frameWidth: 128, frameHeight: 128 } + ); + this.load.spritesheet( + "dude_walk_right", + "assets/Dude_Monster_Walk_6.png", + { + frameWidth: 128, + frameHeight: 128, + } + ); + this.load.spritesheet( + "dude_walk_left", + "assets/Dude_Monster_Walk_Left6.png", + { + frameWidth: 128, + frameHeight: 128, + } + ); + this.load.spritesheet( + "dude_run_right", + "assets/Dude_Monster_Run_6.png", + { + frameWidth: 128, + frameHeight: 128, + } + ); + this.load.spritesheet( + "dude_run_left", + "assets/Dude_Monster_Run_Left6.png", + { + frameWidth: 128, + frameHeight: 128, + } + ); + + this.load.image("level0-platform", "assets/level0/platform.png"); + + //this.load.image("red-opendoor", "assets/level3/red-door-open.png"); + this.load.image("star", "assets/star.png"); + } + + create(data: GameMapData) { + // Resume all animations and tweens + this.anims.resumeAll(); + this.tweens.resumeAll(); + // Make it so player can enter keyboard input + if (this.input.keyboard) { + this.input.keyboard.enabled = true; + } + + this.level0State = data.level0State; + this.level1State = data.level1State; + this.level2State = data.level2State; + this.level3State = data.level3State; + this.level0Stars = data.level0Stars; + this.level1Stars = data.level1Stars; + this.level2Stars = data.level2Stars; + this.level3Stars = data.level3Stars; + + this.galMove = ""; + this.dudeMove = ""; + this.galLastDirection = "right"; + this.dudeLastDirection = "left"; + + this.delay = 0; + + const backgroundImage = this.add + .image(0, 0, "end-cutscene-background") + .setOrigin(0, 0); + backgroundImage.setScale( + this.cameras.main.width / backgroundImage.width + 0.1, + this.cameras.main.height / backgroundImage.height + ); + + this.stackpack = this.add + .image(100, 150, "just-stackpack") + //.setPosition(100, 551) + .setOrigin(0.5, 1); + this.stackpack.setScale(0.1, 0.1); + + this.heart = this.add.image( + this.cameras.main.width / 2, + this.cameras.main.height / 2, + "cutscene-heart" + ); + this.heart.setDepth(20); + this.heart.setScale(0); + + // Creating Gal + this.player = this.physics.add + .sprite(120, 150, "gal_walk_right") + .setScale(1.5, 1.5) + .setOrigin(0.5, 0.5); + this.player.setCollideWorldBounds(true); + + this.anims.create({ + key: "gal_walk_right", + frames: this.anims.generateFrameNumbers("gal_walk_right", { + start: 0, + end: 5, + }), + repeat: -1, + }); + this.anims.create({ + key: "gal_walk_left", + frames: this.anims.generateFrameNumbers("gal_walk_left", { + start: 0, + end: 5, + }), + repeat: -1, + }); + this.anims.create({ + key: "gal_idle_right", + frames: this.anims.generateFrameNumbers("gal_idle_right", { + start: 0, + end: 3, + }), + frameRate: 10, + repeat: -1, + }); + this.anims.create({ + key: "gal_idle_left", + frames: this.anims.generateFrameNumbers("gal_idle_left", { + start: 0, + end: 3, + }), + frameRate: 10, + repeat: -1, + }); + + // Creating Guy + this.dude = this.physics.add + .sprite(this.cameras.main.width - 120, 430, "dude_run_right") + .setScale(1.5, 1.5) + .setOrigin(0.5, 0.5); + this.dude.setCollideWorldBounds(true); + + this.anims.create({ + key: "dude_idle_right", + frames: this.anims.generateFrameNumbers("dude_idle_right", { + start: 0, + end: 3, + }), + frameRate: 10, + repeat: -1, + }); + this.anims.create({ + key: "dude_idle_left", + frames: this.anims.generateFrameNumbers("dude_idle_left", { + start: 0, + end: 3, + }), + frameRate: 10, + repeat: -1, + }); + this.anims.create({ + key: "dude_walk_right", + frames: this.anims.generateFrameNumbers("dude_walk_right", { + start: 0, + end: 5, + }), + repeat: -1, + }); + this.anims.create({ + key: "dude_walk_left", + frames: this.anims.generateFrameNumbers("dude_walk_left", { + start: 0, + end: 5, + }), + repeat: -1, + }); + this.anims.create({ + key: "dude_run_right", + frames: this.anims.generateFrameNumbers("dude_run_right", { + start: 0, + end: 5, + }), + repeat: -1, + }); + this.anims.create({ + key: "dude_run_left", + frames: this.anims.generateFrameNumbers("dude_run_left", { + start: 0, + end: 5, + }), + repeat: -1, + }); + + this.cursors = this.input.keyboard?.createCursorKeys(); + + // Create platforms + this.platforms = this.physics.add.staticGroup(); + this.ground = this.platforms.create( + 650, + 633, + "level0-platform" + ) as Phaser.Physics.Arcade.Image; + + this.ground.setScale(5).refreshBody(); + this.ground.setAlpha(0); // Hide the ground platform + + this.physics.add.collider(this.player, this.platforms); + this.physics.add.collider(this.dude, this.platforms); + + // Resize collision boxes of player and everything else that can be collided with + this.player + .setSize(this.player.width - 64, this.player.height) + .setOffset(32, 0) + .setDepth(10); + + this.dude + .setSize(this.dude.width - 64, this.dude.height) + .setOffset(32, 0) + .setDepth(10); + + this.animateEnding(); + } + + private animateEnding() { + // Make stackpack drop to floor + this.tweens.add({ + targets: this.stackpack, + y: 551, + duration: 900, + }); + + this.delay = 1000; // Wait 1000 millisseconds before starting + + // Step 1: Both player and dude run to the middle of the screen + setTimeout(() => { + // Tween for the player + this.tweens.add({ + targets: this.player, + x: this.cameras.main.centerX - 60, + duration: 1200, + onStart: () => { + this.galMove = "right"; + }, + onComplete: () => { + this.galMove = ""; + }, + }); + + // Tween for the dude + this.tweens.add({ + targets: this.dude, + x: this.cameras.main.centerX + 60, + duration: 1500, + onStart: () => { + this.dudeMove = "left"; + }, + onComplete: () => { + this.dudeMove = ""; + }, + }); + }, this.delay); + + this.delay += 2700; + // Step 2: Player looks left + setTimeout(() => { + this.galLastDirection = "left"; + }, this.delay); + + this.delay += 1000; + // Step 3: Player looks back right to the dude + setTimeout(() => { + this.galLastDirection = "right"; + }, this.delay); + + this.delay += 500; + // Step 4: Player walks to the left and collects the stackpack + setTimeout(() => { + this.tweens.add({ + targets: this.player, + x: 220, + duration: 1200, + onStart: () => { + this.galMove = "left"; + }, + onComplete: () => { + this.galMove = ""; + // Collect stackpack + this.tweens.add({ + targets: this.stackpack, + x: this.player?.x, // Move towards the player's x position + y: this.player?.y, + scaleX: 0.03, // Shrink horizontally + scaleY: 0.03, // Shrink vertically + duration: 500, // Duration of animation + onComplete: () => { + this.stackpack?.setVisible(false); + }, + }); + }, + }); + }, this.delay); + + this.delay += 1700; + // Step 5: Player walks back to the dude + setTimeout(() => { + this.tweens.add({ + targets: this.player, + x: this.cameras.main.centerX - 45, + duration: 1500, + onStart: () => { + this.galMove = "right"; + }, + }); + }, this.delay); + + this.delay += 1500; + // Step 6: Player and dude walk to the right together + setTimeout(() => { + // Player + this.player?.setVelocityX(150); + this.galMove = "right"; + + // Dude + this.dude?.setVelocityX(150); + this.dudeMove = "rightWalk"; + }, this.delay); + + this.delay += 500; + // Step 7: A heart appears and fills the whole screen + setTimeout(() => { + this.tweens.add({ + targets: this.heart, + scaleX: 3, + scaleY: 3, + duration: 2800, + onComplete: () => { + // Step 8: Display last scene + this.displayLastScene(); + }, + }); + }, this.delay); + } + + private displayLastScene() { + // After the cutscene + const newBackgroundImage = this.add + .image(0, 0, "final-background") + .setOrigin(0, 0) + .setDepth(30); + newBackgroundImage.setScale( + this.cameras.main.width / newBackgroundImage.width, + this.cameras.main.height / newBackgroundImage.height + ); + + const thankYouText = this.add.text( + this.cameras.main.centerX, + this.cameras.main.centerY - 230, + "Thank you for Playing", + { + fontFamily: "Comic Sans MS", + fontSize: "90px", + color: "#253347", + align: "center", + fontStyle: "bold", + } + ); + thankYouText.setOrigin(0.5).setDepth(32); + + const authorText = this.add.text( + this.cameras.main.centerX + 55, + this.cameras.main.centerY + 265, + "A game by Aparna Roy, Sam Glover & Emilie Barniak", + { + fontFamily: "Comic Sans MS", + fontSize: "34px", + color: "#253347", + align: "center", + } + ); + authorText.setOrigin(0.5).setDepth(32); + + const totalStarsCollected = + this.level0Stars + + this.level1Stars + + this.level2Stars + + this.level3Stars; + const starsCollectedText = this.add.text( + 557, + 222, + `Earned: ${totalStarsCollected} / 12`, + { + fontFamily: "Comic Sans MS", + fontSize: "32px", + color: "#253347", + align: "center", + } + ); + starsCollectedText.setOrigin(0, 0.5).setDepth(32); + const star = this.add.image(517, 220, "star"); + star.setScale(0.13).setDepth(32); + + this.stackpack + ?.setVisible(true) + .setScale(0.06) + .setDepth(35) + .setPosition(230, 655); + + this.galMove = ""; + this.dudeMove = ""; + this.galLastDirection = "right"; + this.dudeLastDirection = "left"; + this.player?.setVelocityX(0); + this.dude?.setVelocityX(0); + this.player?.setDepth(31); + this.player?.setPosition(this.cameras.main.centerX - 80, 345); + this.dude?.setDepth(31); + this.dude?.setPosition(this.cameras.main.centerX + 80, 345); + this.ground?.setPosition(650, 530).refreshBody(); + + const playAgainButton = this.add + .image( + this.cameras.main.centerX - 180, + this.cameras.main.centerY + 165, + "play-again-button" + ) + .setDepth(30); + playAgainButton.setScale(0.4, 0.4); + playAgainButton.setInteractive(); + + const worldMapButton = this.add + .image( + this.cameras.main.centerX + 180, + this.cameras.main.centerY + 165, + "world-map-button" + ) + .setDepth(30); + worldMapButton.setScale(0.4, 0.4); + worldMapButton.setInteractive(); + + const originalScale = playAgainButton.scaleX; + const hoverScale = originalScale * 1.06; + + playAgainButton.on("pointerover", () => { + this.tweens.add({ + targets: playAgainButton, + scaleX: hoverScale, + scaleY: hoverScale, + duration: 115, + ease: "Linear", + }); + }); + + playAgainButton.on("pointerout", () => { + this.tweens.add({ + targets: playAgainButton, + scaleX: originalScale, + scaleY: originalScale, + duration: 115, + ease: "Linear", + }); + }); + + playAgainButton.on("pointerup", () => { + window.location.reload(); // Reload the page + }); + + worldMapButton.on("pointerover", () => { + this.tweens.add({ + targets: worldMapButton, + scaleX: hoverScale, + scaleY: hoverScale, + duration: 115, + ease: "Linear", + }); + }); + + worldMapButton.on("pointerout", () => { + this.tweens.add({ + targets: worldMapButton, + scaleX: originalScale, + scaleY: originalScale, + duration: 115, + ease: "Linear", + }); + }); + + worldMapButton.on("pointerup", () => { + this.scene.start("game-map", { + level0State: this.level0State, + level1State: this.level1State, + level2State: this.level2State, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + }); + } + + update() { + // Gal animations + if (this.player) { + if (this.galMove == "up") { + this.player.setVelocityY(-530); + } else if (this.galMove == "right") { + this.player.anims.play("gal_walk_right", true); + this.galLastDirection = "right"; // Update last direction + } else if (this.galMove == "left") { + this.player.anims.play("gal_walk_left", true); + this.galLastDirection = "left"; // Update last direction + } else { + this.player.setVelocityX(0); + // Check last direction and play corresponding idle animation + if (this.galLastDirection === "right") { + this.player.anims.play("gal_idle_right", true); + } else { + this.player.anims.play("gal_idle_left", true); + } + } + } + + // Guy animations + if (this.dude) { + if (this.dudeMove == "up") { + this.dude.anims.play("dude_idle_right", true); + this.dude.setVelocityY(-530); + } else if (this.dudeMove == "right") { + this.dude.anims.play("dude_run_right", true); + this.dudeLastDirection = "right"; // Update last direction + } else if (this.dudeMove == "left") { + this.dude.anims.play("dude_run_left", true); + this.dudeLastDirection = "left"; // Update last direction + } else if (this.dudeMove == "rightWalk") { + this.dude.anims.play("dude_walk_right", true); + this.dudeLastDirection = "right"; // Update last direction + } else { + this.dude.setVelocityX(0); + // Check last direction and play corresponding idle animation + if (this.dudeLastDirection === "right") { + this.dude.anims.play("dude_idle_right", true); + } else { + this.dude.anims.play("dude_idle_left", true); + } + } + } + } +} diff --git a/src/scenes/gameMap.ts b/src/scenes/gameMap.ts new file mode 100644 index 00000000..05dee581 --- /dev/null +++ b/src/scenes/gameMap.ts @@ -0,0 +1,544 @@ +import Phaser from "phaser"; + +// Level States: 0 = Locked, 1 = Just Unlocked, 2 = Current Level, 3 = Completed +interface GameMapData { + level0State: number; + level1State: number; + level2State: number; + level3State: number; + level0Stars: number; + level1Stars: number; + level2Stars: number; + level3Stars: number; +} + +export default class GameMap extends Phaser.Scene { + private level0State: number; + private level1State: number; + private level2State: number; + private level3State: number; + private level0Stars: number; + private level1Stars: number; + private level2Stars: number; + private level3Stars: number; + private backgroundMusic?: Phaser.Sound.BaseSound; + + constructor() { + super({ key: "game-map" }); + } + + preload() { + this.load.audio("map-music", "assets/Dream.mp3"); + this.load.image("game-map", "assets/gameMap/game-map.png"); + this.load.image( + "level0-unlocked", + "assets/gameMap/level0-button-unlocked.png" + ); + this.load.image( + "level0-completed", + "assets/gameMap/level0-button-completed.png" + ); + this.load.image( + "level1-locked", + "assets/gameMap/level1-button-locked.png" + ); + this.load.image( + "level1-unlocked", + "assets/gameMap/level1-button-unlocked.png" + ); + this.load.image( + "level1-completed", + "assets/gameMap/level1-button-completed.png" + ); + this.load.image( + "level2-locked", + "assets/gameMap/level2-button-locked.png" + ); + this.load.image( + "level2-unlocked", + "assets/gameMap/level2-button-unlocked.png" + ); + this.load.image( + "level2-completed", + "assets/gameMap/level2-button-completed.png" + ); + this.load.image( + "level3-locked", + "assets/gameMap/level3-button-locked.png" + ); + this.load.image( + "level3-unlocked", + "assets/gameMap/level3-button-unlocked.png" + ); + this.load.image( + "level3-completed", + "assets/gameMap/level3-button-completed.png" + ); + + // Stars + this.load.image("0starsDown", "assets/gameMap/stars/0starsDown.png"); + this.load.image("0starsUp", "assets/gameMap/stars/0starsUp.png"); + this.load.image("1starsDown", "assets/gameMap/stars/1starsDown.png"); + this.load.image("1starsUp", "assets/gameMap/stars/1starsUp.png"); + this.load.image("2starsDown", "assets/gameMap/stars/2starsDown.png"); + this.load.image("2starsUp", "assets/gameMap/stars/2starsUp.png"); + this.load.image("3starsDown", "assets/gameMap/stars/3starsDown.png"); + this.load.image("3starsUp", "assets/gameMap/stars/3starsUp.png"); + + this.load.image("star", "assets/star.png"); + } + + create(data: GameMapData) { + this.level0State = data.level0State | 2; + this.level1State = data.level1State | 0; + this.level2State = data.level2State | 0; + this.level3State = data.level3State | 0; + this.level0Stars = data.level0Stars | 0; + this.level1Stars = data.level1Stars | 0; + this.level2Stars = data.level2Stars | 0; + this.level3Stars = data.level3Stars | 0; + + console.log(this.level0Stars, data.level0Stars); + + const backgroundImage = this.add + .image(0, 0, "game-map") + .setOrigin(0, 0); + backgroundImage.setScale( + this.cameras.main.width / backgroundImage.width, + this.cameras.main.height / backgroundImage.height + ); + + this.backgroundMusic = this.sound.add("map-music"); + this.backgroundMusic.play({ + loop: true, + volume: 0.25, + }); + + const totalStarsCollected = + this.level0Stars + + this.level1Stars + + this.level2Stars + + this.level3Stars; + + const starsCollectedText = this.add.text( + 135, + 91, + `${totalStarsCollected} / 12`, + { + fontFamily: "Comic Sans MS", + fontSize: "32px", + color: "#ffffff", + align: "center", + } + ); + starsCollectedText.setOrigin(0, 0.5).setDepth(32); + + const star = this.add.image(85, 89, "star"); + star.setScale(0.14); + + const originalScale = 0.43; + const hoverScale = 0.46; + + // Creating Level Stars + const level0Stars = this.add + .image(180, 485, "0starsUp") + .setScale(0.21) + .setDepth(2); + + const level1Stars = this.add + .image(447, 446, "0starsDown") + .setScale(0.21) + .setDepth(2); + + const level2Stars = this.add + .image(768, 535, "0starsDown") + .setScale(0.21) + .setDepth(2); + + const level3Stars = this.add + .image(1058, 196, "0starsUp") + .setScale(0.21) + .setDepth(2); + + // If player hasn't played level yet, don't show stars, else, show the number of stars they earned + // Level 0 Stars + if (this.level0Stars == 0) { + level0Stars.setVisible(false); + } else if (this.level0Stars == 1) { + level0Stars.setTexture("1starsUp"); + } else if (this.level0Stars == 2) { + level0Stars.setTexture("2starsUp"); + } else if (this.level0Stars == 3) { + level0Stars.setTexture("3starsUp"); + } + + // Level 1 Stars + if (this.level1Stars == 0) { + level1Stars.setVisible(false); + } else if (this.level1Stars == 1) { + level1Stars.setTexture("1starsDown"); + } else if (this.level1Stars == 2) { + level1Stars.setTexture("2starsDown"); + } else if (this.level1Stars == 3) { + level1Stars.setTexture("3starsDown"); + } + + // Level 2 Stars + if (this.level2Stars == 0) { + level2Stars.setVisible(false); + } else if (this.level2Stars == 1) { + level2Stars.setTexture("1starsDown"); + } else if (this.level2Stars == 2) { + level2Stars.setTexture("2starsDown"); + } else if (this.level2Stars == 3) { + level2Stars.setTexture("3starsDown"); + } + + // Level 3 Stars + if (this.level3Stars == 0) { + level3Stars.setVisible(false); + } else if (this.level3Stars == 1) { + level3Stars.setTexture("1starsUp"); + } else if (this.level3Stars == 2) { + level3Stars.setTexture("2starsUp"); + } else if (this.level3Stars == 3) { + level3Stars.setTexture("3starsUp"); + } + + // Level 0 Button: + const level0Button = this.add.image(180, 552, "level0-unlocked"); + level0Button.setScale(originalScale); + + if (this.level0State == 3) { + level0Button.setTexture("level0-completed"); + } + + level0Button.setInteractive(); + + level0Button.on("pointerup", () => { + this.backgroundMusic?.stop() + this.scene.start("Level0", { + level0State: this.level0State, + level1State: this.level1State, + level2State: this.level2State, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + }); + + // Change scale on hover + level0Button.on("pointerover", () => { + this.tweens.add({ + targets: level0Button, + scaleX: hoverScale, + scaleY: hoverScale, + duration: 100, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + this.tweens.add({ + targets: level0Stars, + scaleX: 0.24, + scaleY: 0.24, + duration: 100, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + }); + + // Restore original scale when pointer leaves + level0Button.on("pointerout", () => { + this.tweens.add({ + targets: level0Button, + scaleX: originalScale, + scaleY: originalScale, + duration: 100, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + this.tweens.add({ + targets: level0Stars, + scaleX: 0.21, + scaleY: 0.21, + duration: 100, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + }); + + // Level 1 Button: + const level1Button = this.add.image(419, 355, "level1-locked"); + level1Button.setScale(originalScale); + + // If level 0 just completed, animate the unlocking of level 1 + if (this.level1State == 1) { + this.tweens.add({ + targets: level1Button, + alpha: 0.3, // Fade out the locked button + duration: 500, + onComplete: () => { + level1Button.setTexture("level1-unlocked"); + level1Button.setScale(originalScale); + level1Button.setAlpha(0); // Make the unlocked button invisible initially + + this.tweens.add({ + targets: level1Button, + alpha: 1, // Fade in the unlocked button + duration: 500, + ease: "Linear", + onComplete: () => { + this.level1State = 2; // Now level 1 is the current level + }, + }); + }, + }); + } + // If level 1 has already been unlocked and is current level + else if (this.level1State == 2) { + level1Button.setTexture("level1-unlocked"); + } + // If level 1 is completed + else if (this.level1State == 3) { + level1Button.setTexture("level1-completed"); + } + + // If level 1 is unlocked or completed, make it clickable + if (this.level1State >= 0) { + level1Button.setInteractive(); + + level1Button.on("pointerup", () => { + this.backgroundMusic?.stop(); + this.scene.start("Level1", { + level0State: this.level0State, + level1State: this.level1State, + level2State: this.level2State, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + }); + + // Change scale on hover + level1Button.on("pointerover", () => { + this.tweens.add({ + targets: level1Button, + scaleX: hoverScale, + scaleY: hoverScale, + duration: 100, + ease: "Linear", + }); + this.tweens.add({ + targets: level1Stars, + scaleX: 0.24, + scaleY: 0.24, + duration: 100, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + }); + + // Restore original scale when pointer leaves + level1Button.on("pointerout", () => { + this.tweens.add({ + targets: level1Button, + scaleX: originalScale, + scaleY: originalScale, + duration: 100, + ease: "Linear", + }); + this.tweens.add({ + targets: level1Stars, + scaleX: 0.21, + scaleY: 0.21, + duration: 100, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + }); + } + + // Level 2 Button: + const level2Button = this.add.image(780, 460, "level2-locked"); + level2Button.setScale(originalScale); + + // If level 1 just completed, animate the unlocking of level 2 + if (this.level2State == 1) { + this.tweens.add({ + targets: level2Button, + alpha: 0.3, // Fade out the locked button + duration: 500, + onComplete: () => { + level2Button.setTexture("level2-unlocked"); + level2Button.setScale(originalScale); + level2Button.setAlpha(0); // Make the unlocked button invisible initially + + this.tweens.add({ + targets: level2Button, + alpha: 1, // Fade in the unlocked button + duration: 500, + ease: "Linear", + onComplete: () => { + this.level2State = 2; // Now level 2 is the current level + }, + }); + }, + }); + } + // If level 2 has already been unlocked and is current level + else if (this.level2State == 2) { + level2Button.setTexture("level2-unlocked"); + } + // If level 2 is completed + else if (this.level2State == 3) { + level2Button.setTexture("level2-completed"); + } + + // If level 2 is unlocked or completed, make it clickable + if (this.level2State >= 0) { + level2Button.setInteractive(); + + level2Button.on("pointerup", () => { + this.backgroundMusic?.stop(); + this.scene.start("Level2", { + level0State: this.level0State, + level1State: this.level1State, + level2State: this.level2State, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + }); + + // Change scale on hover + level2Button.on("pointerover", () => { + this.tweens.add({ + targets: level2Button, + scaleX: hoverScale, + scaleY: hoverScale, + duration: 100, + ease: "Linear", + }); + this.tweens.add({ + targets: level2Stars, + scaleX: 0.24, + scaleY: 0.24, + duration: 100, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + }); + + // Restore original scale when pointer leaves + level2Button.on("pointerout", () => { + this.tweens.add({ + targets: level2Button, + scaleX: originalScale, + scaleY: originalScale, + duration: 100, + ease: "Linear", + }); + this.tweens.add({ + targets: level2Stars, + scaleX: 0.21, + scaleY: 0.21, + duration: 100, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + }); + } + + // Level 3 Button: + const level3Button = this.add.image(1091, 270, "level3-locked"); + level3Button.setScale(originalScale); + + // If level 2 just completed, animate the unlocking of level 3 + if (this.level3State == 1) { + this.tweens.add({ + targets: level3Button, + alpha: 0.3, // Fade out the locked button + duration: 500, + onComplete: () => { + level3Button.setTexture("level3-unlocked"); + level3Button.setScale(originalScale); + level3Button.setAlpha(0); // Make the unlocked button invisible initially + + this.tweens.add({ + targets: level3Button, + alpha: 1, // Fade in the unlocked button + duration: 500, + ease: "Linear", + onComplete: () => { + this.level3State = 2; // Now level 3 is the current level + }, + }); + }, + }); + } + // If level 3 has already been unlocked and is current level + else if (this.level3State == 2) { + level3Button.setTexture("level3-unlocked"); + } + // If level 3 is completed + else if (this.level3State == 3) { + level3Button.setTexture("level3-completed"); + } + + // If level 3 is unlocked or completed, make it clickable + // this.level3State > 0 + if (this.level3State >= 0) { + level3Button.setInteractive(); + + level3Button.on("pointerup", () => { + this.backgroundMusic?.stop(); + this.scene.start("Level3", { + level0State: this.level0State, + level1State: this.level1State, + level2State: this.level2State, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + }); + + // Change scale on hover + level3Button.on("pointerover", () => { + this.tweens.add({ + targets: level3Button, + scaleX: hoverScale, + scaleY: hoverScale, + duration: 100, + ease: "Linear", + }); + this.tweens.add({ + targets: level3Stars, + scaleX: 0.24, + scaleY: 0.24, + duration: 100, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + }); + + // Restore original scale when pointer leaves + level3Button.on("pointerout", () => { + this.tweens.add({ + targets: level3Button, + scaleX: originalScale, + scaleY: originalScale, + duration: 100, + ease: "Linear", + }); + this.tweens.add({ + targets: level3Stars, + scaleX: 0.21, + scaleY: 0.21, + duration: 100, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + }); + } + } + + update() {} +} diff --git a/src/scenes/levelOne.ts b/src/scenes/levelOne.ts new file mode 100644 index 00000000..d6c6c370 --- /dev/null +++ b/src/scenes/levelOne.ts @@ -0,0 +1,2030 @@ +import Phaser from "phaser"; + +interface GameMapData { + level0State: number; + level1State: number; + level2State: number; + level3State: number; + level0Stars: number; + level1Stars: number; + level2Stars: number; + level3Stars: number; +} +export default class LevelOne extends Phaser.Scene { + // General Assets + private player?: Phaser.Physics.Arcade.Sprite; + private cursors?: Phaser.Types.Input.Keyboard.CursorKeys; + private key?: Phaser.GameObjects.Sprite; + private platforms?: Phaser.Physics.Arcade.StaticGroup; + private banana?: Phaser.Physics.Arcade.Sprite; + private bananaBubble?: Phaser.GameObjects.Image; + private mushroom?: Phaser.Physics.Arcade.Sprite; + private vineItem?: Phaser.Physics.Arcade.Sprite; + private stone?: Phaser.Physics.Arcade.Sprite; + private ground?: Phaser.Physics.Arcade.Image; + private monkey?: Phaser.Physics.Arcade.Sprite; + private river?: Phaser.Physics.Arcade.Sprite; + private groundRectangle?: Phaser.GameObjects.Rectangle; + private vineSwing?: Phaser.GameObjects.Sprite; + private door?: Phaser.GameObjects.Image; + private mushroomSign?: Phaser.Physics.Arcade.Sprite; + + // Highlight/Detection Boxes + private stoneDetectionBox?: Phaser.GameObjects.Rectangle; + private stoneHighlightBox?: Phaser.GameObjects.Rectangle; + + private mushroomDetectionBox?: Phaser.GameObjects.Rectangle; + private mushroomHighlightBox?: Phaser.GameObjects.Rectangle; + private mushroomPopped?: boolean = false; + + private bananaDetectionBox?: Phaser.GameObjects.Rectangle; + private bananaHighlightBox?: Phaser.GameObjects.Rectangle; + + private vineDetectionBox?: Phaser.GameObjects.Rectangle; + private vineHighlightBox?: Phaser.GameObjects.Rectangle; + + private keyDetectionArea?: Phaser.GameObjects.Rectangle; + private keyHighlightBox: Phaser.GameObjects.Rectangle; + + private riverDetectionArea?: Phaser.GameObjects.Rectangle; + + private leftRiverBoundary?: Phaser.Physics.Arcade.Sprite; + private rightRiverBoundary?: Phaser.Physics.Arcade.Sprite; + + // Functionality + private stack: Phaser.GameObjects.Sprite[] = []; + private collectedItems: Phaser.GameObjects.Sprite[] = []; // To track all collected items (even after they're popped from stack) + private usedItems: Phaser.GameObjects.Sprite[] = []; + private keyE?: Phaser.Input.Keyboard.Key; + private keyF?: Phaser.Input.Keyboard.Key; + private keyEPressed: boolean = false; // Flag to check if 'E' was pressed to prevent picking up multiple items from one long key press + private keyFPressed: boolean = false; + private isPushingMap: { [key: string]: boolean } = {}; + private hearts?: Phaser.GameObjects.Sprite[] = []; + private lives: number = 3; + private stackY: number = 300; + private stoneStackImg: Phaser.GameObjects.Image; + private mushroomStackImg: Phaser.GameObjects.Image; + private bananaStackImg: Phaser.GameObjects.Image; + private vineStackImg: Phaser.GameObjects.Image; + private keyStackImg: Phaser.GameObjects.Image; + private levelCompleteText?: Phaser.GameObjects.Text; + + private popButton?: Phaser.GameObjects.Image; + private freePopsLeft: number = 2; + private freePopsLeftText: Phaser.GameObjects.Text; + + // For Player animations + private lastDirection: string = "right"; + private isColliding: boolean = false; + private collidingWithWater: boolean = false; + private flashingRed: boolean = false; + private poppingWrongItem: boolean = false; + + // Level States and Stars + private level0State: number; + private level1State: number; + private level2State: number; + private level3State: number; + private level0Stars: number; + private level1Stars: number; + private level2Stars: number; + private level3Stars: number; + + // Timer + private timerText: Phaser.GameObjects.Text; + private startTime: number; + private pausedTime = 0; + private elapsedTime: number; + private isPaused: boolean = false; + + private threeStarsPopup: Phaser.GameObjects.Group; + private twoStarsPopup: Phaser.GameObjects.Group; + private oneStarPopup: Phaser.GameObjects.Group; + private starsPopup: Phaser.GameObjects.Group; + + // Music and sounds + private backgroundMusic: Phaser.Sound.BaseSound; + private musicMuted: boolean = false; + private injureSound: Phaser.Sound.BaseSound; + + constructor() { + super({ key: "Level1" }); + } + + preload() { + this.load.audio("jungle-music", "assets/level1/Jungle.wav"); + this.load.audio("collect-sound", "assets/sounds/collectsound.mp3"); + this.load.audio("dooropen-sound", "assets/sounds/dooropensound.mp3"); + this.load.audio("injure-sound", "assets/sounds/injuresound.mp3"); + this.load.audio("wrong-sound", "assets/sounds/wrongsound.mp3"); + this.load.audio("pop-sound", "assets/sounds/popsound.mp3"); + this.load.audio("death-sound", "assets/sounds/playerdiesound.mp3"); + this.load.audio("menu-sound", "assets/sounds/menusound.mp3"); + this.load.audio("bounce-sound", "assets/level1/boing.mp3"); + this.load.audio("splash-sound", "assets/level1/watersplash.mp3"); + this.load.audio("win-sound", "assets/sounds/winsound.mp3"); + this.load.audio("monkey-sound", "assets/level1/monkeysound.wav"); + this.load.audio("swing-sound", "assets/level1/swingsound.mp3"); + + this.load.image( + "level1Background", + "assets/level1/Level1Background.jpg" + ); + this.load.image("stackpack", "assets/stackpack.png"); + this.load.spritesheet("key", "assets/key.png", { + frameWidth: 768 / 24, + frameHeight: 32, + }); + + this.load.spritesheet("gal_right", "assets/Pink_Monster_Walk_6.png", { + frameWidth: 128, + frameHeight: 128, + }); + this.load.spritesheet( + "gal_left", + "assets/Pink_Monster_Walk_Left6.png", + { + frameWidth: 128, + frameHeight: 128, + } + ); + this.load.spritesheet( + "gal_idle_right", + "assets/Pink_Monster_Idle_4.png", + { + frameWidth: 128, + frameHeight: 128, + } + ); + this.load.spritesheet( + "gal_idle_left", + "assets/Pink_Monster_Idle_Left4.png", + { + frameWidth: 128, + frameHeight: 128, + } + ); + this.load.spritesheet( + "gal_jump_right", + "assets/Pink_Monster_Jump_8.png", + { + frameWidth: 128, + frameHeight: 128, + } + ); + this.load.spritesheet("gal_climb", "assets/Pink_Monster_Climb_4.png", { + frameWidth: 128, + frameHeight: 128, + }); + this.load.spritesheet( + "gal_hurt_right", + "assets/Pink_Monster_Hurt_4.png", + { + frameWidth: 128, + frameHeight: 128, + } + ); + this.load.spritesheet( + "gal_hurt_left", + "assets/Pink_Monster_Hurt_Left4.png", + { + frameWidth: 128, + frameHeight: 128, + } + ); + this.load.image("banana", "assets/level1/banana.png"); + this.load.image("bananaBubble", "assets/level1/bananaBubble.png"); + this.load.image("LgPlatform", "assets/level1/LgPlatform.png"); + this.load.image("MdPlatform", "assets/level1/MdPlatform.png"); + this.load.image("SmPlatform", "assets/level1/SmPlatform.png"); + this.load.image("monkey", "assets/level1/monkey.png"); + this.load.image("mushroom", "assets/level1/mushroom.png"); + this.load.image("vineItem", "assets/level1/vineItem.png"); + this.load.image("stone", "assets/level1/stone.png"); + this.load.image("river", "assets/level1/river.png"); + this.load.image("heart", "assets/heart_16.png"); + this.load.image("vineHook", "assets/level1/vineHook.png"); + this.load.image("vine", "assets/level1/vine.png"); + this.load.image("stackKey", "assets/level1/stackKey.png"); + this.load.image("brown-door", "assets/level1/brown-door.png"); + this.load.image("brown-openDoor", "assets/level1/brown-door-open.png"); + this.load.image("mushroomSign", "assets/level1/mushroomSign.png"); + + this.load.image( + "FreePopInstructions", + "assets/FreePop-Instructions.png" + ); + this.load.image("EF-keys-black", "assets/EF-keys-black.png"); + + this.load.image("pop-button", "assets/freePop2.png"); + + this.load.image("pause-button", "assets/pause2.png"); + this.load.image("pause-popup", "assets/paused-popup.png"); + + this.load.image("3stars", "assets/FullStars.png"); + this.load.image("2stars", "assets/2Stars.png"); + this.load.image("1star", "assets/1Star.png"); + } + + create(data: GameMapData) { + this.resetScene(); + // Resume all animations and tweens + this.anims.resumeAll(); + this.tweens.resumeAll(); + // Make it so player can enter keyboard input + if (this.input.keyboard) { + this.input.keyboard.enabled = true; + } + + // Temporary fix for time not fully resetting bug + setTimeout(() => (this.startTime = this.time.now)); + + this.level0State = data.level0State; + this.level1State = data.level1State; + this.level2State = data.level2State; + this.level3State = data.level3State; + this.level0Stars = data.level0Stars; + this.level1Stars = data.level1Stars; + this.level2Stars = data.level2Stars; + this.level3Stars = data.level3Stars; + + const backgroundImage = this.add + .image(0, 0, "level1Background") + .setOrigin(0, 0); + backgroundImage.setScale( + this.cameras.main.width / backgroundImage.width, + this.cameras.main.height / backgroundImage.height + ); + + this.backgroundMusic = this.sound.add("jungle-music"); + this.backgroundMusic.play({ + loop: true, + volume: 0.25, + }); + this.injureSound = this.sound.add("injure-sound"); + + const EFkeys = this.add.image(390, 60, "EF-keys-black"); + EFkeys.setScale(0.35); + + const stackpack = this.add + .image(0, 0, "stackpack") + .setPosition(1170, 165); + stackpack.setScale(0.26, 0.26); + + this.add.image(640, 70, "vineHook").setScale(0.6, 0.6); + + this.freePopsLeftText = this.add + .text(18, 174, `Pops Left: ${this.freePopsLeft}`, { + fontFamily: "Arial", + fontSize: 18, + color: "#FFFFFF", + }) + .setDepth(4); + + this.player = this.physics.add + .sprite(100, 600, "gal_right") + .setScale(0.77, 0.77) + .setOrigin(0.5, 0.5) + .setDepth(4); + this.player.setCollideWorldBounds(true); + + // KEY ANIM + this.anims.create({ + key: "turn", + frames: this.anims.generateFrameNumbers("key", { + start: 0, + end: 25, + }), + frameRate: 8, + repeat: -1, + }); + + // PLAYER ANIMS + this.anims.create({ + key: "right", + frames: this.anims.generateFrameNumbers("gal_right", { + start: 0, + end: 5, + }), + repeat: -1, + }); + this.anims.create({ + key: "turn", + frames: [{ key: "gal_right", frame: 1 }], + }); + this.anims.create({ + key: "left", + frames: this.anims.generateFrameNumbers("gal_left", { + start: 0, + end: 5, + }), + repeat: -1, + }); + this.anims.create({ + key: "idle_right", + frames: this.anims.generateFrameNumbers("gal_idle_right", { + start: 0, + end: 3, + }), + frameRate: 10, + repeat: -1, + }); + this.anims.create({ + key: "idle_left", + frames: this.anims.generateFrameNumbers("gal_idle_left", { + start: 0, + end: 3, + }), + frameRate: 10, + repeat: -1, + }); + this.anims.create({ + key: "jump_right", + frames: this.anims.generateFrameNumbers("gal_jump_right", { + start: 0, + end: 7, + }), + }); + this.anims.create({ + key: "climb", + frames: this.anims.generateFrameNumbers("gal_climb", { + start: 0, + end: 3, + }), + frameRate: 15, + }); + this.anims.create({ + key: "hurt_right", + frames: this.anims.generateFrameNumbers("gal_hurt_right", { + start: 0, + end: 1, + }), + frameRate: 10, + repeat: 0, + }); + this.anims.create({ + key: "hurt_left", + frames: this.anims.generateFrameNumbers("gal_hurt_left", { + start: 4, + end: 2, + }), + frameRate: 10, + repeat: 0, + }); + + this.cursors = this.input.keyboard?.createCursorKeys(); + + // PLATFORMS + this.platforms = this.physics.add.staticGroup(); + this.ground = this.platforms.create( + 600, + 770, + "LgPlatform" + ) as Phaser.Physics.Arcade.Image; + this.ground.setSize(this.ground.width + 1100, this.ground.height - 370); + + this.groundRectangle = this.add.rectangle( + 600, + 770, + 1400, + 200, + 0x172808 + ); + + this.groundRectangle.depth = 99; + + const platform1 = this.platforms + .create(290, 585, "SmPlatform") + .setScale(0.7, 0.7); + const platform2 = this.platforms + .create(350, 340, "MdPlatform") + .setScale(0.7, 0.7); + const platform3 = this.platforms + .create(810, 450, "LgPlatform") + .setScale(1.3, 0.75); + const platform4 = this.platforms + .create(900, 250, "SmPlatform") + .setScale(0.7, 0.7); + platform1 + .setSize(platform1.width - 210, platform1.height - 550) + .setOffset(110, 260); + platform2 + .setSize(platform2.width - 150, platform2.height - 550) + .setOffset(75, 260); + platform3 + .setSize(platform2.width + 60, platform2.height - 550) + .setOffset(-30, 260); + platform4 + .setSize(platform2.width - 210, platform2.height - 550) + .setOffset(105, 260); + this.physics.add.collider(this.player, this.platforms); + + // KEY ITEM + this.key = this.add.sprite(245, 270, "key").setScale(2.5, 2.5); + this.physics.add.collider(this.key, this.platforms); + this.key.setSize(this.key.width - 100, this.key.height - 100); + this.key.setName("key"); + + this.player + .setSize(this.player.width - 64, this.player.height - 12) + .setOffset(32, 10); + + // ALL ITEMS SPAWNING + this.banana = this.physics.add + .sprite(900, 380, "banana") + .setScale(0.5, 0.5); + this.physics.add.collider(this.banana, this.platforms); + this.banana.setSize(this.banana.width - 100, this.banana.height - 400); + this.banana.setName("banana"); + + this.stone = this.physics.add + .sprite(300, 620, "stone") + .setScale(0.3, 0.3); + this.physics.add.collider(this.stone, this.platforms); + this.stone.setSize(this.stone.width + 200, this.stone.height + 30); + this.stone.setName("stone"); + + this.mushroom = this.physics.add + .sprite(300, 500, "mushroom") + .setScale(0.5, 0.5); + this.physics.add.collider(this.mushroom, this.platforms); + this.mushroom.setSize( + this.mushroom.width - 100, + this.mushroom.height - 400 + ); + this.mushroom.setName("mushroom"); + this.mushroom.setPushable(false); + + this.monkey = this.physics.add + .sprite(300, 200, "monkey") + .setScale(0.5, 0.5); + this.physics.add.collider(this.monkey, this.platforms); + this.monkey.setSize(this.monkey.width - 300, this.monkey.height - 200); + this.monkey.setName("monkey"); + this.monkey.setPushable(false); + this.physics.add.collider(this.monkey, this.player); + + this.vineItem = this.physics.add + .sprite(700, 350, "vineItem") + .setScale(0.5, 0.5); + this.physics.add.collider(this.vineItem, this.platforms); + this.vineItem.setSize( + this.vineItem.width - 100, + this.vineItem.height - 350 + ); + this.vineItem.setName("vineItem"); + + this.river = this.physics.add + .sprite(700, 630, "river") + .setScale(0.8, 0.8); + this.physics.add.collider(this.river, this.platforms); + this.river + .setSize(this.river.width - 20, this.river.height - 440) + .setOffset(10, 200); + this.river.setPushable(false); + this.river.setName("river"); + + this.mushroomSign = this.physics.add.sprite(1100, 550, "mushroomSign"); + this.physics.add.collider(this.mushroomSign, this.platforms); + this.physics.add.collider(this.mushroomSign, this.player); + this.mushroomSign.setPushable(false); + this.mushroomSign + .setSize( + this.mushroomSign.width - 250, + this.mushroomSign.height - 355 + ) + .setScale(0.5, 0.5); + + this.leftRiverBoundary = this.physics.add.sprite(580, 550, "mushroom"); + this.physics.add.collider(this.leftRiverBoundary, this.platforms); + this.physics.add.collider(this.leftRiverBoundary, this.player); + this.leftRiverBoundary.setPushable(false); + this.leftRiverBoundary.setVisible(false); + this.leftRiverBoundary.setSize( + this.leftRiverBoundary.width - 420, + this.leftRiverBoundary.height - 470 + ); + this.rightRiverBoundary = this.physics.add.sprite(820, 550, "mushroom"); + this.physics.add.collider(this.rightRiverBoundary, this.platforms); + this.physics.add.collider(this.rightRiverBoundary, this.player); + this.rightRiverBoundary.setPushable(false); + this.rightRiverBoundary.setVisible(false); + this.rightRiverBoundary.setSize( + this.rightRiverBoundary.width - 410, + this.rightRiverBoundary.height - 500 + ); + + this.bananaBubble = this.add + .image(200, 140, "bananaBubble") + .setScale(0.66, 0.66); + + this.vineSwing = this.add.sprite(655, 140, "vine").setScale(0.35, 0.35); + this.vineSwing.setOrigin(0.5, 0); + this.vineSwing.setAngle(this.vineSwing.angle + 60); + this.vineSwing.setVisible(false); + + this.door = this.add + .image(910, 140, "brown-door") + .setScale(0.35, 0.35) + .setDepth(2); + + // Handling Pushing.Popping + this.keyE = this.input.keyboard?.addKey( + Phaser.Input.Keyboard.KeyCodes.E + ); + this.keyF = this.input.keyboard?.addKey( + Phaser.Input.Keyboard.KeyCodes.F + ); + + // PULSATING + this.createPulsateEffect( + this, + this.mushroom, + 1.1, // Scale factor for pulsating effect + 1000 // Duration of each tween cycle in milliseconds + ); + this.createPulsateEffect( + this, + this.banana, + 1.1, // Scale factor for pulsating effect + 1000 // Duration of each tween cycle in milliseconds + ); + this.createPulsateEffect( + this, + this.vineItem, + 1.1, // Scale factor for pulsating effect + 1000 // Duration of each tween cycle in milliseconds + ); + this.createPulsateEffect( + this, + this.stone, + 1.1, // Scale factor for pulsating effect + 1000 // Duration of each tween cycle in milliseconds + ); + + // Create Lives + this.createHearts(); + + // Invisible check boxes + + this.stoneDetectionBox = this.add.rectangle(540, 600, 100, 150); + this.physics.world.enable(this.stoneDetectionBox); + this.physics.add.collider(this.stoneDetectionBox, this.ground); + this.physics.add.collider(this.stoneDetectionBox, this.river); + + this.stoneHighlightBox = this.add.rectangle( + 700, + 600, + 100, + 80, + 0xffff00 + ); + this.stoneHighlightBox.setAlpha(0.3); + this.stoneHighlightBox.setVisible(false); + + this.mushroomDetectionBox = this.add.rectangle(1070, 600, 100, 150); + this.physics.world.enable(this.mushroomDetectionBox); + this.physics.add.collider(this.mushroomDetectionBox, this.ground); + + this.mushroomHighlightBox = this.add.rectangle( + 1160, + 640, + 100, + 150, + 0xffff00 + ); + this.mushroomHighlightBox.setAlpha(0.3); + this.mushroomHighlightBox.setVisible(false); + + this.bananaDetectionBox = this.add.rectangle(350, 200, 30, 150); + this.physics.world.enable(this.bananaDetectionBox); + this.physics.add.collider(this.bananaDetectionBox, this.platforms); + + this.bananaHighlightBox = this.add.rectangle( + 200, + 125, + 80, + 80, + 0xffff00 + ); + this.bananaHighlightBox.setAlpha(0.3); + this.bananaHighlightBox.setVisible(false); + + this.vineDetectionBox = this.add.rectangle(490, 200, 30, 150); + this.physics.world.enable(this.vineDetectionBox); + this.physics.add.collider(this.vineDetectionBox, this.platforms); + + this.vineHighlightBox = this.add.rectangle(647, 130, 50, 50, 0xffff00); + this.vineHighlightBox.setAlpha(0.5); + this.vineHighlightBox.setVisible(false); + + this.keyDetectionArea = this.add.rectangle(890, 100, 200, 150); + this.physics.world.enable(this.keyDetectionArea); + this.physics.add.collider(this.keyDetectionArea, this.platforms); + + this.keyHighlightBox = this.add.rectangle(900, 140, 170, 190, 0xffff00); + this.keyHighlightBox.setAlpha(0.25); + this.keyHighlightBox.setVisible(false); + + this.riverDetectionArea = this.add.rectangle(700, 600, 200, 20); + this.physics.world.enable(this.riverDetectionArea); + this.physics.add.collider(this.riverDetectionArea, this.platforms); + + // setting depths + this.stone.depth = 1; + this.river.depth = 0; + + // Level complete stuff + this.levelCompleteText = this.add.text( + this.cameras.main.width / 2, + this.cameras.main.height / 2, + "Level Complete!", + { fontSize: "96px", color: "#03572a", fontFamily: "Verdana" } + ); + this.levelCompleteText.setOrigin(0.5); + this.levelCompleteText.setVisible(false); + + // Set initial properties for animation + this.levelCompleteText.setScale(0); + this.levelCompleteText.setAlpha(0); + + // Free Pop stuff + const popButton = this.add.image(65, 140, "pop-button").setScale(0.31); + popButton.setInteractive(); + + const originalScale = popButton.scaleX; + const hoverScale = originalScale * 1.05; + popButton.on("pointerover", () => { + this.tweens.add({ + targets: popButton, + scaleX: hoverScale, + scaleY: hoverScale, + duration: 115, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + }); + + popButton.on("pointerout", () => { + this.tweens.add({ + targets: popButton, + scaleX: originalScale, + scaleY: originalScale, + duration: 115, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + }); + + popButton.on("pointerup", () => { + this.sound.play("pop-sound"); + this.freePop(); + this.freePopsLeft -= 1; + this.freePopsLeftText.setText(`Pops Left: ${this.freePopsLeft}`); + if (this.freePopsLeft <= 0) { + popButton.setScale(originalScale); + popButton.disableInteractive(); + popButton.setTint(0x696969); + } + }); + + // Pause Buttons + const pauseGroup = this.add.group(); + + // Creating Pause Popup + const pausePopup = this.add.image(650, 350, "pause-popup"); + pausePopup.setOrigin(0.5); + pausePopup.setDepth(9); + pauseGroup.add(pausePopup); + + // Exit button for Pause popup + const exitButton = this.add.rectangle(640, 530, 200, 75).setDepth(10); + exitButton.setOrigin(0.5); + exitButton.setInteractive(); + pauseGroup.add(exitButton); + + exitButton.on("pointerover", () => { + exitButton.setFillStyle(0xffff00).setAlpha(0.5); + }); + + exitButton.on("pointerout", () => { + exitButton.setFillStyle(); + }); + + exitButton.on("pointerup", () => { + this.sound.play("menu-sound"); + this.backgroundMusic.stop(); + this.isPaused = false; + this.resetScene(); + this.scene.start("game-map", { + level0State: this.level0State, + level1State: this.level1State, + level2State: this.level2State, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + }); + + // Return button for Pause popup + const restartButton = this.add + .rectangle(640, 425, 200, 75) + .setDepth(10); + restartButton.setOrigin(0.5); + restartButton.setInteractive(); + pauseGroup.add(restartButton); + + restartButton.on("pointerover", () => { + restartButton.setFillStyle(0xffff00).setAlpha(0.5); + }); + + restartButton.on("pointerout", () => { + restartButton.setFillStyle(); + }); + + restartButton.on("pointerup", () => { + this.sound.play("menu-sound"); + this.backgroundMusic.stop(); + this.backgroundMusic.destroy(); + this.resetScene(); + this.scene.start("Level1", { + level0State: this.level0State, + level1State: this.level1State, + level2State: this.level2State, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + }); + + // Resume button for Pause popup + const resumeButton = this.add.rectangle(640, 320, 200, 75).setDepth(10); + resumeButton.setOrigin(0.5); + resumeButton.setInteractive(); + pauseGroup.add(resumeButton); + + resumeButton.on("pointerover", () => { + resumeButton.setFillStyle(0xffff00).setAlpha(0.5); + }); + + resumeButton.on("pointerout", () => { + resumeButton.setFillStyle(); + }); + + resumeButton.on("pointerup", () => { + this.sound.play("menu-sound"); + pauseGroup.setVisible(false); + this.pauseTime(); + // Resume all animations and tweens + this.anims.resumeAll(); + this.tweens.resumeAll(); + // Make it so player can enter keyboard input + if (this.input.keyboard) { + this.input.keyboard.enabled = true; + } + // Make it so player can click Free Pop button + if (this.freePopsLeft > 0) { + popButton.setInteractive(); + } + }); + + // No music button for Pause popup + const muteMusic = this.add.rectangle(585, 217, 90, 90).setDepth(10); + muteMusic.setOrigin(0.5); + muteMusic.setInteractive(); + pauseGroup.add(muteMusic); + + muteMusic.on("pointerover", () => { + muteMusic.setFillStyle(0xffff00).setAlpha(0.5); + }); + + muteMusic.on("pointerout", () => { + muteMusic.setFillStyle(); + }); + + muteMusic.on("pointerup", () => { + this.sound.play("menu-sound"); + this.musicMuted = !this.musicMuted; + if (this.musicMuted) { + this.backgroundMusic.pause(); + } else { + this.backgroundMusic.resume(); + } + }); + + // No sound button for Pause popup + const muteSound = this.add.rectangle(700, 217, 90, 90).setDepth(10); + muteSound.setOrigin(0.5); + muteSound.setInteractive(); + pauseGroup.add(muteSound); + + muteSound.on("pointerover", () => { + muteSound.setFillStyle(0xffff00).setAlpha(0.5); + }); + + muteSound.on("pointerout", () => { + muteSound.setFillStyle(); + }); + + // Has to get fixed once we have sound + muteSound.on("pointerup", () => { + this.sound.play("menu-sound"); + pauseGroup.setVisible(false); + }); + + pauseGroup.setVisible(false); + + // Creating Pause Button + const pauseButton = this.add + .image(30, 30, "pause-button") + .setScale(0.25); + pauseButton.setInteractive(); + + const pauseOriginalScale = pauseButton.scaleX; + const pauseHoverScale = pauseOriginalScale * 1.05; + + // Change scale on hover + pauseButton.on("pointerover", () => { + this.tweens.add({ + targets: pauseButton, + scaleX: pauseHoverScale, + scaleY: pauseHoverScale, + duration: 115, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + }); + + // Restore original scale when pointer leaves + pauseButton.on("pointerout", () => { + this.tweens.add({ + targets: pauseButton, + scaleX: pauseOriginalScale, + scaleY: pauseOriginalScale, + duration: 115, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + }); + + pauseButton.on("pointerup", () => { + this.sound.play("menu-sound"); + if (!this.isPaused) { + this.pauseTime(); + pauseGroup.setVisible(true); + // Pause all animations and tweens + this.anims.pauseAll(); + this.tweens.pauseAll(); + // Make it so player can't enter keyboard input + if (this.input.keyboard) { + this.input.keyboard.enabled = false; + } + // Make it so player can't click Free Pop button + popButton.disableInteractive(); + } + }); + + // Creating timer + this.timerText = this.add.text(60, 15, "Time: 00:00", { + fontSize: "32px", + color: "#000000", + }); + this.startTime = this.time.now; + this.pausedTime = 0; + + // Level complete popup - still working + const completeExitButton = this.add.circle(790, 185, 35).setDepth(10); + completeExitButton.setInteractive(); + completeExitButton.on("pointerover", () => { + completeExitButton.setFillStyle(0xffff00).setAlpha(0.5); + }); + completeExitButton.on("pointerout", () => { + completeExitButton.setFillStyle(); + }); + + const completeReplayButton = this.add.circle(510, 505, 55).setDepth(10); + completeReplayButton.setInteractive(); + completeReplayButton.on("pointerover", () => { + completeReplayButton.setFillStyle(0xffff00).setAlpha(0.5); + }); + completeReplayButton.on("pointerout", () => { + completeReplayButton.setFillStyle(); + }); + + const completeMenuButton = this.add.circle(655, 530, 55).setDepth(10); + completeMenuButton.setInteractive(); + completeMenuButton.on("pointerover", () => { + completeMenuButton.setFillStyle(0xffff00).setAlpha(0.5); + }); + completeMenuButton.on("pointerout", () => { + completeMenuButton.setFillStyle(); + }); + + const completeNextButton = this.add.circle(800, 505, 55).setDepth(10); + completeNextButton.setInteractive(); + completeNextButton.on("pointerover", () => { + completeNextButton.setFillStyle(0xffff00).setAlpha(0.5); + }); + completeNextButton.on("pointerout", () => { + completeNextButton.setFillStyle(); + }); + + this.threeStarsPopup = this.add.group(); + const threeStars = this.add.image(650, 350, "3stars"); + this.threeStarsPopup.add(threeStars); + this.threeStarsPopup.add(completeExitButton); + this.threeStarsPopup.add(completeReplayButton); + this.threeStarsPopup.add(completeMenuButton); + this.threeStarsPopup.add(completeNextButton); + + this.twoStarsPopup = this.add.group(); + const twoStars = this.add.image(650, 350, "2stars"); + this.twoStarsPopup.add(twoStars); + this.twoStarsPopup.add(completeExitButton); + this.twoStarsPopup.add(completeReplayButton); + this.twoStarsPopup.add(completeMenuButton); + this.twoStarsPopup.add(completeNextButton); + + this.oneStarPopup = this.add.group(); + const oneStar = this.add.image(650, 350, "1star"); + this.oneStarPopup.add(oneStar); + this.oneStarPopup.add(completeExitButton); + this.oneStarPopup.add(completeReplayButton); + this.oneStarPopup.add(completeMenuButton); + this.oneStarPopup.add(completeNextButton); + + completeExitButton.on("pointerup", () => { + this.sound.play("menu-sound"); + this.backgroundMusic.stop(); + this.isPaused = false; + if (threeStars.visible) { + this.threeStarsPopup.setVisible(false); + } + if (twoStars.visible) { + this.twoStarsPopup.setVisible(false); + } + if (oneStar.visible) { + this.oneStarPopup.setVisible(false); + } + }); + + completeReplayButton.on("pointerup", () => { + this.sound.play("menu-sound"); + this.backgroundMusic.stop(); + this.backgroundMusic.destroy(); + this.resetScene(); + this.scene.start("Level1", { + level0State: this.level0State, + level1State: this.level1State, + level2State: this.level2State, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + }); + + completeMenuButton.on("pointerup", () => { + this.sound.play("menu-sound"); + this.backgroundMusic.stop(); + if (data.level2State == 0) { + setTimeout(() => { + this.scene.start("game-map", { + level0State: this.level0State, + level1State: 3, + level2State: 1, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + }, 500); + } else { + setTimeout(() => { + this.scene.start("game-map", { + level0State: this.level0State, + level1State: 3, + level2State: this.level2State, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + }, 1000); + } + }); + + completeNextButton.on("pointerup", () => { + this.sound.play("menu-sound"); + this.backgroundMusic.stop(); + this.backgroundMusic.destroy(); + if (data.level2State == 0) { + // If level 2 was locked before, set it to current level status + this.scene.start("Level2", { + level0State: this.level0State, + level1State: 3, + level2State: 2, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + } else { + this.scene.start("Level2", { + level0State: this.level0State, + level1State: 3, + level2State: this.level2State, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + } + }); + + this.threeStarsPopup.setVisible(false); + this.twoStarsPopup.setVisible(false); + this.oneStarPopup.setVisible(false); + } + + // HELPER FUNCTIONS + + private formatTime(milliseconds: number) { + var mins = Math.floor(milliseconds / 60000); + var secs = Math.floor((milliseconds % 60000) / 1000); + return ( + mins.toString().padStart(2, "0") + + ":" + + secs.toString().padStart(2, "0") + ); + } + + private pauseTime() { + this.isPaused = !this.isPaused; + if (this.isPaused) { + this.pausedTime = this.time.now - this.startTime; + } else { + this.startTime = this.time.now - this.pausedTime; + } + } + + private vineSwingStart() { + if (this.vineSwing && this.player) { + this.vineSwing.setOrigin(0.5, 0); + this.tweens.add({ + targets: this.vineSwing, + angle: this.vineSwing.angle - 120, + }); + this.tweens.add({ + targets: this.player, + x: 800, + y: 180, + onComplete: () => { + this.tweens.add({ + targets: this.player, + x: 890, + y: 100, + onComplete: () => { + this.vineSwing?.setVisible(false); + }, + }); + }, + }); + } + } + + private imageViewInStack(item: Phaser.GameObjects.Sprite) { + const buffer = 60; + if (item.name === "stone") { + this.stoneStackImg = this.add + .image(1170, this.stackY - buffer, item.name) + .setScale(0.15, 0.15) + .setSize(80, 80); + } else if (item.name === "mushroom") { + this.mushroomStackImg = this.add + .image(1170, this.stackY - buffer, item.name) + .setScale(0.25, 0.25) + .setSize(80, 80); + } else if (item.name === "banana") { + this.bananaStackImg = this.add + .image(1170, this.stackY - buffer, item.name) + .setScale(0.25, 0.25) + .setSize(80, 80); + } else if (item.name === "vineItem") { + this.vineStackImg = this.add + .image(1170, this.stackY - buffer, item.name) + .setScale(0.25, 0.25) + .setSize(80, 80); + } else if (item.name === "key") { + this.keyStackImg = this.add + .image(1170, this.stackY - buffer, "stackKey") + .setScale(1.4, 1.4) + .setSize(80, 80); + } + this.stackY -= buffer; + this.isPushingMap[item.name] = false; + } + + private imageViewOutStack(item: Phaser.GameObjects.Sprite) { + if (item.name === "stone") { + this.stoneStackImg.setVisible(false); + this.stackY = this.stackY + 60; + } else if (item.name === "mushroom") { + this.mushroomStackImg.setVisible(false); + this.stackY = this.stackY + 60; + } else if (item.name === "banana") { + this.bananaStackImg.setVisible(false); + this.stackY = this.stackY + 60; + } else if (item.name === "vineItem") { + this.vineStackImg.setVisible(false); + this.stackY = this.stackY + 60; + } else if (item.name === "key") { + this.keyStackImg.setVisible(false); + this.stackY = this.stackY + 60; + } + } + + private updateStackView() { + const offsetX = 1170; // starting X position for stack items + const offsetY = 270; // starting Y position for stack items + const padding = 20; + + let currTotalHeight = 0; + + this.stack.forEach((item) => { + // Calculate and set (x, y) position of stack items in stackpack view + item.setOrigin(0.5, 0); + const stackItemX = offsetX; + const stackItemY = + offsetY - item.displayHeight - currTotalHeight - padding; + currTotalHeight += item.displayHeight + padding; + + // Animation to drop the item into its position in the stackpack + this.tweens.add({ + targets: item, + x: stackItemX, + y: stackItemY, + duration: 800, + ease: "Cubic.InOut", + onComplete: () => { + this.isPushingMap[item.name] = false; + }, + }); + }); + } + + private collectItem(item: Phaser.GameObjects.Sprite) { + this.sound.play("collect-sound"); + if (this.collectedItems.includes(item)) { + return; + } + + this.isPushingMap[item.name] = true; + + // Save the x and y scales of the collected item + const currScaleX = item.scaleX; + const currScaleY = item.scaleY; + + // Animation to make item bigger, then smaller, and then fly up to stackpack + this.tweens.add({ + targets: item, + scaleX: currScaleX * 1.5, // Scale up item size for a bit + scaleY: currScaleY * 1.5, + duration: 180, + ease: "Exponential.InOut", + onComplete: () => { + this.tweens.add({ + targets: item, + scaleX: currScaleX, // Scale down item back to normal + scaleY: currScaleY, + duration: 150, + ease: "Exponential.InOut", + onComplete: () => { + // Move item to the stackpack view + this.tweens.add({ + targets: item, + x: 1170, + y: -10, // Y position of item before it is dropped into its actual position in stackpack + scaleX: currScaleX * 0.5, // Scale down the item for stackpack view + scaleY: currScaleY * 0.5, + rotation: Math.PI * 2, // Rotate the item while moving it to stackpack + duration: 940, + ease: "Cubic.In", + onComplete: () => { + // Add the item to the stack + this.stack.push(item); + //this.updateStackView(); + item.setVisible(false); + this.imageViewInStack(item); + // put them back in original position for free pop + if (item.name === "stone") { + item.setPosition(300, 620); + } else if (item.name === "mushroom") { + item.setPosition(300, 500); + } else if (item.name === "banana") { + item.setPosition(900, 380); + } else if (item.name === "vineItem") { + item.setPosition(700, 350); + } else if (item.name === "key") { + item.setPosition(245, 270); + } + }, + }); + }, + }); + }, + }); + + // Add the item to the grand list of collected items + this.collectedItems.push(item); + this.stopPulsateEffect(); + + //this.updateStackView(); + } + + private useItem() { + if (this.isPushingMap[this.stack[this.stack.length - 1].name]) { + return; // Prevent popping if a push is in progress + } + + // Remove the top item from the stackpack + const poppedItem = this.stack.pop(); + + if (poppedItem) { + this.usedItems.push(poppedItem); + // Animation to fade item out from stackpack and then fade in in its new location + this.tweens.add({ + targets: poppedItem, + alpha: 0, // Fade out + duration: 200, + onComplete: () => { + // Set item origin back to default (center) + poppedItem.setOrigin(0.5, 0.5); + + // Move popped item to location it will be used + if ( + poppedItem.name === "stone" && + this.player && + this.stone + ) { + this.sound.play("splash-sound"); + poppedItem.setPosition(700, 560).setScale(0.22, 0.22); + this.stoneHighlightBox?.setVisible(false); + this.stone.setVisible(true); + this.physics.add.collider(this.player, this.stone); + this.stone.setPushable(false); + } + if (poppedItem.name === "mushroom") { + this.sound.play("bounce-sound"); + poppedItem.setPosition(1160, 500); + //poppedItem.setPosition(1160, 560); + poppedItem.setSize( + poppedItem.width - 200, + poppedItem.height - 400 + ); + this.mushroomHighlightBox?.setVisible(false); + this.mushroomPopped = true; + if (this.player) { + this.physics.add.collider(this.player, poppedItem); + } + this.mushroom?.setVisible(true); + this.mushroomSign?.setVisible(false); + this.mushroomSign?.disableBody(); + } + if (poppedItem.name === "banana") { + this.sound.play("monkey-sound"); + if (this.banana) { + this.banana.setVelocity(0, 0); + } + poppedItem.setVisible(false); + this.bananaHighlightBox?.setVisible(false); + this.bananaBubble?.setVisible(false); + this.monkey?.disableBody(true, true); + } + if (poppedItem.name === "vineItem") { + this.sound.play("swing-sound"); + poppedItem.setVisible(false); + this.vineHighlightBox?.setVisible(false); + this.vineSwing?.setVisible(true); + this.vineSwingStart(); + } + if (poppedItem.name === "key") { + this.sound.play("dooropen-sound"); + this.keyHighlightBox.setVisible(false); + poppedItem.setVisible(false); + this.door?.setTexture("brown-openDoor"); + this.pauseTime(); + if (this.player && this.door) { + this.tweens.add({ + targets: this.player, + scaleX: 0.27, + scaleY: 0.27, + rotation: Math.PI * 3, + x: this.door.x - 10, + y: this.door.y + 15, + duration: 800, + onComplete: () => { + this.sound.play("win-sound"); + if (this.input.keyboard) { + this.input.keyboard.enabled = false; + } + this.player?.setVisible(false); + var completedTime = this.add + .text( + 640, + 345, + this.formatTime(this.elapsedTime), + { + fontSize: "40px", + color: "#000000", + } + ) + .setDepth(11) + .setVisible(false); + // Level popup depends on time it takes to complete + if (this.elapsedTime <= 30000) { + this.starsPopup = this.threeStarsPopup; + this.threeStarsPopup.add(completedTime); + this.threeStarsPopup + .setVisible(true) + .setDepth(10); + this.level1Stars = 3; + } + if ( + this.elapsedTime > 30000 && + this.elapsedTime <= 120000 + ) { + this.starsPopup = this.twoStarsPopup; + this.twoStarsPopup.add(completedTime); + this.twoStarsPopup + .setVisible(true) + .setDepth(10); + // Update stars if its better than previous time + if (this.level1Stars < 2) { + this.level1Stars = 2; + } + } + if (this.elapsedTime > 120000) { + this.starsPopup = this.oneStarPopup; + this.oneStarPopup.add(completedTime); + this.oneStarPopup + .setVisible(true) + .setDepth(10); + // Update stars if its better than previous time + if (this.level1Stars < 1) { + this.level1Stars = 1; + } + } + this.tweens.add({ + targets: this.starsPopup, + alpha: 1, + duration: 5000, + ease: "Linear", + delay: 1000, // Delay the animation slightly + }); + + if (this.level2State == 0) { + this.level1State = 3; + this.level2State = 1; + } else { + this.level1State = 3; + } + }, + }); + } + } + this.imageViewOutStack(poppedItem); + + this.tweens.add({ + targets: poppedItem, + scaleX: poppedItem.scaleX * 2, + scaleY: poppedItem.scaleY * 2, + alpha: 1, // Fade in + duration: 300, + onComplete: () => { + //this.imageViewOutStack(poppedItem); + //this.updateStackView(); + }, + }); + }, + }); + } + } + + private popWrongItem(usageArea: Phaser.GameObjects.Rectangle) { + if (this.isPushingMap[this.stack[this.stack.length - 1].name]) { + return; // Prevent popping if a push is in progress + } + + this.poppingWrongItem = true; + this.loseLife(); + this.poppingWrongItem = false; + + // Remove the top item from the stackpack + const poppedItem = this.stack.pop(); + + if (poppedItem && this.lives !== 0) { + // Remove popped item from grand list of collected items + const index = this.collectedItems.indexOf(poppedItem); + if (index !== -1) { + this.collectedItems.splice(index, 1); + } + + // Animation to flash red in location player tried to use item + this.tweens.add({ + targets: usageArea, + alpha: 0, + duration: 300, + yoyo: true, + repeat: 1, + onStart: () => { + usageArea.alpha = 0.55; + usageArea.fillColor = 0xff0000; // Make area red + this.flashingRed = true; + }, + onComplete: () => { + usageArea.alpha = 0.25; // Reset area color and alpha + usageArea.fillColor = 0xffff00; + this.flashingRed = false; + }, + }); + + // Animation to fade item out from stackpack and then fade in in its new location + this.tweens.add({ + targets: poppedItem, + alpha: 0, // Fade out + duration: 200, + onComplete: () => { + // Set item origin back to default (center) + poppedItem.setOrigin(0.5, 0.5); + + let originalScaleX = 0; + let originalScaleY = 0; + // Move popped item to its original location + if (poppedItem.name === "stone") { + poppedItem.setPosition(300, 590); + originalScaleX = 0.3; + originalScaleY = 0.3; + } + if (poppedItem.name === "mushroom") { + poppedItem.setPosition(300, 470); + originalScaleX = 0.5; + originalScaleY = 0.5; + } + if (poppedItem.name === "banana") { + poppedItem.setPosition(900, 310); + originalScaleX = 0.5; + originalScaleY = 0.5; + } + if (poppedItem.name === "vineItem") { + poppedItem.setPosition(700, 310); + originalScaleX = 0.5; + originalScaleY = 0.5; + } + if (poppedItem.name === "key") { + poppedItem.setPosition(245, 270); + originalScaleX = 2.5; + originalScaleY = 2.5; + } + poppedItem.setVisible(true); + this.tweens.add({ + targets: poppedItem, + scaleX: originalScaleX, + scaleY: originalScaleY, + alpha: 1, // Fade in + duration: 300, + onComplete: () => { + this.imageViewOutStack(poppedItem); + this.createPulsateEffect( + this, + poppedItem, + 1.1, + 1000 + ); + }, + }); + }, + }); + } + } + + // Animation for using free pop + private freePop() { + if (this.isPushingMap[this.stack[this.stack.length - 1].name]) { + return; // Prevent popping if a push is in progress + } + + // Remove the top item from the stackpack and from grand list of collected items + const poppedItem = this.stack.pop(); + this.collectedItems.pop(); + + if (poppedItem && this.lives !== 0) { + const index = this.collectedItems.indexOf(poppedItem); + if (index !== -1) { + this.collectedItems.splice(index, 1); + } + // Animation to fade item out from stackpack and then fade in in its new location + this.tweens.add({ + targets: poppedItem, + alpha: 0, // Fade out + duration: 200, + onComplete: () => { + // Set item origin back to default (center) + //poppedItem.setOrigin(0.5, 0.5); + + let originalScaleX = 0; + let originalScaleY = 0; + // Move popped item to its original location + if (poppedItem.name === "stone") { + poppedItem.setPosition(300, 590); + originalScaleX = 0.3; + originalScaleY = 0.3; + } + if (poppedItem.name === "mushroom") { + poppedItem.setPosition(300, 470); + originalScaleX = 0.5; + originalScaleY = 0.5; + } + if (poppedItem.name === "banana") { + poppedItem.setPosition(900, 310); + originalScaleX = 0.5; + originalScaleY = 0.5; + } + if (poppedItem.name === "vineItem") { + poppedItem.setPosition(700, 310); + originalScaleX = 0.5; + originalScaleY = 0.5; + } + if (poppedItem.name === "key") { + poppedItem.setPosition(245, 270); + originalScaleX = 2.5; + originalScaleY = 2.5; + } + poppedItem.setOrigin(0.5, 0.5); + + poppedItem.setVisible(true); + this.tweens.add({ + targets: poppedItem, + scaleX: originalScaleX, + scaleY: originalScaleY, + alpha: 1, // Fade in + duration: 300, + onComplete: () => { + //this.updateStackView(); + this.imageViewOutStack(poppedItem); + this.createPulsateEffect( + this, + poppedItem, + 1.1, + 1000 + ); + }, + }); + }, + }); + } + } + + private createHearts() { + this.lives = 3; + this.hearts = []; + + for (let i = 0; i < 3; i++) { + this.hearts.push( + this.add.sprite(32 + i * 50, 80, "heart").setScale(0.5) + ); + } + } + + private loseLife() { + if (!this.isColliding && this.player) { + this.isColliding = true; + if (this.poppingWrongItem) { + this.sound.play("wrong-sound"); + } else { + this.injureSound.play(); + } + + this.player.setVelocity(0, 0); + if (this.lastDirection === "right") { + this.player.anims.play("hurt_right"); + } else { + this.player.anims.play("hurt_left"); + } + this.lives--; + + // Removing hearts from free pop + const heartToRemove = this.hearts?.pop(); + if (heartToRemove) { + //heartToRemove.destroy(); + this.tweens.add({ + targets: heartToRemove, + scaleX: 0.8, + scaleY: 0.8, + duration: 200, + yoyo: true, + onComplete: () => { + heartToRemove.setTint(0x000000); // Make heart black + heartToRemove.setScale(0.5); // Reset the heart's scale + }, + }); + } + + if (this.lives === 0) { + this.playerDie(); + } + + // Reset isColliding flag + this.time.delayedCall( + 500, + () => { + this.isColliding = false; + if (this.collidingWithWater) { + this.player?.setPosition(100, 450); // Reset player's position + this.collidingWithWater = false; + } + }, + [], + this + ); + } + } + + private playerDie() { + this.sound.play("death-sound"); + this.player?.setTint(0xff0000); + + this.time.delayedCall(300, () => { + this.scene.launch("YouDiedScene1", { + currentLevelKey: this.scene.key, + level0State: this.level0State, + level1State: this.level1State, + level2State: this.level2State, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + this.player?.clearTint(); + + // Reset the stack and collected items + this.stack = []; + this.collectedItems = []; + this.usedItems = []; + this.lives = 3; + this.createHearts(); + this.freePopsLeft = 2; + this.backgroundMusic.stop(); + this.backgroundMusic.destroy(); + this.poppingWrongItem = false; + }); + } + + private resetScene() { + // Reset the stack and collected items + this.stack = []; + this.updateStackView(); + this.collectedItems = []; + this.usedItems = []; + this.lives = 3; + this.createHearts(); + this.freePopsLeft = 2; + this.startTime = this.time.now; + this.pausedTime = 0; + this.isPaused = false; + this.stackY = 300; + this.mushroomPopped = false; + this.isColliding = false; + this.collidingWithWater = false; + this.flashingRed = false; + this.poppingWrongItem = false; + } + + private createPulsateEffect( + scene: Phaser.Scene, + target: Phaser.GameObjects.GameObject, + scaleFactor: number, + duration: number + ): Phaser.Tweens.Tween | null { + // Check if the item has been collected + if (this.collectedItems.includes(target as Phaser.GameObjects.Sprite)) { + return null; // Don't create the tween if the item has been collected + } + return scene.tweens.add({ + targets: target, + scaleX: `*=${scaleFactor}`, + scaleY: `*=${scaleFactor}`, + duration: duration, + yoyo: true, // Reverse back to original scale + repeat: -1, // Repeat indefinitely + }); + } + + private stopPulsateEffect() { + // Stop pulsating collected items + this.collectedItems.forEach((item) => { + const tween = this.tweens.getTweensOf(item); + if (tween.length > 1) { + tween[0].stop(); + } + }); + } + + update() { + // KEY TURN + if (this.key) { + this.key.anims.play("turn", true); + } + + // PLAYER ANIMS + if (this.player && this.cursors) { + if (!this.isColliding) { + if (this.cursors.up.isDown && this.player.body?.touching.down) { + this.player.anims.play("jump_right", true); + this.player.setVelocityY(-470); + } else if (this.cursors.right.isDown) { + this.player.setVelocityX(290); + this.player.anims.play("right", true); + this.lastDirection = "right"; // Update last direction + } else if (this.cursors.left.isDown) { + this.player.setVelocityX(-290); + this.player.anims.play("left", true); + this.lastDirection = "left"; // Update last direction + } else { + this.player.setVelocityX(0); + // Check last direction and play corresponding idle animation + if (this.lastDirection === "right") { + this.player.anims.play("idle_right", true); + } else { + this.player.anims.play("idle_left", true); + } + } + } + } + + // ITEM COLLECTION + if (this.player && this.keyE?.isDown && !this.keyEPressed) { + this.keyEPressed = true; // Set the flag for the E key being pressed to true + + // Check if the player is close enough to the key, ladder, or plank, and if so, collect it + if ( + this.key && + Phaser.Math.Distance.Between( + this.player.x, + this.player.y, + this.key.x, + this.key.y + ) < 100 + ) { + this.collectItem(this.key); + } + if ( + this.stone && + Phaser.Math.Distance.Between( + this.player.x, + this.player.y, + this.stone.x, + this.stone.y + ) < 100 + ) { + this.collectItem(this.stone); + } + if ( + this.mushroom && + Phaser.Math.Distance.Between( + this.player.x, + this.player.y, + this.mushroom.x, + this.mushroom.y + ) < 100 + ) { + this.collectItem(this.mushroom); + } + if ( + this.banana && + Phaser.Math.Distance.Between( + this.player.x, + this.player.y, + this.banana.x, + this.banana.y + ) < 100 + ) { + this.collectItem(this.banana); + } + if ( + this.vineItem && + Phaser.Math.Distance.Between( + this.player.x, + this.player.y, + this.vineItem.x, + this.vineItem.y + ) < 100 + ) { + this.collectItem(this.vineItem); + } + } + // Check if 'E' key is released + if (this.keyE?.isUp) { + this.keyEPressed = false; // Reset the keyEPressed flag when the E key is released + } + + // Check if 'F' key is released + if (this.keyF?.isUp) { + this.keyFPressed = false; // Reset the keyFPressed flag when the F key is released + } + + // Detection Box Checks for popping + if ( + this.player && + this.stoneDetectionBox && + this.mushroomDetectionBox && + this.bananaDetectionBox && + this.keyDetectionArea && + this.stoneHighlightBox && + this.mushroomHighlightBox && + this.bananaHighlightBox && + this.stone && + this.mushroom && + this.banana && + this.vineItem && + this.key + ) { + if ( + Phaser.Geom.Intersects.RectangleToRectangle( + this.player.getBounds(), + this.stoneDetectionBox.getBounds() + ) && + !this.usedItems.includes(this.stone) + ) { + this.stoneHighlightBox.setVisible(true); + if ( + this.keyF?.isDown && + !this.keyFPressed && + this.stack.length > 0 + ) { + if (this.stack[this.stack.length - 1].name === "stone") { + this.stoneDetectionBox.setVisible(false); + this.keyFPressed = true; + this.useItem(); + } else { + this.keyFPressed = true; + this.popWrongItem(this.stoneHighlightBox); + } + } + } else if ( + Phaser.Geom.Intersects.RectangleToRectangle( + this.player.getBounds(), + this.mushroomDetectionBox.getBounds() + ) && + !this.usedItems.includes(this.mushroom) + ) { + this.mushroomHighlightBox.setVisible(true); + if ( + this.keyF?.isDown && + !this.keyFPressed && + this.stack.length > 0 + ) { + if (this.stack[this.stack.length - 1].name === "mushroom") { + this.keyFPressed = true; + this.useItem(); + } else { + this.keyFPressed = true; + this.popWrongItem(this.mushroomHighlightBox); + } + } + } else if ( + Phaser.Geom.Intersects.RectangleToRectangle( + this.player.getBounds(), + this.bananaDetectionBox.getBounds() + ) && + !this.usedItems.includes(this.banana) + ) { + this.bananaHighlightBox.setVisible(true); + if ( + this.keyF?.isDown && + !this.keyFPressed && + this.stack.length > 0 + ) { + if (this.stack[this.stack.length - 1].name === "banana") { + this.keyFPressed = true; + this.useItem(); + } else { + this.keyFPressed = true; + this.popWrongItem(this.bananaHighlightBox); + } + } + } else if ( + Phaser.Geom.Intersects.RectangleToRectangle( + this.player.getBounds(), + this.keyDetectionArea.getBounds() + ) && + !this.usedItems.includes(this.key) + ) { + this.keyHighlightBox.setVisible(true); + if ( + this.keyF?.isDown && + !this.keyFPressed && + this.stack.length > 0 + ) { + if (this.stack[this.stack.length - 1].name === "key") { + this.keyFPressed = true; + this.useItem(); + this.tweens.add({ + targets: this.levelCompleteText, + scale: 1, + alpha: 1, + duration: 1000, + ease: "Bounce", + delay: 500, // Delay the animation slightly + }); + } else { + this.keyFPressed = true; + this.popWrongItem(this.keyHighlightBox); + } + } + } else { + this.stoneHighlightBox.setVisible(false); + this.mushroomHighlightBox.setVisible(false); + this.bananaHighlightBox.setVisible(false); + this.keyHighlightBox.setVisible(false); + } + } + + if ( + this.player && + this.vineDetectionBox && + this.vineItem && + this.vineHighlightBox + ) { + if ( + Phaser.Geom.Intersects.RectangleToRectangle( + this.player.getBounds(), + this.vineDetectionBox.getBounds() + ) && + !this.usedItems.includes(this.vineItem) + ) { + this.vineHighlightBox.setVisible(true); // replace with vine highlight box + if ( + this.keyF?.isDown && + !this.keyFPressed && + this.stack.length > 0 + ) { + if (this.stack[this.stack.length - 1].name === "vineItem") { + this.keyFPressed = true; + this.useItem(); + } else { + this.keyFPressed = true; + this.popWrongItem(this.vineHighlightBox); + } + } + } else { + this.vineHighlightBox.setVisible(false); + } + } + + // On top of Mushroom Check + if (this.player && this.mushroom) { + let playerBounds = this.player.getBounds(); + let mushroomBounds = this.mushroom.getBounds(); + if ( + playerBounds.bottom > mushroomBounds.top + 100 && + playerBounds.left > mushroomBounds.left && + playerBounds.right < mushroomBounds.right && + this.mushroomPopped + ) { + this.sound.play("bounce-sound"); + this.player.setVelocityY(-640); + } + } + + // Checking for collision with water (drown) + if (this.player && this.riverDetectionArea) { + this.physics.add.collider( + this.player, + this.riverDetectionArea, + () => { + this.collidingWithWater = true; + this.loseLife(); + }, + undefined, + this + ); + } + + // Timer + if (!this.isPaused) { + var currentTime = this.time.now; + this.elapsedTime = currentTime - this.startTime; + this.timerText.setText( + "Time: " + this.formatTime(this.elapsedTime) + ); + } + } +} diff --git a/src/scenes/levelThree.ts b/src/scenes/levelThree.ts new file mode 100644 index 00000000..fc76535d --- /dev/null +++ b/src/scenes/levelThree.ts @@ -0,0 +1,2664 @@ +import Phaser from "phaser"; + +interface GameMapData { + level0State: number; + level1State: number; + level2State: number; + level3State: number; + level0Stars: number; + level1Stars: number; + level2Stars: number; + level3Stars: number; +} + +export default class LevelThree extends Phaser.Scene { + private player?: Phaser.Physics.Arcade.Sprite; + private cursors?: Phaser.Types.Input.Keyboard.CursorKeys; + private key?: Phaser.GameObjects.Sprite; + private platforms?: Phaser.Physics.Arcade.StaticGroup; + private ground?: Phaser.Physics.Arcade.Image; + private lava?: Phaser.Physics.Arcade.StaticGroup; + private stones!: Phaser.Physics.Arcade.Group; + private liftPlatforms!: Phaser.Physics.Arcade.Group; + + private fireball1?: Phaser.Physics.Arcade.Image; + private fireball2?: Phaser.Physics.Arcade.Image; + + private stone0?: Phaser.Physics.Arcade.Image; + private stone1?: Phaser.Physics.Arcade.Image; + private stone2?: Phaser.Physics.Arcade.Image; + private stone3?: Phaser.Physics.Arcade.Image; + private stone4?: Phaser.Physics.Arcade.Image; + + private water?: Phaser.GameObjects.Sprite; + private gasMask?: Phaser.GameObjects.Sprite; + private sword?: Phaser.GameObjects.Sprite; + private toolbox?: Phaser.GameObjects.Sprite; + private chainsaw?: Phaser.GameObjects.Sprite; + + private fire?: Phaser.GameObjects.Sprite; + private toxicGas?: Phaser.GameObjects.Sprite; + private gasBarrel?: Phaser.GameObjects.Sprite; + private skeleton?: Phaser.GameObjects.Sprite; + private dangerSign?: Phaser.GameObjects.Sprite; + private liftFloor?: Phaser.Physics.Arcade.Image; + private liftWall1?: Phaser.Physics.Arcade.Image; + private liftWall2?: Phaser.Physics.Arcade.Image; + private tree?: Phaser.GameObjects.Sprite; + private door?: Phaser.Physics.Arcade.Image; + + private skeletonDirection: number = 1; + + private waterDetectionArea: Phaser.GameObjects.Rectangle; + private waterHighlightBox: Phaser.GameObjects.Rectangle; + private gasMaskDetectionArea: Phaser.GameObjects.Rectangle; + private gasMaskHighlightBox: Phaser.GameObjects.Rectangle; + private swordDetectionArea: Phaser.GameObjects.Rectangle; + private swordHighlightBox: Phaser.GameObjects.Rectangle; + private toolboxDetectionArea: Phaser.GameObjects.Rectangle; + private toolboxHighlightBox: Phaser.GameObjects.Rectangle; + private chainsawDetectionArea: Phaser.GameObjects.Rectangle; + private chainsawHighlightBox: Phaser.GameObjects.Rectangle; + private keyDetectionArea: Phaser.GameObjects.Rectangle; + private keyHighlightBox: Phaser.GameObjects.Rectangle; + + private lavaArea: Phaser.GameObjects.Rectangle; + private toxicGasArea: Phaser.GameObjects.Rectangle; + private fireArea: Phaser.GameObjects.Rectangle; + private liftArea: Phaser.GameObjects.Rectangle; + private treeArea: Phaser.GameObjects.Rectangle; + private skeletonArea: Phaser.GameObjects.Rectangle; + + private stack: Phaser.GameObjects.Sprite[] = []; + private collectedItems: Phaser.GameObjects.Sprite[] = []; // To track all collected items (even after they're popped from stack) + private usedItems: Phaser.GameObjects.Sprite[] = []; + private keyE?: Phaser.Input.Keyboard.Key; + private keyF?: Phaser.Input.Keyboard.Key; + private keyEPressed: boolean = false; // Flag to check if 'E' was pressed to prevent picking up multiple items from one long key press + private keyFPressed: boolean = false; // Flag to check if 'E' was pressed to prevent using multiple items from one long key press + private lastDirection: string = "right"; + private isPushingMap: { [key: string]: boolean } = {}; // Flags for each item to make sure you can't pop it while it is being pushed + private flashingRed: boolean = false; + private freePopsLeft: number = 4; + private freePopsLeftText: Phaser.GameObjects.Text; + + private hearts?: Phaser.GameObjects.Sprite[] = []; + private lives: number = 3; + private isColliding: boolean = false; + private collidingWithDeath: boolean = false; + private usedSword: boolean = false; + private skeletonDead: boolean = false; + private playerLostLife: boolean = false; + private poppingWrongItem: boolean = false; + + private timerText: Phaser.GameObjects.Text; + private startTime: number; + private pausedTime = 0; + private elapsedTime: number; + private isPaused: boolean = false; + + private threeStarsPopup: Phaser.GameObjects.Group; + private twoStarsPopup: Phaser.GameObjects.Group; + private oneStarPopup: Phaser.GameObjects.Group; + private starsPopup: Phaser.GameObjects.Group; + + private level0State: number; + private level1State: number; + private level2State: number; + private level3State: number; + private level0Stars: number; + private level1Stars: number; + private level2Stars: number; + private level3Stars: number; + + private backgroundMusic: Phaser.Sound.BaseSound; + private musicMuted: boolean = false; + + constructor() { + super({ key: "Level3" }); + } + + preload() { + this.load.audio("cave-music", "assets/level3/Dark-chamber.mp3"); + this.load.audio("collect-sound", "assets/sounds/collectsound.mp3"); + this.load.audio("dooropen-sound", "assets/sounds/dooropensound.mp3"); + this.load.audio("injure-sound", "assets/sounds/injuresound.mp3"); + this.load.audio("wrong-sound", "assets/sounds/wrongsound.mp3"); + this.load.audio("pop-sound", "assets/sounds/popsound.mp3"); + this.load.audio("death-sound", "assets/sounds/playerdiesound.mp3"); + this.load.audio("menu-sound", "assets/sounds/menusound.mp3"); + this.load.audio("win-sound", "assets/sounds/winsound.mp3"); + + this.load.image( + "level3-background", + "assets/level3/level3-background.jpg" + ); + this.load.image("stackpack", "assets/stackpack.png"); + + this.load.image("EF-keys-white", "assets/EF-keys-white.png"); + + // Key + this.load.spritesheet("key", "assets/key.png", { + frameWidth: 768 / 24, + frameHeight: 32, + }); + + // Player + this.load.spritesheet("gal_right", "assets/Pink_Monster_Walk_6.png", { + frameWidth: 128, + frameHeight: 128, + }); + this.load.spritesheet( + "gal_left", + "assets/Pink_Monster_Walk_Left6.png", + { frameWidth: 128, frameHeight: 128 } + ); + this.load.spritesheet( + "gal_idle_right", + "assets/Pink_Monster_Idle_4.png", + { frameWidth: 128, frameHeight: 128 } + ); + this.load.spritesheet( + "gal_idle_left", + "assets/Pink_Monster_Idle_Left4.png", + { frameWidth: 128, frameHeight: 128 } + ); + this.load.spritesheet( + "gal_jump_right", + "assets/Pink_Monster_Jump_8.png", + { frameWidth: 128, frameHeight: 128 } + ); + this.load.spritesheet("gal_climb", "assets/Pink_Monster_Climb_4.png", { + frameWidth: 128, + frameHeight: 128, + }); + this.load.spritesheet( + "gal_hurt_right", + "assets/Pink_Monster_Hurt_4.png", + { frameWidth: 128, frameHeight: 128 } + ); + this.load.spritesheet( + "gal_hurt_left", + "assets/Pink_Monster_Hurt_Left4.png", + { frameWidth: 128, frameHeight: 128 } + ); + + // Skeleton + this.load.spritesheet( + "walk_right", + "assets/level3/Skeleton_With_VFX/Skeleton_01_White_Walk.png", + { + frameWidth: 96, + frameHeight: 64, + } + ); + this.load.spritesheet( + "walk_left", + "assets/level3/Skeleton_With_VFX/Skeleton_01_White_Walk_left.png", + { + frameWidth: 96, + frameHeight: 64, + } + ); + this.load.spritesheet( + "attack_right", + "assets/level3/Skeleton_With_VFX/Skeleton_01_White_Attack1.png", + { + frameWidth: 96, + frameHeight: 64, + } + ); + this.load.spritesheet( + "attack_left", + "assets/level3/Skeleton_With_VFX/Skeleton_01_White_Attack1_left.png", + { + frameWidth: 96, + frameHeight: 64, + } + ); + this.load.spritesheet( + "die_right", + "assets/level3/Skeleton_With_VFX/Skeleton_01_White_Die.png", + { + frameWidth: 96, + frameHeight: 64, + } + ); + this.load.spritesheet( + "die_left", + "assets/level3/Skeleton_With_VFX/Skeleton_01_White_Die_left.png", + { + frameWidth: 96, + frameHeight: 64, + } + ); + + this.load.image("ground", "assets/level3/first-platform.png"); + this.load.image("level3-platform", "assets/level3/lava-platform.png"); + this.load.image( + "level3-platform-small", + "assets/level3/lava-platform-small.png" + ); + this.load.image("lava", "assets/level3/lava.png"); + this.load.image("fireball", "assets/level3/fireball.png"); + this.load.image("stone1", "assets/level3/stone1.png"); + this.load.image("stone2", "assets/level3/stone2.png"); + this.load.image("stone3", "assets/level3/stone3.png"); + + // Collectable items + this.load.image("water", "assets/level3/water-bucket.png"); + this.load.image("gas-mask", "assets/level3/gas-mask.png"); + this.load.image("sword", "assets/level3/sword.png"); + this.load.image("toolbox", "assets/level3/toolbox.png"); + this.load.image("chainsaw", "assets/level3/chainsaw.png"); + + // Usage areas + this.load.image("fire", "assets/level3/fire.png"); + this.load.image("toxic-gas", "assets/level3/toxic-gas.png"); + this.load.image("barrel", "assets/level3/toxic-gas-barrel.png"); + this.load.image("skeleton", "assets/level3/skeleton-man.png"); + this.load.image( + "danger-sign", + "assets/level3/electric-danger-sign.png" + ); + this.load.image("lift-off", "assets/level3/lift-off.png"); + this.load.image("lift-on", "assets/level3/lift-on.png"); + this.load.image("tree", "assets/level3/dead-tree.png"); + this.load.image("tree-cut", "assets/level3/dead-tree-cut.png"); + + this.load.image("red-door", "assets/level3/red-door.png"); + this.load.image("red-opendoor", "assets/level3/red-door-open.png"); + this.load.image("heart", "assets/heart_16.png"); + this.load.image("pop-button", "assets/freePop2.png"); + + this.load.image("pause-button", "assets/pause2.png"); + this.load.image("pause-popup", "assets/paused-popup.png"); + + this.load.image("3stars", "assets/FullStars.png"); + this.load.image("2stars", "assets/2Stars.png"); + this.load.image("1star", "assets/1Star.png"); + } + + create(data: GameMapData) { + this.level0State = data.level0State; + this.level1State = data.level1State; + this.level2State = data.level2State; + this.level3State = data.level3State; + this.level0Stars = data.level0Stars; + this.level1Stars = data.level1Stars; + this.level2Stars = data.level2Stars; + this.level3Stars = data.level3Stars; + + this.resetScene(); + // Resume all animations and tweens + this.anims.resumeAll(); + this.tweens.resumeAll(); + // Make it so player can enter keyboard input + if (this.input.keyboard) { + this.input.keyboard.enabled = true; + } + + setTimeout(() => (this.startTime = this.time.now)); + + this.lastDirection = "right"; + this.usedSword = false; + + this.freePopsLeftText = this.add + .text(285, 71, `${this.freePopsLeft}`, { + fontFamily: "Arial", + fontSize: 20, + color: "#D0F4DC", + }) + .setDepth(4); + + const backgroundImage = this.add + .image(0, 0, "level3-background") + .setOrigin(0, 0); + backgroundImage.setScale( + this.cameras.main.width / backgroundImage.width, + this.cameras.main.height / backgroundImage.height + ); + + this.backgroundMusic = this.sound.add("cave-music"); + this.backgroundMusic.play({ + loop: true, + volume: 0.25, + }); + + const stackpack = this.add + .image(0, 0, "stackpack") + .setPosition(1170, 165); + stackpack.setScale(0.26, 0.26); + + const EFkeys = this.add.image(10, 115, "EF-keys-white").setOrigin(0, 0); + EFkeys.setScale(0.35); + + this.anims.create({ + key: "turn", + frames: this.anims.generateFrameNumbers("key", { + start: 0, + end: 25, + }), + frameRate: 8, + repeat: -1, + }); + + this.player = this.physics.add + .sprite(300, 550, "gal_right") + .setScale(0.77, 0.77) + .setOrigin(0.5, 0.5); + this.player.setCollideWorldBounds(true); + + this.anims.create({ + key: "right", + frames: this.anims.generateFrameNumbers("gal_right", { + start: 0, + end: 5, + }), + repeat: -1, + }); + this.anims.create({ + key: "turn", + frames: [{ key: "gal_right", frame: 1 }], + }); + this.anims.create({ + key: "left", + frames: this.anims.generateFrameNumbers("gal_left", { + start: 0, + end: 5, + }), + repeat: -1, + }); + this.anims.create({ + key: "idle_right", + frames: this.anims.generateFrameNumbers("gal_idle_right", { + start: 0, + end: 3, + }), + frameRate: 10, + repeat: -1, + }); + this.anims.create({ + key: "idle_left", + frames: this.anims.generateFrameNumbers("gal_idle_left", { + start: 0, + end: 3, + }), + frameRate: 10, + repeat: -1, + }); + this.anims.create({ + key: "jump_right", + frames: this.anims.generateFrameNumbers("gal_jump_right", { + start: 0, + end: 7, + }), + }); + this.anims.create({ + key: "climb", + frames: this.anims.generateFrameNumbers("gal_climb", { + start: 0, + end: 3, + }), + frameRate: 15, + }); + this.anims.create({ + key: "hurt_right", + frames: this.anims.generateFrameNumbers("gal_hurt_right", { + start: 0, + end: 1, + }), + frameRate: 10, + repeat: 0, + }); + this.anims.create({ + key: "hurt_left", + frames: this.anims.generateFrameNumbers("gal_hurt_left", { + start: 4, + end: 2, + }), + frameRate: 10, + repeat: 0, + }); + + this.cursors = this.input.keyboard?.createCursorKeys(); + + // Creating lives + this.createHearts(); + + // Creating Free Pop Button + const popButton = this.add.image(225, 80, "pop-button").setScale(0.31); + popButton.setInteractive(); + + const originalScale = popButton.scaleX; + const hoverScale = originalScale * 1.05; + + // Pop button hover animation + popButton.on("pointerover", () => { + this.tweens.add({ + targets: popButton, + scaleX: hoverScale, + scaleY: hoverScale, + duration: 115, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + }); + popButton.on("pointerout", () => { + this.tweens.add({ + targets: popButton, + scaleX: originalScale, + scaleY: originalScale, + duration: 115, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + }); + popButton.on("pointerup", () => { + this.sound.play("pop-sound"); + this.freePop(); + this.freePopsLeft -= 1; + this.freePopsLeftText.setText(`${this.freePopsLeft}`); + if (this.freePopsLeft <= 0) { + popButton.setScale(originalScale); + popButton.disableInteractive(); + popButton.setTint(0x696969); + } + }); + + // Define keys 'E' and 'F' for collecting and using items respectively + this.keyE = this.input.keyboard?.addKey( + Phaser.Input.Keyboard.KeyCodes.E + ); + this.keyF = this.input.keyboard?.addKey( + Phaser.Input.Keyboard.KeyCodes.F + ); + + // Create platforms + this.platforms = this.physics.add.staticGroup(); + this.ground = this.platforms.create( + 150, + 720, + "ground" + ) as Phaser.Physics.Arcade.Image; + const floor = this.platforms.create( + 300, + 770, + "ground" + ) as Phaser.Physics.Arcade.Image; + + this.ground.setScale(0.5).refreshBody(); + this.ground.setAlpha(1); // Hide the ground platform + + floor.setScale(3, 0.5).refreshBody().setAlpha(0); + + const platform1 = this.platforms + .create(580, 695, "level3-platform-small") + .setScale(0.27, 0.3) + .refreshBody(); + const platform2 = this.platforms + .create(820, 695, "level3-platform-small") + .setScale(0.27, 0.3) + .refreshBody(); + const platform3 = this.platforms + .create(1140, 600, "level3-platform") + .setScale(0.38, 0.3) + .refreshBody(); + const platform4 = this.platforms + .create(220, 485, "level3-platform") + .setScale(0.6, 0.3) + .refreshBody(); + const platform5 = this.platforms + .create(670, 260, "level3-platform") + .setScale(0.77, 0.26) + .refreshBody(); + + this.physics.add.collider(this.player, this.platforms); + + this.stones = this.physics.add.group({ + immovable: true, + allowGravity: false, + }); + this.stone0 = this.stones.create( + 552, + 470, + "stone2" + ) as Phaser.Physics.Arcade.Image; + this.stone0.setScale(0.045, 0.045).refreshBody(); + this.stone0.setFlipX(true); + this.stone1 = this.stones.create( + 630, + 475, + "stone1" + ) as Phaser.Physics.Arcade.Image; + this.stone1.setScale(0.04, 0.04).refreshBody(); + this.stone2 = this.stones.create( + 700, + 470, + "stone2" + ) as Phaser.Physics.Arcade.Image; + this.stone2.setScale(0.04, 0.04).refreshBody(); + this.stone3 = this.stones.create( + 795, + 473, + "stone3" + ) as Phaser.Physics.Arcade.Image; + this.stone3.setScale(0.04, 0.04).refreshBody(); + this.stone4 = this.stones.create( + 885, + 489, + "stone1" + ) as Phaser.Physics.Arcade.Image; + this.stone4.setScale(0.04, 0.035).refreshBody(); + + this.physics.add.collider(this.player, this.stones); + + // Make stones fall when player jumps on them + const stonesArray = [ + this.stone4, + this.stone3, + this.stone2, + this.stone1, + this.stone0, + ]; + stonesArray.forEach((stone) => { + // Create a sensor above each stone to detect player overlap + const sensor = this.physics.add + .sprite(stone.x, stone.y + 20, "sensor") + .setAlpha(0); + sensor.body.setSize(stone.width * 0.02, 10).setOffset(-15, -40); // Adjust sensor size and offset as needed + //sensor.body.setAllowGravity(false); + this.physics.add.collider(sensor, this.stones); + if (this.player) { + // Make stones fall if player touches the sensors + this.physics.add.overlap(sensor, this.player, () => { + this.makeStonesFall.call(this); + }); + } + }); + + // Create lift platform + this.liftPlatforms = this.physics.add.group({ + immovable: true, // Make all platforms immovable by collisions + allowGravity: false, // Disable gravity for platforms + }); + + this.liftFloor = this.liftPlatforms.create( + 95, + 430, + "lift-off" + ) as Phaser.Physics.Arcade.Image; + this.liftFloor.setScale(0.23, 0.35).refreshBody(); + this.liftFloor + .setSize(this.liftFloor.width - 270, this.liftFloor.height - 195) + .setOffset(135, 130); + this.liftFloor.setVisible(true); + + this.liftWall1 = this.liftPlatforms.create( + 32, + 425, + "lift-off" + ) as Phaser.Physics.Arcade.Image; + this.liftWall1.setScale(0.02, 0.2).refreshBody(); + this.liftWall1.setVisible(false); + + this.liftWall2 = this.liftPlatforms.create( + 159, + 425, + "lift-off" + ) as Phaser.Physics.Arcade.Image; + this.liftWall2.setScale(0.02, 0.2).refreshBody(); + this.liftWall2.setVisible(false); + + // Add collision between player and platforms + this.physics.add.collider(this.player, this.liftPlatforms); + + this.lava = this.physics.add.staticGroup(); + this.lava.create(360, 650, "lava").setScale(0.75, 0.75); + this.lava.create(360 + 192, 650, "lava").setScale(0.75, 0.75); + this.lava.create(360 + 2 * 192, 650, "lava").setScale(0.75, 0.75); + this.lava.create(360 + 3 * 192, 650, "lava").setScale(0.75, 0.75); + this.lava.create(360 + 4 * 192, 650, "lava").setScale(0.75, 0.75); + this.lava.create(360 + 5 * 192, 650, "lava").setScale(0.75, 0.75); + + this.fireball1 = this.add.image( + 465, + 800, + "fireball" + ) as Phaser.Physics.Arcade.Image; + this.fireball1.setScale(0.07, -0.07); + + this.fireball2 = this.add.image( + 700, + 800, + "fireball" + ) as Phaser.Physics.Arcade.Image; + this.fireball2.setScale(0.07, -0.07); + + // Creating collectable items: water, gas mask, sword, toolbox, chainsaw, key + this.water = this.add.sprite(1230, 510, "water").setScale(0.2, 0.2); + this.water.setName("water"); + + this.gasMask = this.add.sprite(160, 610, "gas-mask").setScale(0.4, 0.4); + this.gasMask.setName("gas-mask"); + + this.sword = this.add.sprite(50, 600, "sword").setScale(0.2, 0.2); + this.sword.setRotation(Phaser.Math.DegToRad(10)); + this.sword.setName("sword"); + + this.toolbox = this.add.sprite(820, 610, "toolbox").setScale(0.2, 0.2); + this.toolbox.setName("toolbox"); + + this.chainsaw = this.add + .sprite(245, 385, "chainsaw") + .setScale(0.45, 0.45); + this.chainsaw.setRotation(Phaser.Math.DegToRad(85)); + this.chainsaw.setName("chainsaw"); + + this.key = this.add.sprite(580, 610, "key").setScale(2.5, 2.5); + this.key.setName("key"); + + // Creating usage areas: fire, toxic gas, skeleton, danger sign, lift, tree, door + this.fire = this.add.sprite(340, 410, "fire").setScale(0.5, 0.5); + this.fire.setName("fire"); + + this.toxicGas = this.add + .sprite(1100, 373, "toxic-gas") + .setScale(0.28, 0.28); + this.toxicGas.setName("toxic-gas"); + + this.gasBarrel = this.add + .sprite(1125, 500, "barrel") + .setScale(0.25, 0.25); + this.gasBarrel.setName("barrel"); + + this.skeleton = this.add + .sprite(830, 150, "walk_right") + .setScale(2.6, 2.6); + this.skeleton.setName("skeleton"); + + // Create animation from the sprite sheet + this.anims.create({ + key: "walk_right", // Animation key + frames: this.anims.generateFrameNumbers("walk_right", { + start: 0, + end: 9, + }), // Define frames for animation + frameRate: 8, // Frame rate of the animation + repeat: -1, // Repeat indefinitely + }); + this.anims.create({ + key: "walk_left", // Animation key + frames: this.anims.generateFrameNumbers("walk_left", { + start: 9, + end: 0, + }), // Define frames for animation + frameRate: 8, // Frame rate of the animation + repeat: -1, // Repeat indefinitely + }); + this.anims.create({ + key: "attack_right", // Animation key + frames: this.anims.generateFrameNumbers("attack_right", { + start: 0, + end: 9, + }), // Define frames for animation + frameRate: 10, // Frame rate of the animation + repeat: -1, // Repeat indefinitely + }); + this.anims.create({ + key: "attack_left", // Animation key + frames: this.anims.generateFrameNumbers("attack_left", { + start: 9, + end: 0, + }), // Define frames for animation + frameRate: 10, // Frame rate of the animation + repeat: -1, // Repeat indefinitely + }); + this.anims.create({ + key: "die_right", // Animation key + frames: this.anims.generateFrameNumbers("die_right", { + start: 0, + end: 12, + }), // Define frames for animation + frameRate: 12, // Frame rate of the animation + repeat: -1, // Repeat indefinitely + }); + this.anims.create({ + key: "die_left", // Animation key + frames: this.anims.generateFrameNumbers("die_left", { + start: 12, + end: 0, + }), // Define frames for animation + frameRate: 12, // Frame rate of the animation + repeat: -1, // Repeat indefinitely + }); + + this.dangerSign = this.add + .sprite(95, 355, "danger-sign") + .setScale(0.38, 0.4); + this.dangerSign.setName("danger-sign"); + + this.liftFloor.setName("lift"); + + this.tree = this.add.sprite(480, 130, "tree").setScale(0.5, 0.5); + this.tree.setName("tree"); + + this.door = this.physics.add + .image(880, 100, "red-door") + .setScale(0.1, 0.1); + this.physics.add.collider(this.door, this.platforms); + + // Set the depth of the player and skeleton sprites to a high value + this.player.setDepth(6); + this.skeleton.setDepth(4); + + this.liftFloor.setDepth(7); + this.toxicGas.setDepth(3); + + // Set the depth of other game objects to lower values + this.gasBarrel.setDepth(2); + this.ground.setDepth(2); + this.lava.setDepth(1); + this.fireball1.setDepth(0); + this.fireball2.setDepth(0); + //this.door.setDepth(1); + + // Resize collision boxes of player and everything that can be collided with + this.player + .setSize(this.player.width - 64, this.player.height) + .setOffset(32, 0); + + this.ground + .setSize( + this.ground.width * 0.5 - 30, + this.ground.height * 0.5 - 10 + ) + .setOffset(15, 5); + platform1 + .setSize(platform1.width * 0.27 - 16, platform1.height * 0.3 - 28) + .setOffset(8, 7); + platform2 + .setSize(platform2.width * 0.27 - 16, platform2.height * 0.3 - 28) + .setOffset(8, 7); + platform3 + .setSize(platform3.width * 0.35 - 24, platform3.height * 0.3 - 75) + .setOffset(12, 9); + platform4 + .setSize(platform4.width * 0.6 - 34, platform4.height * 0.3 - 75) + .setOffset(17, 6); + platform5 + .setSize(platform5.width * 0.77 - 50, platform5.height * 0.3 - 75) + .setOffset(25, 6); + this.stone0 + .setSize( + this.stone0.width * 0.72 - 30, + this.stone0.height * 0.045 - 20 + ) + .setOffset(330, 120); + this.stone1 + .setSize( + this.stone1.width * 0.75 - 30, + this.stone1.height * 0.04 - 20 + ) + .setOffset(270, 250); + this.stone2 + .setSize( + this.stone2.width * 0.72 - 26, + this.stone2.height * 0.04 - 15 + ) + .setOffset(390, 90); + this.stone3 + .setSize( + this.stone3.width * 0.92 - 26, + this.stone3.height * 0.04 - 25 + ) + .setOffset(150, 220); + this.stone4 + .setSize( + this.stone4.width * 0.78 - 30, + this.stone4.height * 0.035 - 34 + ) + .setOffset(270, 130); + + this.door + .setSize(this.door.width, this.door.height - 60) + .setOffset(0, 0); + + // Make collectable items continuously pulsate + this.createPulsateEffect( + this, + this.water, + 1.15, // Scale factor for pulsating effect + 1000 // Duration of each tween cycle in milliseconds + ); + this.createPulsateEffect( + this, + this.gasMask, + 1.15, // Scale factor for pulsating effect + 1000 // Duration of each tween cycle in milliseconds + ); + this.createPulsateEffect( + this, + this.sword, + 1.15, // Scale factor for pulsating effect + 1000 // Duration of each tween cycle in milliseconds + ); + this.createPulsateEffect( + this, + this.toolbox, + 1.15, // Scale factor for pulsating effect + 1000 // Duration of each tween cycle in milliseconds + ); + this.createPulsateEffect( + this, + this.chainsaw, + 1.15, // Scale factor for pulsating effect + 1000 // Duration of each tween cycle in milliseconds + ); + + // Creating detection area for using gas mask + this.gasMaskDetectionArea = this.add.rectangle(1005, 400, 130, 170); + this.physics.world.enable(this.gasMaskDetectionArea); + this.physics.add.collider(this.gasMaskDetectionArea, this.platforms); + + // Creating a highlighted rectangle to indicate where to use gas mask + this.gasMaskHighlightBox = this.add.rectangle( + 1119, + 440, + 120, + 280, + 0xffff00 + ); + this.gasMaskHighlightBox.setAlpha(0.25); + this.gasMaskHighlightBox.setVisible(false); + + // Creating detection area for using water + this.waterDetectionArea = this.add.rectangle(450, 360, 110, 170); + this.physics.world.enable(this.waterDetectionArea); + this.physics.add.collider(this.waterDetectionArea, this.platforms); + + // Creating a highlighted rectangle to indicate where to use water + this.waterHighlightBox = this.add.rectangle( + 340, + 410, + 130, + 120, + 0xffff00 + ); + this.waterHighlightBox.setAlpha(0.25); + this.waterHighlightBox.setVisible(false); + + // Creating detection area for using toolbox + this.toolboxDetectionArea = this.add.rectangle(205, 360, 90, 170); + this.physics.world.enable(this.toolboxDetectionArea); + this.physics.add.collider(this.toolboxDetectionArea, this.platforms); + + // Creating a highlighted rectangle to indicate where to use toolbox + this.toolboxHighlightBox = this.add.rectangle( + 95, + 370, + 160, + 180, + 0xffff00 + ); + this.toolboxHighlightBox.setAlpha(0.25); + this.toolboxHighlightBox.setVisible(false); + + // Creating detection area for using chainsaw + this.chainsawDetectionArea = this.add.rectangle(385, 80, 100, 170); + this.physics.world.enable(this.chainsawDetectionArea); + this.physics.add.collider(this.chainsawDetectionArea, this.platforms); + + // Creating a highlighted rectangle to indicate where to use chainsaw + this.chainsawHighlightBox = this.add.rectangle( + 510, + 130, + 180, + 240, + 0xffff00 + ); + this.chainsawHighlightBox.setAlpha(0.25); + this.chainsawHighlightBox.setVisible(false); + + // Creating detection area for using sword + this.swordDetectionArea = this.add.rectangle(815, 100, 430, 170); + this.physics.world.enable(this.swordDetectionArea); + this.physics.add.collider(this.swordDetectionArea, this.platforms); + + // Creating a highlighted rectangle to indicate where to use sword + this.swordHighlightBox = this.add.rectangle( + 830, + 170, + 110, + 130, + 0xffff00 + ); + this.swordHighlightBox.setAlpha(0.25); + this.swordHighlightBox.setVisible(false); + + // Creating detection area for using key + this.keyDetectionArea = this.add.rectangle(870, 105, 130, 200); + this.physics.world.enable(this.keyDetectionArea); + this.physics.add.collider(this.keyDetectionArea, this.platforms); + + // Creating a highlighted rectangle to indicate where to use key + this.keyHighlightBox = this.add.rectangle(870, 135, 170, 200, 0xffff00); + this.keyHighlightBox.setAlpha(0.25); + this.keyHighlightBox.setVisible(false); + + // Defining lava area for dying + this.lavaArea = this.add.rectangle(840, 670, 940, 20); + this.physics.world.enable(this.lavaArea); + this.physics.add.collider(this.lavaArea, floor); + + // Defining toxic gas area for dying + this.toxicGasArea = this.add.rectangle(1125, 400, 45, 250); + this.physics.world.enable(this.toxicGasArea); + this.physics.add.collider(this.toxicGasArea, this.platforms); + + // Defining fire area for dying + this.fireArea = this.add.rectangle(340, 380, 80, 100); + this.physics.world.enable(this.fireArea); + this.physics.add.collider(this.fireArea, this.platforms); + + // Defining lift area for dying + this.liftArea = this.add.rectangle(95, 340, 100, 150); + this.physics.world.enable(this.liftArea); + this.physics.add.collider(this.liftArea, this.platforms); + + // Defining tree area for dying + this.treeArea = this.add.rectangle(510, 100, 100, 200); + this.physics.world.enable(this.treeArea); + this.physics.add.collider(this.treeArea, this.platforms); + + // Make fireballs jump out every few seconds after jumping once at beginning + if (!this.isPaused) { + this.animateBothFireballs(); + } + + // Pause Menu & Level Complete Menu + // Creating Pause Group for Buttons and Pause Popup + const pauseGroup = this.add.group(); + + // Creating Pause Popup + const pausePopup = this.add.image(650, 350, "pause-popup"); + pausePopup.setOrigin(0.5); + pausePopup.setDepth(20); + pauseGroup.add(pausePopup); + + // Exit button for Pause popup + const exitButton = this.add.rectangle(640, 530, 200, 75).setDepth(20); + exitButton.setOrigin(0.5); + exitButton.setInteractive(); + pauseGroup.add(exitButton); + + exitButton.on("pointerover", () => { + exitButton.setFillStyle(0xffff00).setAlpha(0.5); + }); + + exitButton.on("pointerout", () => { + exitButton.setFillStyle(); + }); + + exitButton.on("pointerup", () => { + this.sound.play("menu-sound"); + this.backgroundMusic.stop(); + this.isPaused = false; + this.resetScene(); + this.scene.start("game-map", { + level0State: this.level0State, + level1State: this.level1State, + level2State: this.level2State, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + }); + + // Return button for Pause popup + const restartButton = this.add + .rectangle(640, 425, 200, 75) + .setDepth(20); + restartButton.setOrigin(0.5); + restartButton.setInteractive(); + pauseGroup.add(restartButton); + + restartButton.on("pointerover", () => { + restartButton.setFillStyle(0xffff00).setAlpha(0.5); + }); + + restartButton.on("pointerout", () => { + restartButton.setFillStyle(); + }); + + restartButton.on("pointerup", () => { + this.sound.play("menu-sound"); + this.backgroundMusic.stop(); + this.backgroundMusic.destroy(); + this.resetScene(); + this.scene.start("Level3", { + level0State: this.level0State, + level1State: this.level1State, + level2State: this.level2State, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + }); + + // Resume button for Pause popup + const resumeButton = this.add.rectangle(640, 320, 200, 75).setDepth(20); + resumeButton.setOrigin(0.5); + resumeButton.setInteractive(); + pauseGroup.add(resumeButton); + + resumeButton.on("pointerover", () => { + resumeButton.setFillStyle(0xffff00).setAlpha(0.5); + }); + + resumeButton.on("pointerout", () => { + resumeButton.setFillStyle(); + }); + + resumeButton.on("pointerup", () => { + this.sound.play("menu-sound"); + pauseGroup.setVisible(false); + this.pauseTime(); + // Resume all animations and tweens + this.anims.resumeAll(); + this.tweens.resumeAll(); + // Make it so player can enter keyboard input + if (this.input.keyboard) { + this.input.keyboard.enabled = true; + } + // Make it so player can click Free Pop button + if (this.freePopsLeft > 0) { + popButton.setInteractive(); + } + }); + + // No music button for Pause popup + const muteMusic = this.add.rectangle(585, 217, 90, 90).setDepth(20); + muteMusic.setOrigin(0.5); + muteMusic.setInteractive(); + pauseGroup.add(muteMusic); + + muteMusic.on("pointerover", () => { + muteMusic.setFillStyle(0xffff00).setAlpha(0.5); + }); + + muteMusic.on("pointerout", () => { + muteMusic.setFillStyle(); + }); + + // Has to get fixed once we have sound + muteMusic.on("pointerup", () => { + this.sound.play("menu-sound"); + this.musicMuted = !this.musicMuted; + if (this.musicMuted) { + this.backgroundMusic.pause(); + } else { + this.backgroundMusic.resume(); + } + }); + + // No sound button for Pause popup + const muteSound = this.add.rectangle(700, 217, 90, 90).setDepth(20); + muteSound.setOrigin(0.5); + muteSound.setInteractive(); + pauseGroup.add(muteSound); + + muteSound.on("pointerover", () => { + muteSound.setFillStyle(0xffff00).setAlpha(0.5); + }); + + muteSound.on("pointerout", () => { + muteSound.setFillStyle(); + }); + + // Has to get fixed once we have sound + muteSound.on("pointerup", () => { + this.sound.play("menu-sound"); + pauseGroup.setVisible(false); + }); + + pauseGroup.setVisible(false); + + // Creating Pause Button + const pauseButton = this.add + .image(30, 30, "pause-button") + .setScale(0.25); + pauseButton.setInteractive(); + + const pauseOriginalScale = pauseButton.scaleX; + const pauseHoverScale = pauseOriginalScale * 1.05; + + // Change scale on hover + pauseButton.on("pointerover", () => { + this.tweens.add({ + targets: pauseButton, + scaleX: pauseHoverScale, + scaleY: pauseHoverScale, + duration: 115, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + }); + + // Restore original scale when pointer leaves + pauseButton.on("pointerout", () => { + this.tweens.add({ + targets: pauseButton, + scaleX: pauseOriginalScale, + scaleY: pauseOriginalScale, + duration: 115, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + }); + + pauseButton.on("pointerup", () => { + this.sound.play("menu-sound"); + if (!this.isPaused) { + this.pauseTime(); + pauseGroup.setVisible(true); + // Pause all animations and tweens + this.anims.pauseAll(); + this.tweens.pauseAll(); + // Make it so player can't enter keyboard input + if (this.input.keyboard) { + this.input.keyboard.enabled = false; + } + // Make it so player can't click Free Pop button + popButton.disableInteractive(); + } + }); + + // Creating timer + this.timerText = this.add.text(60, 15, "Time: 0", { + fontSize: "32px", + color: "#ffffff", + }); + this.startTime = this.time.now; + this.pausedTime = 0; + + // Level complete popup - still working + const completeExitButton = this.add.circle(790, 185, 35).setDepth(20); + completeExitButton.setInteractive(); + completeExitButton.on("pointerover", () => { + completeExitButton.setFillStyle(0xffff00).setAlpha(0.5); + }); + completeExitButton.on("pointerout", () => { + completeExitButton.setFillStyle(); + }); + + const completeReplayButton = this.add.circle(510, 505, 55).setDepth(20); + completeReplayButton.setInteractive(); + completeReplayButton.on("pointerover", () => { + completeReplayButton.setFillStyle(0xffff00).setAlpha(0.5); + }); + completeReplayButton.on("pointerout", () => { + completeReplayButton.setFillStyle(); + }); + + const completeMenuButton = this.add.circle(655, 530, 55).setDepth(20); + completeMenuButton.setInteractive(); + completeMenuButton.on("pointerover", () => { + completeMenuButton.setFillStyle(0xffff00).setAlpha(0.5); + }); + completeMenuButton.on("pointerout", () => { + completeMenuButton.setFillStyle(); + }); + + const completeNextButton = this.add.circle(800, 505, 55).setDepth(20); + completeNextButton.setInteractive(); + completeNextButton.on("pointerover", () => { + completeNextButton.setFillStyle(0xffff00).setAlpha(0.5); + }); + completeNextButton.on("pointerout", () => { + completeNextButton.setFillStyle(); + }); + + this.threeStarsPopup = this.add.group(); + const threeStars = this.add.image(650, 350, "3stars"); + this.threeStarsPopup.add(threeStars); + this.threeStarsPopup.add(completeExitButton); + this.threeStarsPopup.add(completeReplayButton); + this.threeStarsPopup.add(completeMenuButton); + this.threeStarsPopup.add(completeNextButton); + + this.twoStarsPopup = this.add.group(); + const twoStars = this.add.image(650, 350, "2stars"); + this.twoStarsPopup.add(twoStars); + this.twoStarsPopup.add(completeExitButton); + this.twoStarsPopup.add(completeReplayButton); + this.twoStarsPopup.add(completeMenuButton); + this.twoStarsPopup.add(completeNextButton); + + this.oneStarPopup = this.add.group(); + const oneStar = this.add.image(650, 350, "1star"); + this.oneStarPopup.add(oneStar); + this.oneStarPopup.add(completeExitButton); + this.oneStarPopup.add(completeReplayButton); + this.oneStarPopup.add(completeMenuButton); + this.oneStarPopup.add(completeNextButton); + + completeExitButton.on("pointerup", () => { + this.sound.play("menu-sound"); + this.backgroundMusic.stop(); + this.isPaused = false; + if (threeStars.visible) { + this.threeStarsPopup.setVisible(false); + } + if (twoStars.visible) { + this.twoStarsPopup.setVisible(false); + } + if (oneStar.visible) { + this.oneStarPopup.setVisible(false); + } + }); + + completeReplayButton.on("pointerup", () => { + this.sound.play("menu-sound"); + this.backgroundMusic.stop(); + this.backgroundMusic.destroy(); + this.resetScene(); + this.scene.start("Level3", { + level0State: this.level0State, + level1State: this.level1State, + level2State: this.level2State, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + }); + + completeMenuButton.on("pointerup", () => { + this.sound.play("menu-sound"); + this.backgroundMusic.stop(); + this.backgroundMusic.destroy(); + // Transition to ending cut scene if level 3 completed for the first time + if (data.level3State != 3) { + setTimeout(() => { + this.scene.start("EndCutScene", { + level0State: this.level0State, + level1State: this.level1State, + level2State: this.level2State, + level3State: 3, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + }, 500); + } else { + setTimeout(() => { + this.scene.start("game-map", { + level0State: this.level0State, + level1State: this.level1State, + level2State: this.level2State, + level3State: 3, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + }, 500); + } + }); + + completeNextButton.on("pointerup", () => { + this.sound.play("menu-sound"); + this.backgroundMusic.stop(); + this.backgroundMusic.destroy(); + // Transition to ending cut scene + setTimeout(() => { + this.scene.start("EndCutScene", { + level0State: this.level0State, + level1State: this.level1State, + level2State: this.level2State, + level3State: 3, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + }, 500); + }); + + this.threeStarsPopup.setVisible(false); + this.twoStarsPopup.setVisible(false); + this.oneStarPopup.setVisible(false); + } + + private updateStackView() { + const offsetX = 1170; // starting X position for stack items + const offsetY = 270; // starting Y position for stack items + const padding = 10; + + let currTotalHeight = 0; + + let stackItemScale = 1; + if (this.stack.length == 5) { + stackItemScale = 0.8; + } + + this.stack.forEach((item) => { + // Calculate and set (x, y) position of stack items in stackpack view + item.setOrigin(0.5, 0); + item.setScale(item.scale * stackItemScale); + const stackItemX = offsetX; + const stackItemY = + offsetY - item.displayHeight - currTotalHeight - padding; + currTotalHeight += item.displayHeight + padding; + + // Animation to drop the item into its position in the stackpack + this.tweens.add({ + targets: item, + x: stackItemX, + y: stackItemY, + duration: 800, + ease: "Cubic.InOut", + onComplete: () => { + this.isPushingMap[item.name] = false; + }, + }); + }); + } + + private collectItem(item: Phaser.GameObjects.Sprite) { + this.sound.play("collect-sound"); + if (this.collectedItems.includes(item)) { + return; + } + + this.isPushingMap[item.name] = true; + + // Save the x and y scales of the collected item + const currScaleX = item.scaleX; + const currScaleY = item.scaleY; + + // Animation to make item bigger, then smaller, and then fly up to stackpack + this.tweens.add({ + targets: item, + scaleX: currScaleX * 1.5, // Scale up item size for a bit + scaleY: currScaleY * 1.5, + duration: 180, + ease: "Exponential.InOut", + onComplete: () => { + this.tweens.add({ + targets: item, + scaleX: currScaleX, // Scale down item back to normal + scaleY: currScaleY, + duration: 150, + ease: "Exponential.InOut", + onComplete: () => { + // Move item to the stackpack view + this.tweens.add({ + targets: item, + x: 1170, + y: -10, // Y position of item before it is dropped into its actual position in stackpack + scaleX: currScaleX * 0.5, // Scale down the item for stackpack view + scaleY: currScaleY * 0.5, + rotation: Math.PI * 2, // Rotate the item while moving it to stackpack + duration: 940, + ease: "Cubic.In", + onComplete: () => { + // Add the item to the stack + this.stack.push(item); + this.updateStackView(); + }, + }); + }, + }); + }, + }); + + // Add the item to the grand list of collected items + this.collectedItems.push(item); + this.stopPulsateEffect(); + + this.updateStackView(); + } + + private useItem() { + if (this.isPushingMap[this.stack[this.stack.length - 1].name]) { + return; // Prevent popping if a push is in progress + } + + // Remove the top item from the stackpack + const poppedItem = this.stack.pop(); + + if (poppedItem) { + // Add the item to the list of used items + this.usedItems.push(poppedItem); + + // Animation to fade item out from stackpack and then fade in in its new location + this.tweens.add({ + targets: poppedItem, + alpha: 0, // Fade out + duration: 200, + onComplete: () => { + // Set item origin back to default (center) + poppedItem.setOrigin(0.5, 0.5); + + // Move popped item to location it will be used + if (poppedItem.name === "gas-mask") { + poppedItem.setDepth(10); + // Move the gas mask towards the player + poppedItem.setPosition(1005, 400); + // Scale down the gas mask to make it disappear + this.tweens.add({ + targets: poppedItem, + x: this.player?.x, + y: this.player?.y, + scaleX: 0.2, + scaleY: 0.2, + duration: 300, + delay: 500, + onComplete: () => { + poppedItem.setVisible(false); + }, + }); + this.tweens.add({ + targets: [this.toxicGas, this.gasBarrel], + alpha: 0, // Fade out + duration: 1200, + }); + this.toxicGasArea.setPosition(-100, -100); + this.gasMaskHighlightBox.setVisible(false); + } + if (poppedItem.name === "water") { + // Play animation to tilt the water to the side + this.tweens.add({ + targets: poppedItem, + angle: -75, // Tilt the water to the side + duration: 500, // Duration of the tilt animation + yoyo: true, // Play the animation in reverse + repeat: 0, // No repeat + onStart: () => { + poppedItem.setPosition(400, 315); + }, + onComplete: () => { + poppedItem.setVisible(false); + this.tweens.add({ + targets: this.fire, + alpha: 0, // Fade out + duration: 500, + }); + }, + }); + this.fireArea.setPosition(-100, -100); + this.waterHighlightBox.setVisible(false); + } + if (poppedItem.name === "toolbox") { + poppedItem.setVisible(false); + this.tweens.add({ + targets: this.dangerSign, + alpha: 0, // Fade out + duration: 500, + onComplete: () => { + this.liftFloor?.setTexture("lift-on"); + // Start the lift + this.tweens.add({ + targets: this.liftPlatforms.getChildren(), // Move all platforms in the group + y: "-=190", // Move the lift platforms up + delay: 600, + duration: 2200, // Duration of the movement + yoyo: true, // Platforms will return to their original position + repeat: -1, // Repeat indefinitely + ease: "Linear", + }); + }, + }); + this.liftArea.setPosition(-100, -100); + this.toolboxHighlightBox.setVisible(false); + } + if (poppedItem.name === "chainsaw") { + // Play animation to rotate and move the chainsaw side to side + if (this.chainsaw) { + this.tweens.add({ + targets: poppedItem, + angle: 45, // Rotate the chainsaw to the side + x: 570 + 20, // Move the chainsaw a bit to the right + duration: 500, // Duration of the rotation and movement + yoyo: true, // Play the animation in reverse + repeat: 0, // No repeat + onStart: () => { + poppedItem.setDepth(3); + poppedItem.setPosition(570, 170); + }, + onComplete: () => { + // Execute callback function after animation finishes + poppedItem.setVisible(false); + this.tree?.setScale(0.25); + this.tree?.setPosition(537, 205); + this.tree?.setTexture("tree-cut"); + }, + }); + } + this.treeArea.setPosition(-100, -100); + this.chainsawHighlightBox.setVisible(false); + } + if (poppedItem.name === "sword") { + this.usedSword = true; + this.skeletonDead = true; + poppedItem.setDepth(5); + if (this.sword && this.player && this.skeleton) { + // Set the sword's initial position to the player's location + this.sword.setPosition( + this.player.x, + this.player.y + ); + + if (this.skeleton.x > this.player.x) { + // If the skeleton is to the right of the player, rotate the sword to face right + this.sword.setRotation( + Phaser.Math.DegToRad(132) + ); // Facing right + } else { + // If the skeleton is to the left of the player, rotate the sword to face left + this.sword.setRotation( + Phaser.Math.DegToRad(-48) + ); // Facing left + } + + // Make the sword move towards the skeleton and rotate down after passing it + this.tweens.add({ + targets: this.sword, + x: this.skeleton.x + 100, + y: this.skeleton.y, + duration: 300, // Adjust duration as needed + onComplete: () => { + if (this.sword) { + this.tweens.add({ + targets: this.sword, + scaleX: this.sword.scaleX * 0.9, + scaleY: this.sword.scaleY * 0.9, + x: 1000, + y: 170, + rotation: Phaser.Math.DegToRad(222), + duration: 100, + onComplete: () => { + // Play the die animation for the skeleton + if ( + this.skeleton && + this.player && + this.skeleton.x < + this.player.x + ) { + this.skeleton.anims.play( + "die_right", + true + ); + } else if ( + this.skeleton && + this.player && + this.skeleton.x > + this.player.x + ) { + this.skeleton.anims.play( + "die_left", + true + ); + } + + // After the die animation completes, remove the skeleton from the scene + setTimeout(() => { + this.skeleton?.setVisible( + false + ); + this.skeleton?.setPosition( + -600, + -600 + ); + this.skeleton?.destroy(); + }, 1000); + }, + }); + } + }, + }); + } + this.swordHighlightBox.setVisible(false); + } + if (poppedItem.name === "key") { + this.sound.play("dooropen-sound"); + this.keyHighlightBox.setVisible(false); + this.door?.setTexture("red-opendoor"); + this.pauseTime(); + // Make the player get sucked into the door + if (this.player && this.door) { + this.tweens.add({ + targets: this.player, + scaleX: 0.27, + scaleY: 0.27, + rotation: Math.PI * 3, + x: this.door.x - 10, + y: this.door.y + 15, + duration: 800, + onComplete: () => { + this.sound.play("win-sound"); + if (this.input.keyboard) { + this.input.keyboard.enabled = false; + } + this.player?.setVisible(false); + // TODO: Transition to game map OR to ending cut scene: set level 3 to completed status + var completedTime = this.add + .text( + 640, + 345, + this.formatTime(this.elapsedTime), + { + fontSize: "40px", + color: "#000000", + } + ) + .setDepth(21) + .setVisible(false); + // Level popup depends on time it takes to complete + if (this.elapsedTime <= 30000) { + this.starsPopup = this.threeStarsPopup; + this.threeStarsPopup.add(completedTime); + this.threeStarsPopup + .setVisible(true) + .setDepth(20); + this.level3Stars = 3; + } + if ( + this.elapsedTime > 30000 && + this.elapsedTime <= 60000 + ) { + this.starsPopup = this.twoStarsPopup; + this.twoStarsPopup.add(completedTime); + this.twoStarsPopup + .setVisible(true) + .setDepth(20); + // Update stars if its better than previous time + if (this.level3Stars < 2) { + this.level3Stars = 2; + } + } + if (this.elapsedTime > 60000) { + this.starsPopup = this.oneStarPopup; + this.oneStarPopup.add(completedTime); + this.oneStarPopup + .setVisible(true) + .setDepth(20); + // Update stars if its better than previous time + if (this.level3Stars < 1) { + this.level3Stars = 1; + } + } + // Animate level complete text + this.tweens.add({ + targets: this.starsPopup, + alpha: 1, + duration: 5000, + ease: "Linear", + delay: 1000, // Delay the animation slightly + }); + + this.level3State = 3; + }, + }); + } + } + + this.tweens.add({ + targets: poppedItem, + scaleX: poppedItem.scaleX * 2, + scaleY: poppedItem.scaleY * 2, + alpha: 1, // Fade in + duration: 300, + onComplete: () => { + this.updateStackView(); + }, + }); + }, + }); + } + } + + private popWrongItem(usageArea: Phaser.GameObjects.Rectangle) { + if (this.isPushingMap[this.stack[this.stack.length - 1].name]) { + return; // Prevent popping if a push is in progress + } + + this.poppingWrongItem = true; + this.loseLife(); + this.poppingWrongItem = false; + + // Remove the top item from the stackpack + const poppedItem = this.stack.pop(); + + if (poppedItem && this.lives !== 0) { + // Remove popped item from grand list of collected items + const index = this.collectedItems.indexOf(poppedItem); + if (index !== -1) { + this.collectedItems.splice(index, 1); + } + + // Animation to flash red in location player tried to use item + this.tweens.add({ + targets: usageArea, + alpha: 0, + duration: 300, + yoyo: true, + repeat: 1, + onStart: () => { + usageArea.alpha = 0.55; + usageArea.fillColor = 0xff0000; // Make area red + this.flashingRed = true; + }, + onComplete: () => { + usageArea.alpha = 0.25; // Reset area color and alpha + usageArea.fillColor = 0xffff00; + this.flashingRed = false; + }, + }); + + // Animation to fade item out from stackpack and then fade in in its new location + this.tweens.add({ + targets: poppedItem, + alpha: 0, // Fade out + duration: 200, + onComplete: () => { + // Set item origin back to default (center) + poppedItem.setOrigin(0.5, 0.5); + + let originalScaleX = 0; + let originalScaleY = 0; + // Move popped item to its original location + if (poppedItem.name === "gas-mask") { + poppedItem.setPosition(160, 610); + originalScaleX = 0.4; + originalScaleY = 0.4; + } + if (poppedItem.name === "water") { + poppedItem.setPosition(1230, 510); + originalScaleX = 0.2; + originalScaleY = 0.2; + } + if (poppedItem.name === "toolbox") { + poppedItem.setPosition(820, 610); + originalScaleX = 0.2; + originalScaleY = 0.2; + } + if (poppedItem.name === "chainsaw") { + poppedItem.setPosition(245, 385); + originalScaleX = 0.45; + originalScaleY = 0.45; + } + if (poppedItem.name === "sword") { + poppedItem.setPosition(50, 600); + originalScaleX = 0.2; + originalScaleY = 0.2; + } + if (poppedItem.name === "key") { + poppedItem.setPosition(580, 610); + originalScaleX = 2.5; + originalScaleY = 2.5; + } + + this.tweens.add({ + targets: poppedItem, + scaleX: originalScaleX, + scaleY: originalScaleY, + alpha: 1, // Fade in + duration: 300, + onComplete: () => { + this.updateStackView(); + this.createPulsateEffect( + this, + poppedItem, + 1.15, + 1000 + ); + }, + }); + }, + }); + } + } + + // Animation for using free pop + private freePop() { + if (this.isPushingMap[this.stack[this.stack.length - 1].name]) { + return; // Prevent popping if a push is in progress + } + + // Remove the top item from the stackpack + const poppedItem = this.stack.pop(); + + if (poppedItem && this.lives !== 0) { + // Remove popped item from grand list of collected items + const index = this.collectedItems.indexOf(poppedItem); + if (index !== -1) { + this.collectedItems.splice(index, 1); + } + + // Animation to fade item out from stackpack and then fade in in its new location + this.tweens.add({ + targets: poppedItem, + alpha: 0, // Fade out + duration: 200, + onComplete: () => { + // Set item origin back to default (center) + poppedItem.setOrigin(0.5, 0.5); + + let originalScaleX = 0; + let originalScaleY = 0; + // Move popped item to its original location + if (poppedItem.name === "gas-mask") { + poppedItem.setPosition(160, 610); + originalScaleX = 0.4; + originalScaleY = 0.4; + } + if (poppedItem.name === "water") { + poppedItem.setPosition(1230, 510); + originalScaleX = 0.2; + originalScaleY = 0.2; + } + if (poppedItem.name === "toolbox") { + poppedItem.setPosition(820, 610); + originalScaleX = 0.2; + originalScaleY = 0.2; + } + if (poppedItem.name === "chainsaw") { + poppedItem.setPosition(245, 385); + originalScaleX = 0.45; + originalScaleY = 0.45; + } + if (poppedItem.name === "sword") { + poppedItem.setPosition(50, 600); + originalScaleX = 0.2; + originalScaleY = 0.2; + } + if (poppedItem.name === "key") { + poppedItem.setPosition(580, 610); + originalScaleX = 2.5; + originalScaleY = 2.5; + } + + this.tweens.add({ + targets: poppedItem, + scaleX: originalScaleX, + scaleY: originalScaleY, + alpha: 1, // Fade in + duration: 300, + onComplete: () => { + this.updateStackView(); + this.createPulsateEffect( + this, + poppedItem, + 1.15, + 1000 + ); + }, + }); + }, + }); + } + } + + private createHearts() { + this.lives = 3; + this.hearts = []; + + for (let i = 0; i < 3; i++) { + this.hearts.push( + this.add.sprite(32 + i * 50, 80, "heart").setScale(0.5) + ); + } + } + + private loseLife() { + if (!this.isColliding && this.player) { + this.isColliding = true; + if (this.poppingWrongItem) { + this.sound.play("wrong-sound"); + } else { + this.sound.play("injure-sound"); + } + + this.player.setVelocity(0, 0); + if (this.lastDirection === "right") { + this.player.anims.play("hurt_right"); + } else { + this.player.anims.play("hurt_left"); + } + this.lives--; + + // Removing hearts from free pop + const heartToRemove = this.hearts?.pop(); + if (heartToRemove) { + //heartToRemove.destroy(); + this.tweens.add({ + targets: heartToRemove, + scaleX: 0.8, + scaleY: 0.8, + duration: 200, + yoyo: true, + onComplete: () => { + heartToRemove.setTint(0x000000); // Make heart black + heartToRemove.setScale(0.5); // Reset the heart's scale + }, + }); + } + + console.log(this.lives); + if (this.lives === 0) { + this.playerDie(); + console.log("dying"); + } + + // Reset isColliding flag + this.time.delayedCall( + 500, + () => { + this.isColliding = false; + if (this.collidingWithDeath) { + this.player?.setPosition(300, 550); // Reset player's position + // Reset stone bridge if it has fallen + if ( + this.stone0 && + this.stone1 && + this.stone2 && + this.stone3 && + this.stone4 + ) { + if ( + this.stone0.y > 470 || + this.stone1.y > 475 || + this.stone2.y > 470 || + this.stone3.y > 473 || + this.stone4.y > 489 + ) { + this.resetStones(); + } + } + this.collidingWithDeath = false; + } + }, + [], + this + ); + } + } + + private playerDie() { + this.sound.play("death-sound"); + this.player?.setTint(0xff0000); + + this.time.delayedCall(300, () => { + this.scene.launch("YouDiedScene3", { + currentLevelKey: this.scene.key, + level0State: this.level0State, + level1State: this.level1State, + level2State: this.level2State, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + this.player?.clearTint(); + + // Reset the stack and collected items + this.stack = []; + this.updateStackView(); + this.collectedItems = []; + this.usedItems = []; + this.lives = 3; + this.createHearts(); + this.freePopsLeft = 4; + this.backgroundMusic.stop(); + this.backgroundMusic.destroy(); + this.poppingWrongItem = false; + }); + } + + private resetScene() { + // Reset the stack and collected items + this.stack = []; + this.updateStackView(); + this.collectedItems = []; + this.usedItems = []; + this.lives = 3; + this.createHearts(); + this.freePopsLeft = 4; + this.isPaused = false; + this.skeletonDead = false; + this.flashingRed = false; + this.isColliding = false; + this.collidingWithDeath = false; + this.usedSword = false; + this.playerLostLife = false; + this.poppingWrongItem = false; + } + + private formatTime(milliseconds: number) { + var mins = Math.floor(milliseconds / 60000); + var secs = Math.floor((milliseconds % 60000) / 1000); + return ( + mins.toString().padStart(2, "0") + + ":" + + secs.toString().padStart(2, "0") + ); + } + + private pauseTime() { + this.isPaused = !this.isPaused; + if (this.isPaused) { + this.pausedTime = this.time.now - this.startTime; + } else { + this.startTime = this.time.now - this.pausedTime; + } + } + + private createPulsateEffect( + scene: Phaser.Scene, + target: Phaser.GameObjects.GameObject, + scaleFactor: number, + duration: number + ): Phaser.Tweens.Tween | null { + // Check if the item has been collected + if (this.collectedItems.includes(target as Phaser.GameObjects.Sprite)) { + return null; // Don't create the tween if the item has been collected + } + return scene.tweens.add({ + targets: target, + scaleX: `*=${scaleFactor}`, + scaleY: `*=${scaleFactor}`, + duration: duration, + yoyo: true, // Reverse back to original scale + repeat: -1, // Repeat indefinitely + }); + } + + private stopPulsateEffect() { + // Stop pulsating collected items + this.collectedItems.forEach((item) => { + const tween = this.tweens.getTweensOf(item); + if (tween.length > 1) { + tween[0].stop(); + } + }); + } + + // Make stones fall + private makeStonesFall() { + // Delay between each stone falling + const delayBetweenStones = 410; + + // Array containing references to the stones in the desired falling order + const stonesArray = [ + this.stone4, + this.stone3, + this.stone2, + this.stone1, + this.stone0, + ]; + + stonesArray.forEach((stone, index) => { + // Add a delay based on the index to create a sequence + const delay = index * delayBetweenStones; + this.time.delayedCall(delay, () => { + if (stone && stone.body && !this.playerLostLife) { + // Make stones fall down but stop if player lost life + stone.body.velocity.y = 120; + } + }); + }); + } + + // Reset stones + private resetStones() { + this.playerLostLife = true; + // Define initial positions of the stones + const initialPositions = [ + { x: 885, y: 489 }, + { x: 795, y: 473 }, + { x: 700, y: 470 }, + { x: 630, y: 475 }, + { x: 552, y: 470 }, + ]; + + // Array containing references to the stones + const stonesArray = [ + this.stone4, + this.stone3, + this.stone2, + this.stone1, + this.stone0, + ]; + + // Loop through stones and reset their positions + stonesArray.forEach((stone, index) => { + if (stone && stone.body) { + // Stop making stones fall down + stone.body.velocity.y = 0; + + // Set stone position to initial position + stone.setPosition( + initialPositions[index].x, + initialPositions[index].y + ); + + // Recreate a sensor above each stone to detect player overlap + const sensor = this.physics.add + .sprite(stone.x, stone.y + 20, "sensor") + .setAlpha(0); + sensor.body.setSize(stone.width * 0.02, 10).setOffset(-15, -40); // Adjust sensor size and offset as needed + this.physics.add.collider(sensor, this.stones); + if (this.player) { + // Make stones fall if player touches the sensors + this.physics.add.overlap(sensor, this.player, () => { + this.makeStonesFall.call(this); + }); + } + } + }); + + this.time.delayedCall(1000, () => { + this.playerLostLife = false; + }); + } + + animateFireball(fireball: Phaser.Physics.Arcade.Image) { + this.tweens.add({ + targets: fireball, + y: "-=210", + delay: 200, + duration: 1200, + yoyo: true, + repeat: -1, + ease: "Sine.InOut", + onYoyo: () => { + // Flip vertically when reaching the top or bottom + fireball.scaleY *= -1; + }, + onRepeat: () => { + fireball.scaleY *= -1; + }, + }); + } + + animateBothFireballs() { + if (this.fireball1 && this.fireball2) { + this.animateFireball(this.fireball1); + this.animateFireball(this.fireball2); + } + } + + update() { + // Updating timer + if (!this.isPaused) { + var currentTime = this.time.now; + this.elapsedTime = currentTime - this.startTime; + this.timerText.setText( + "Time: " + this.formatTime(this.elapsedTime) + ); + } + + // Key animation + if (this.key) { + this.key.anims.play("turn", true); + } + + // Skeleton walking animation + const rightBoundary = 1000; + const leftBoundary = 715; + const chaseThreshold = 300; + const attackThreshold = 70; + if (!this.usedSword && !this.isPaused) { + if (this.skeleton && this.player && !this.skeletonDead) { + // Calculate the distance between the skeleton and the player + const distanceX = Math.abs(this.player.x - this.skeleton.x); + const distanceY = Math.abs(this.player.y - this.skeleton.y); + + // If player is close-ish, move toward player + if ( + distanceX < chaseThreshold && + distanceX > attackThreshold && + distanceY < 40 + ) { + if (this.skeleton.x < this.player.x) { + this.skeleton.x += 4.3; // Move right + this.skeleton.anims.play("walk_right", true); + } else if (this.skeleton.x > this.player.x) { + this.skeleton.x -= 4.3; // Move left + this.skeleton.anims.play("walk_left", true); + } + } + // If player is close enough to hit, attack + else if (distanceX <= attackThreshold && distanceY < 100) { + if (this.skeleton.x < this.player.x) { + this.skeleton.anims.play("attack_right", true); // Attack right + } else if (this.skeleton.x > this.player.x) { + this.skeleton.anims.play("attack_left", true); // Attack left + } + if (!this.collidingWithDeath) { + this.time.delayedCall( + 500, + () => { + this.collidingWithDeath = true; + this.loseLife(); + }, + [], + this + ); + } + } + // If player is not close, just walk back and forth + else { + if ( + this.skeleton.x <= rightBoundary && + this.skeletonDirection === 1 + ) { + this.skeleton.x += 1.5; + this.skeleton.anims.play("walk_right", true); + } else if ( + this.skeleton.x >= leftBoundary && + this.skeletonDirection === -1 + ) { + this.skeleton.x -= 1.5; + this.skeleton.anims.play("walk_left", true); + } + // If the skeleton reaches the right boundary, change direction to left + else if (this.skeleton.x > rightBoundary) { + this.skeletonDirection = -1; + } + // If the skeleton reaches the left boundary, change direction to right + else if (this.skeleton.x < leftBoundary) { + this.skeletonDirection = 1; + } + } + } + } + + // Move the gal with arrow keys + if (this.player && this.cursors) { + if (!this.isColliding) { + if (this.cursors.up.isDown && this.player.body?.touching.down) { + this.player.anims.play("jump_right", true); + this.player.setVelocityY(-450); + } else if (this.cursors.right.isDown) { + this.player.setVelocityX(280); + this.player.anims.play("right", true); + this.lastDirection = "right"; // Update last direction + } else if (this.cursors.left.isDown) { + this.player.setVelocityX(-280); + this.player.anims.play("left", true); + this.lastDirection = "left"; // Update last direction + } else { + this.player.setVelocityX(0); + // Check last direction and play corresponding idle animation + if (this.lastDirection === "right") { + this.player.anims.play("idle_right", true); + } else { + this.player.anims.play("idle_left", true); + } + } + } + } + + // Collect item if 'E' key is pressed + if (this.player && this.keyE?.isDown && !this.keyEPressed) { + this.keyEPressed = true; // Set the flag for the E key being pressed to true + + // Check if the player is close enough to the water, gas mask, sword, toolbox, chainsaw, or key, and if so, collect it + if ( + this.sword && + Phaser.Math.Distance.Between( + this.player.x, + this.player.y, + this.sword.x, + this.sword.y + ) < 60 + ) { + this.collectItem(this.sword); + } + if ( + this.gasMask && + Phaser.Math.Distance.Between( + this.player.x, + this.player.y, + this.gasMask.x, + this.gasMask.y + ) < 60 + ) { + this.collectItem(this.gasMask); + } + if ( + this.toolbox && + Phaser.Math.Distance.Between( + this.player.x, + this.player.y, + this.toolbox.x, + this.toolbox.y + ) < 100 + ) { + this.collectItem(this.toolbox); + } + if ( + this.water && + Phaser.Math.Distance.Between( + this.player.x, + this.player.y, + this.water.x, + this.water.y + ) < 100 + ) { + this.collectItem(this.water); + } + if ( + this.chainsaw && + Phaser.Math.Distance.Between( + this.player.x, + this.player.y, + this.chainsaw.x, + this.chainsaw.y + ) < 100 + ) { + this.collectItem(this.chainsaw); + } + if ( + this.key && + Phaser.Math.Distance.Between( + this.player.x, + this.player.y, + this.key.x, + this.key.y + ) < 100 + ) { + this.collectItem(this.key); + } + } + + // Check if 'E' key is released + if (this.keyE?.isUp) { + this.keyEPressed = false; // Reset the keyEPressed flag when the E key is released + } + + // Check if 'F' key is released + if (this.keyF?.isUp) { + this.keyFPressed = false; // Reset the keyFPressed flag when the F key is released + } + + // Check if player is near detection area + if (this.player) { + // Gas Mask + if ( + Phaser.Geom.Intersects.RectangleToRectangle( + this.player.getBounds(), + this.gasMaskDetectionArea.getBounds() + ) && + this.gasMask && + !this.usedItems.includes(this.gasMask) + ) { + // If player overlaps with gas mask detection area, show the highlight box + this.gasMaskHighlightBox.setVisible(true); + if ( + this.keyF?.isDown && + !this.keyFPressed && + this.stack.length > 0 + ) { + // If player presses F + if (this.stack[this.stack.length - 1].name === "gas-mask") { + // If the top item is gas mask, use it + this.keyFPressed = true; + this.useItem(); + } else { + // If the top item is not gas mask, pop it and lose life + this.keyFPressed = true; + this.popWrongItem(this.gasMaskHighlightBox); + } + } + } else if (!this.flashingRed) { + this.gasMaskHighlightBox.setVisible(false); + } + + // Water + if ( + Phaser.Geom.Intersects.RectangleToRectangle( + this.player.getBounds(), + this.waterDetectionArea.getBounds() + ) && + this.water && + !this.usedItems.includes(this.water) + ) { + // If player overlaps with water detection area, show the highlight box + this.waterHighlightBox.setVisible(true); + if ( + this.keyF?.isDown && + !this.keyFPressed && + this.stack.length > 0 + ) { + // If player presses F + if (this.stack[this.stack.length - 1].name === "water") { + // If the top item is water, use it + this.keyFPressed = true; + this.useItem(); + } else { + // If the top item is not water, pop it and lose life + this.keyFPressed = true; + this.popWrongItem(this.waterHighlightBox); + } + } + } else if (!this.flashingRed) { + this.waterHighlightBox.setVisible(false); + } + + // Toolbox + if ( + Phaser.Geom.Intersects.RectangleToRectangle( + this.player.getBounds(), + this.toolboxDetectionArea.getBounds() + ) && + this.toolbox && + !this.usedItems.includes(this.toolbox) + ) { + // If player overlaps with toolbox detection area, show highlight box + this.toolboxHighlightBox.setVisible(true); + if ( + this.keyF?.isDown && + !this.keyFPressed && + this.stack.length > 0 + ) { + // If player presses F + if (this.stack[this.stack.length - 1].name === "toolbox") { + // If the top item is toolbox, use it + this.keyFPressed = true; + this.useItem(); + } else { + // If the top item is not toolbox, pop it and lose life + this.keyFPressed = true; + this.popWrongItem(this.toolboxHighlightBox); + } + } + } else if (!this.flashingRed) { + this.toolboxHighlightBox.setVisible(false); + } + + // Chainsaw + if ( + Phaser.Geom.Intersects.RectangleToRectangle( + this.player.getBounds(), + this.chainsawDetectionArea.getBounds() + ) && + this.chainsaw && + !this.usedItems.includes(this.chainsaw) + ) { + // If player overlaps with chainsaw detection area, show highlight box + this.chainsawHighlightBox.setVisible(true); + if ( + this.keyF?.isDown && + !this.keyFPressed && + this.stack.length > 0 + ) { + // If player presses F + if (this.stack[this.stack.length - 1].name === "chainsaw") { + // If the top item is chainsaw, use it + this.keyFPressed = true; + this.useItem(); + } else { + // If the top item is not chainsaw, pop it and lose life + this.keyFPressed = true; + this.popWrongItem(this.chainsawHighlightBox); + } + } + } else if (!this.flashingRed) { + this.chainsawHighlightBox.setVisible(false); + } + + // Sword + this.swordDetectionArea.setPosition( + this.skeleton?.x, + this.swordDetectionArea.y + ); + if ( + Phaser.Geom.Intersects.RectangleToRectangle( + this.player.getBounds(), + this.swordDetectionArea.getBounds() + ) && + this.sword && + !this.usedItems.includes(this.sword) + ) { + // If player overlaps with sword detection area, show the highlight box + this.swordHighlightBox.setPosition( + this.skeleton?.x, + this.swordHighlightBox.y + ); + this.swordHighlightBox.setVisible(true); + if ( + this.keyF?.isDown && + !this.keyFPressed && + this.stack.length > 0 + ) { + // If player presses F + if (this.stack[this.stack.length - 1].name === "sword") { + // If the top item is sword, use it + this.keyFPressed = true; + this.useItem(); + } else { + // If the top item is not sword, pop it and lose life + this.keyFPressed = true; + this.popWrongItem(this.swordHighlightBox); + } + } + } else if (!this.flashingRed) { + this.swordHighlightBox.setVisible(false); + } + + // Key + if ( + Phaser.Geom.Intersects.RectangleToRectangle( + this.player.getBounds(), + this.keyDetectionArea.getBounds() + ) && + this.key && + !this.usedItems.includes(this.key) + ) { + // If player overlaps with key detection area, show highlight box + this.keyHighlightBox.setVisible(true); + if ( + this.keyF?.isDown && + !this.keyFPressed && + this.stack.length > 0 && + this.skeletonDead + ) { + // If player presses F + if (this.stack[this.stack.length - 1].name === "key") { + // If the top item is key and player killed skeleton, open door + this.keyFPressed = true; + this.useItem(); + } else { + // If the top item is not key, pop it and lose life + this.keyFPressed = true; + this.popWrongItem(this.keyHighlightBox); + } + } + } else if (!this.flashingRed) { + this.keyHighlightBox.setVisible(false); + } + } + + // Lose life if player touches lava + if (this.player) { + this.physics.add.collider( + this.player, + this.lavaArea, + () => { + this.collidingWithDeath = true; + this.loseLife(); + }, + undefined, + this + ); + } + // Lose life if player touches fireballs + if (this.player && this.fireball1 && this.fireball2) { + // Fireball 1 + const distX1 = Math.abs(this.player.x - this.fireball1.x); + const distY1 = Math.abs(this.player.y - this.fireball1.y); + if (distX1 < 40 && distY1 < 90) { + this.collidingWithDeath = true; + this.loseLife(); + } + // Fireball 2 + const distX2 = Math.abs(this.player.x - this.fireball2.x); + const distY2 = Math.abs(this.player.y - this.fireball2.y); + if (distX2 < 40 && distY2 < 90) { + this.collidingWithDeath = true; + this.loseLife(); + } + } + // Lose life if player touches toxic gas + if ( + this.player && + Phaser.Geom.Intersects.RectangleToRectangle( + this.player.getBounds(), + this.toxicGasArea.getBounds() + ) + ) { + this.collidingWithDeath = true; + this.loseLife(); + } + // Lose life if player touches fire + if ( + this.player && + Phaser.Geom.Intersects.RectangleToRectangle( + this.player.getBounds(), + this.fireArea.getBounds() + ) + ) { + this.collidingWithDeath = true; + this.loseLife(); + } + // Lose life if player touches lift (while it's not repaired) + if ( + this.player && + Phaser.Geom.Intersects.RectangleToRectangle( + this.player.getBounds(), + this.liftArea.getBounds() + ) + ) { + this.collidingWithDeath = true; + this.loseLife(); + } + if ( + this.player && + Phaser.Geom.Intersects.RectangleToRectangle( + this.player.getBounds(), + this.treeArea.getBounds() + ) + ) { + this.collidingWithDeath = true; + this.loseLife(); + } + } +} diff --git a/src/scenes/levelTwo.ts b/src/scenes/levelTwo.ts new file mode 100644 index 00000000..f7038bfb --- /dev/null +++ b/src/scenes/levelTwo.ts @@ -0,0 +1,2373 @@ +import Phaser from "phaser"; + +interface GameMapData { + level0State: number; + level1State: number; + level2State: number; + level3State: number; + level0Stars: number; + level1Stars: number; + level2Stars: number; + level3Stars: number; +} + +export default class LevelTwo extends Phaser.Scene { + private player?: Phaser.Physics.Arcade.Sprite; + private cursors?: Phaser.Types.Input.Keyboard.CursorKeys; + private key?: Phaser.GameObjects.Sprite; + private clouds?: Phaser.Physics.Arcade.StaticGroup; + private invisiblePot?: Phaser.Physics.Arcade.Image; + private door?: Phaser.Physics.Arcade.Image; + private ground?: Phaser.Physics.Arcade.Image; + private wand?: Phaser.GameObjects.Sprite; + private club?: Phaser.GameObjects.Sprite; + private pot?: Phaser.GameObjects.Sprite; + private seeds?: Phaser.GameObjects.Sprite; + private wateringCan?: Phaser.GameObjects.Sprite; + private flyingBird?: Phaser.Physics.Arcade.Sprite; + private birdPlatform!: Phaser.Physics.Arcade.Group; + private bird?: Phaser.Physics.Arcade.Image; + private onBird: boolean = false; + private smogGroup?: Phaser.Physics.Arcade.StaticGroup; + private smog4?: Phaser.Physics.Arcade.Sprite; + private smog5?: Phaser.Physics.Arcade.Sprite; + private troll?: Phaser.Physics.Arcade.Sprite; + private trollDirection: number = 1; // 1 for right, -1 for left + private trollDead?: boolean = false; + private usedClub?: boolean = false; + private plant?: Phaser.Physics.Arcade.Sprite; + private bush?: Phaser.Physics.Arcade.Sprite; + + private stack: Phaser.GameObjects.Sprite[] = []; + private collectedItems: Phaser.GameObjects.Sprite[] = []; // To track all collected items (even after they're popped from stack) + private usedItems: Phaser.GameObjects.Sprite[] = []; + private keyE?: Phaser.Input.Keyboard.Key; + private keyF?: Phaser.Input.Keyboard.Key; + private keyEPressed: boolean = false; // Flag to check if 'E' was pressed to prevent picking up multiple items from one long key press + private keyFPressed: boolean = false; // Flag to check if 'E' was pressed to prevent using multiple items from one long key press + private lastDirection: string = "right"; + private climbing: boolean = false; + private clubCollected: boolean = false; + private isPushingMap: { [key: string]: boolean } = {}; // Flags for each item to make sure you can't pop it while it is being pushed + private freePopsLeft: number = 3; + private freePopsLeftText: Phaser.GameObjects.Text; + private flashingRed: boolean = false; + private usingWand: boolean = false; + + private keyDetectionArea: Phaser.GameObjects.Rectangle; + private wandDetectionArea: Phaser.GameObjects.Rectangle; + private wandHighlightArea: Phaser.GameObjects.Rectangle; + private clubDetectionArea: Phaser.GameObjects.Rectangle; + private clubHighlightArea: Phaser.GameObjects.Rectangle; + private seedsDetectionArea: Phaser.GameObjects.Rectangle; + private seedsHighlightArea: Phaser.GameObjects.Rectangle; + private potDetectionArea: Phaser.GameObjects.Rectangle; + private potHighlightArea: Phaser.GameObjects.Rectangle; + private canDetectionArea: Phaser.GameObjects.Rectangle; + private canHighlightArea: Phaser.GameObjects.Rectangle; + private keyHighlightArea: Phaser.GameObjects.Rectangle; + + private hearts?: Phaser.GameObjects.Sprite[] = []; + private lives: number = 3; + private isColliding: boolean = false; + private collidingWithSmog: boolean = false; + private poppingWrongItem: boolean = false; + + private level0State: number; + private level1State: number; + private level2State: number; + private level3State: number; + private level0Stars: number; + private level1Stars: number; + private level2Stars: number; + private level3Stars: number; + + private timerText: Phaser.GameObjects.Text; + private startTime: number; + private pausedTime = 0; + private elapsedTime: number; + private isPaused: boolean = false; + + private threeStarsPopup: Phaser.GameObjects.Group; + private twoStarsPopup: Phaser.GameObjects.Group; + private oneStarPopup: Phaser.GameObjects.Group; + private starsPopup: Phaser.GameObjects.Group; + + private backgroundMusic: Phaser.Sound.BaseSound; + private musicMuted: boolean = false; + + constructor() { + super({ key: "Level2" }); + } + + preload() { + this.load.audio("cloud-music", "assets/level2/Cloud.mp3"); + this.load.audio("collect-sound", "assets/sounds/collectsound.mp3"); + this.load.audio("dooropen-sound", "assets/sounds/dooropensound.mp3"); + this.load.audio("injure-sound", "assets/sounds/injuresound.mp3"); + this.load.audio("wrong-sound", "assets/sounds/wrongsound.mp3"); + this.load.audio("pop-sound", "assets/sounds/popsound.mp3"); + this.load.audio("death-sound", "assets/sounds/playerdiesound.mp3"); + this.load.audio("menu-sound", "assets/sounds/menusound.mp3"); + this.load.audio("win-sound", "assets/sounds/winsound.mp3"); + this.load.audio("wand-sound", "assets/level2/wandsound.mp3"); + this.load.audio("can-sound", "assets/level2/wateringsound.mp3"); + + this.load.image( + "level2-background", + "assets/level2/level2-background.png" + ); + this.load.image("stackpack", "assets/stackpack.png"); + this.load.image("wand", "assets/level2/wand.png"); + this.load.image("club", "assets/level2/club.webp"); + this.load.image("pot", "assets/level2/pot.png"); + this.load.image("seeds", "assets/level2/seeds.png"); + this.load.image("watering-can", "assets/level2/watering-can.png"); + this.load.image("smog", "assets/level2/smog.png"); + this.load.image("sign", "assets/level2/toxic-gas.png"); + this.load.image("garden-sign", "assets/level2/garden-sign.png"); + this.load.image("bush", "assets/level2/bushes_png.png"); + + this.load.image("EF-keys-black", "assets/EF-keys-black.png"); + + this.load.spritesheet("key", "assets/key.png", { + frameWidth: 768 / 24, + frameHeight: 32, + }); + + this.load.spritesheet("gal_right", "assets/Pink_Monster_Walk_6.png", { + frameWidth: 128, + frameHeight: 128, + }); + this.load.spritesheet( + "gal_left", + "assets/Pink_Monster_Walk_Left6.png", + { frameWidth: 128, frameHeight: 128 } + ); + this.load.spritesheet( + "gal_idle_right", + "assets/Pink_Monster_Idle_4.png", + { frameWidth: 128, frameHeight: 128 } + ); + this.load.spritesheet( + "gal_idle_left", + "assets/Pink_Monster_Idle_Left4.png", + { frameWidth: 128, frameHeight: 128 } + ); + this.load.spritesheet( + "gal_jump_right", + "assets/Pink_Monster_Jump_8.png", + { frameWidth: 128, frameHeight: 128 } + ); + this.load.spritesheet("gal_climb", "assets/Pink_Monster_Climb_4.png", { + frameWidth: 128, + frameHeight: 128, + }); + this.load.spritesheet( + "gal_hurt_right", + "assets/Pink_Monster_Hurt_4.png", + { frameWidth: 128, frameHeight: 128 } + ); + this.load.spritesheet( + "gal_hurt_left", + "assets/Pink_Monster_Hurt_Left4.png", + { frameWidth: 128, frameHeight: 128 } + ); + + this.load.spritesheet("bird_right", "assets/level2/bird.png", { + frameWidth: 1280 / 10, + frameHeight: 756 / 9, + }); + + this.load.spritesheet("troll", "assets/level2/troll.png", { + frameWidth: 6618 / 6, + frameHeight: 4095 / 5, + }); + + this.load.spritesheet("plant", "assets/level2/plant.png", { + frameWidth: 7488 / 18, + frameHeight: 416, + }); + + this.load.image("cloud-platform", "assets/level2/cloud-platform.png"); + this.load.image("pinkdoor", "assets/level2/pink-door.png"); + this.load.image("pinkopendoor", "assets/level2/pink-door-open.png"); + this.load.image("heart", "assets/heart_16.png"); + + this.load.image("pop-button", "assets/freePop2.png"); + + this.load.image("pause-button", "assets/pause2.png"); + this.load.image("pause-popup", "assets/paused-popup.png"); + + this.load.image("3stars", "assets/FullStars.png"); + this.load.image("2stars", "assets/2Stars.png"); + this.load.image("1star", "assets/1Star.png"); + } + + create(data: GameMapData) { + this.level0State = data.level0State; + this.level1State = data.level1State; + this.level2State = data.level2State; + this.level3State = data.level3State; + this.level0Stars = data.level0Stars; + this.level1Stars = data.level1Stars; + this.level2Stars = data.level2Stars; + this.level3Stars = data.level3Stars; + + this.resetScene(); + // Resume all animations and tweens + this.anims.resumeAll(); + this.tweens.resumeAll(); + // Make it so player can enter keyboard input + if (this.input.keyboard) { + this.input.keyboard.enabled = true; + } + + // Temporary fix for time not fully resetting bug + setTimeout(() => (this.startTime = this.time.now)); + + this.lastDirection = "right"; + + const backgroundImage = this.add + .image(0, 0, "level2-background") + .setOrigin(0, 0); + backgroundImage.setScale( + this.cameras.main.width / backgroundImage.width, + this.cameras.main.height / backgroundImage.height + ); + + this.backgroundMusic = this.sound.add("cloud-music"); + this.backgroundMusic.play({ + loop: true, + volume: 0.25, + }); + + this.freePopsLeftText = this.add + .text(285, 71, `${this.freePopsLeft}`, { + fontFamily: "Arial", + fontSize: 20, + color: "#004f28", + }) + .setDepth(4); + + const stackpack = this.add + .image(0, 0, "stackpack") + .setPosition(1170, 165); + stackpack.setScale(0.26, 0.26); + + const EFkeys = this.add.image(10, 115, "EF-keys-black").setOrigin(0, 0); + EFkeys.setScale(0.35); + + this.anims.create({ + key: "turn", + frames: this.anims.generateFrameNumbers("key", { + start: 0, + end: 25, + }), + frameRate: 8, + repeat: -1, + }); + this.player = this.physics.add + .sprite(50, 200, "gal_right") + .setScale(0.77, 0.77) + .setOrigin(0.5, 0.5); + this.player.setCollideWorldBounds(true); + + this.flyingBird = this.physics.add + .sprite(230, 330, "bird_right") + .setScale(1) + .setDepth(1) + .refreshBody(); + this.flyingBird.setCollideWorldBounds(true); + this.flyingBird + .setSize(this.flyingBird.width, this.flyingBird.height - 30) + .setOffset(0, 0); + + this.birdPlatform = this.physics.add.group({ + immovable: true, // Make immovable by collisions + allowGravity: false, // Disable gravity + }); + this.bird = this.birdPlatform.create( + 230, + 335, + "bird_right" + ) as Phaser.Physics.Arcade.Image; + this.bird.setScale(1).refreshBody(); + this.bird + .setSize(this.bird.width - 44, this.bird.height - 20) + .setOffset(22, 37); + this.bird.setVisible(false); + + this.physics.add.collider(this.player, this.birdPlatform); + + // Making bird move back and forth + this.tweens.add({ + targets: this.birdPlatform.getChildren(), + x: "+=340", + duration: 4000, + yoyo: true, + repeat: -1, + ease: "Linear", + }); + this.tweens.add({ + targets: this.flyingBird, + x: "+=340", + duration: 4000, + yoyo: true, + repeat: -1, + ease: "Linear", + onYoyo: () => { + if (this.flyingBird) { + this.flyingBird.flipX = !this.flyingBird.flipX; + } + }, + onRepeat: () => { + if (this.flyingBird) { + this.flyingBird.flipX = !this.flyingBird.flipX; + } + }, + }); + + this.troll = this.physics.add.sprite(250, 800, "troll").setScale(0.3); + this.troll.body?.setSize( + this.troll.width - 200, + this.troll.height - 300 + ); + this.troll.setCollideWorldBounds(true); + if (this.ground) { + this.physics.add.overlap(this.troll, this.ground); + } + + this.anims.create({ + key: "right", + frames: this.anims.generateFrameNumbers("gal_right", { + start: 0, + end: 5, + }), + repeat: -1, + }); + this.anims.create({ + key: "turn", + frames: [{ key: "gal_right", frame: 1 }], + }); + this.anims.create({ + key: "left", + frames: this.anims.generateFrameNumbers("gal_left", { + start: 0, + end: 5, + }), + repeat: -1, + }); + this.anims.create({ + key: "idle_right", + frames: this.anims.generateFrameNumbers("gal_idle_right", { + start: 0, + end: 3, + }), + frameRate: 10, + repeat: -1, + }); + this.anims.create({ + key: "idle_left", + frames: this.anims.generateFrameNumbers("gal_idle_left", { + start: 0, + end: 3, + }), + frameRate: 10, + repeat: -1, + }); + this.anims.create({ + key: "jump_right", + frames: this.anims.generateFrameNumbers("gal_jump_right", { + start: 0, + end: 7, + }), + }); + this.anims.create({ + key: "climb", + frames: this.anims.generateFrameNumbers("gal_climb", { + start: 0, + end: 3, + }), + frameRate: 15, + }); + this.anims.create({ + key: "hurt_right", + frames: this.anims.generateFrameNumbers("gal_hurt_right", { + start: 0, + end: 1, + }), + frameRate: 10, + repeat: 0, + }); + this.anims.create({ + key: "hurt_left", + frames: this.anims.generateFrameNumbers("gal_hurt_left", { + start: 4, + end: 2, + }), + frameRate: 10, + repeat: 0, + }); + + this.anims.create({ + key: "fly_right", + frames: this.anims.generateFrameNumbers("bird_right", { + start: 70, + end: 73, + }), + frameRate: 9, + repeat: -1, + }); + this.flyingBird.play("fly_right"); + + this.anims.create({ + key: "troll_right", + frames: this.anims.generateFrameNumbers("troll", { + start: 21, + end: 27, + }), + frameRate: 10, + repeat: -1, + }); + this.troll.play("troll_right"); + + this.anims.create({ + key: "troll_attack", + frames: this.anims.generateFrameNumbers("troll", { + start: 0, + end: 6, + }), + frameRate: 10, + repeat: -1, + }); + + this.anims.create({ + key: "troll_die", + frames: this.anims.generateFrameNumbers("troll", { + start: 7, + end: 13, + }), + frameRate: 10, + repeat: 0, + }); + + this.anims.create({ + key: "growing", + frames: this.anims.generateFrameNumbers("plant", { + start: 0, + end: 17, + }), + frameRate: 5, + repeat: 0, + }); + + this.cursors = this.input.keyboard?.createCursorKeys(); + + // Create cloud platforms + this.clouds = this.physics.add.staticGroup(); + this.ground = this.clouds.create( + 650, + 950, + "cloud-platform" + ) as Phaser.Physics.Arcade.Image; + + this.ground.setScale(5).refreshBody(); + this.ground.setAlpha(0); // Hide the ground platform + + const cloud1 = this.clouds + .create(50, 340, "cloud-platform") + .setScale(0.5); + const cloud2 = this.clouds + .create(430, 195, "cloud-platform") + .setScale(0.75); + const cloud3 = this.clouds + .create(910, 220, "cloud-platform") + .setScale(0.5); + + const birdGroundPlatform = this.physics.add.staticGroup(); + const birdGround = birdGroundPlatform + .create(520, 380, "cloud-platform") + .setScale(1.9, 0.5) + .setVisible(false) + .refreshBody(); + + // Preventing pot and plant from moving + this.invisiblePot = this.clouds + .create(1050, 660, "pot") + .setScale(0.065) as Phaser.Physics.Arcade.Image; + this.invisiblePot.setSize(115, 200).setOffset(790, 800); + this.physics.add.collider(this.player, this.invisiblePot); + this.invisiblePot.disableBody(true, true); + this.invisiblePot.setVisible(false); + + this.physics.add.collider(this.player, this.clouds); + + // Create objects: key, door, wand, club, pot, seeds, watering can + this.key = this.add.sprite(1200, 670, "key").setScale(2.5, 2.5); + this.key.setName("key"); + this.physics.add.collider(this.key, this.clouds); + + this.door = this.physics.add.image(920, 50, "pinkdoor").setScale(0.4); + this.physics.add.collider(this.door, this.clouds); + + this.wand = this.add.sprite(425, 115, "wand").setScale(0.06); + this.physics.add.collider(this.wand, this.clouds); + this.wand.setName("wand"); + + this.club = this.add.sprite(450, 415, "club").setScale(0.4); + this.physics.add.collider(this.club, this.clouds); + this.club.setName("club"); + + this.pot = this.add.sprite(80, 650, "pot").setScale(0.065); + this.physics.add.collider(this.pot, this.ground); + this.pot.setName("pot"); + + this.seeds = this.add.sprite(850, 680, "seeds").setScale(0.6); + this.seeds.setName("seeds"); + + this.add.image(1050, 670, "garden-sign").setScale(0.07, 0.06); + + this.wateringCan = this.add + .sprite(650, 655, "watering-can") + .setScale(0.75) + .setDepth(1); + this.wateringCan.setName("can"); + + // Make collectable items continuously pulsate + this.createPulsateEffect( + this, + this.wand, + 1.15, // Scale factor for pulsating effect + 1000 // Duration of each tween cycle in milliseconds + ); + this.createPulsateEffect( + this, + this.pot, + 1.15, // Scale factor for pulsating effect + 1000 // Duration of each tween cycle in milliseconds + ); + this.createPulsateEffect( + this, + this.seeds, + 1.15, // Scale factor for pulsating effect + 1000 // Duration of each tween cycle in milliseconds + ); + this.createPulsateEffect( + this, + this.wateringCan, + 1.15, // Scale factor for pulsating effect + 1000 // Duration of each tween cycle in milliseconds + ); + this.createPulsateEffect( + this, + this.club, + 1.15, // Scale factor for pulsating effect + 1000 // Duration of each tween cycle in milliseconds + ); + + // Creating smog + this.add.image(125, 435, "sign").setScale(0.2); + this.smogGroup = this.physics.add.staticGroup(); + const smog1 = this.smogGroup.create(250, 430, "smog").setScale(0.5); + const smog2 = this.smogGroup.create(400, 430, "smog").setScale(-0.5); + const smog3 = this.smogGroup.create(550, 430, "smog").setScale(0.5); + this.smog4 = this.smogGroup.create(700, 430, "smog").setScale(-0.5); + this.smog5 = this.smogGroup.create(850, 430, "smog").setScale(0.5); + + this.physics.add.collider(this.flyingBird, birdGround); + this.physics.add.collider(this.bird, birdGround); + + // Creating bush + this.bush = this.physics.add + .sprite(1200, 670, "bush") + .setScale(2, 2.8) + .setAlpha(0); + this.bush.setCollideWorldBounds(true); + this.physics.add.collider(this.bush, this.ground); + + this.bush + .setSize(this.bush.width, this.bush.height - 15) + .setOffset(0, 15); + + // Creating lives + this.createHearts(); + + // Creating Free Pop Button + const popButton = this.add.image(225, 80, "pop-button").setScale(0.31); + popButton.setInteractive(); + + const originalScale = popButton.scaleX; + const hoverScale = originalScale * 1.05; + + // Change scale on hover + popButton.on("pointerover", () => { + this.tweens.add({ + targets: popButton, + scaleX: hoverScale, + scaleY: hoverScale, + duration: 115, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + }); + + // Restore original scale when pointer leaves + popButton.on("pointerout", () => { + this.tweens.add({ + targets: popButton, + scaleX: originalScale, + scaleY: originalScale, + duration: 115, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + }); + + popButton.on("pointerup", () => { + this.sound.play("pop-sound"); + this.freePop(); + this.freePopsLeft -= 1; + this.freePopsLeftText.setText(`${this.freePopsLeft}`); + if (this.freePopsLeft <= 0) { + popButton.setScale(originalScale); + popButton.disableInteractive(); + popButton.setTint(0x696969); + } + }); + + // Creating Pause Group for Buttons and Pause Popup + const pauseGroup = this.add.group(); + + // Creating Pause Popup + const pausePopup = this.add.image(650, 350, "pause-popup"); + pausePopup.setOrigin(0.5); + pausePopup.setDepth(10); + pauseGroup.add(pausePopup); + + // Exit button for Pause popup + const exitButton = this.add.rectangle(640, 530, 200, 75).setDepth(10); + exitButton.setOrigin(0.5); + exitButton.setInteractive(); + pauseGroup.add(exitButton); + + exitButton.on("pointerover", () => { + exitButton.setFillStyle(0xffff00).setAlpha(0.5); + }); + + exitButton.on("pointerout", () => { + exitButton.setFillStyle(); + }); + + exitButton.on("pointerup", () => { + this.sound.play("menu-sound"); + this.backgroundMusic.stop(); + this.isPaused = false; + this.resetScene(); + this.scene.start("game-map", { + level0State: this.level0State, + level1State: this.level1State, + level2State: this.level2State, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + }); + + // Return button for Pause popup + const restartButton = this.add + .rectangle(640, 425, 200, 75) + .setDepth(10); + restartButton.setOrigin(0.5); + restartButton.setInteractive(); + pauseGroup.add(restartButton); + + restartButton.on("pointerover", () => { + restartButton.setFillStyle(0xffff00).setAlpha(0.5); + }); + + restartButton.on("pointerout", () => { + restartButton.setFillStyle(); + }); + + restartButton.on("pointerup", () => { + this.sound.play("menu-sound"); + this.backgroundMusic.stop(); + this.backgroundMusic.destroy(); + this.resetScene(); + this.scene.start("Level2", { + level0State: this.level0State, + level1State: this.level1State, + level2State: this.level2State, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + }); + + // Resume button for Pause popup + const resumeButton = this.add.rectangle(640, 320, 200, 75).setDepth(10); + resumeButton.setOrigin(0.5); + resumeButton.setInteractive(); + pauseGroup.add(resumeButton); + + resumeButton.on("pointerover", () => { + resumeButton.setFillStyle(0xffff00).setAlpha(0.5); + }); + + resumeButton.on("pointerout", () => { + resumeButton.setFillStyle(); + }); + + resumeButton.on("pointerup", () => { + this.sound.play("menu-sound"); + pauseGroup.setVisible(false); + this.pauseTime(); + // Resume all animations and tweens + this.anims.resumeAll(); + this.tweens.resumeAll(); + // Make it so player can enter keyboard input + if (this.input.keyboard) { + this.input.keyboard.enabled = true; + } + // Make it so player can click Free Pop button + if (this.freePopsLeft > 0) { + popButton.setInteractive(); + } + }); + + // No music button for Pause popup + const muteMusic = this.add.rectangle(585, 217, 90, 90).setDepth(10); + muteMusic.setOrigin(0.5); + muteMusic.setInteractive(); + pauseGroup.add(muteMusic); + + muteMusic.on("pointerover", () => { + muteMusic.setFillStyle(0xffff00).setAlpha(0.5); + }); + + muteMusic.on("pointerout", () => { + muteMusic.setFillStyle(); + }); + + // Has to get fixed once we have sound + muteMusic.on("pointerup", () => { + this.sound.play("menu-sound"); + this.musicMuted = !this.musicMuted; + if (this.musicMuted) { + this.backgroundMusic.pause(); + } else { + this.backgroundMusic.resume(); + } + }); + + // No sound button for Pause popup + const muteSound = this.add.rectangle(700, 217, 90, 90).setDepth(10); + muteSound.setOrigin(0.5); + muteSound.setInteractive(); + pauseGroup.add(muteSound); + + muteSound.on("pointerover", () => { + muteSound.setFillStyle(0xffff00).setAlpha(0.5); + }); + + muteSound.on("pointerout", () => { + muteSound.setFillStyle(); + }); + + // Has to get fixed once we have sound + muteSound.on("pointerup", () => { + this.sound.play("menu-sound"); + pauseGroup.setVisible(false); + }); + + pauseGroup.setVisible(false); + + // Creating Pause Button + const pauseButton = this.add + .image(30, 30, "pause-button") + .setScale(0.25); + pauseButton.setInteractive(); + + const pauseOriginalScale = pauseButton.scaleX; + const pauseHoverScale = pauseOriginalScale * 1.05; + + // Change scale on hover + pauseButton.on("pointerover", () => { + this.tweens.add({ + targets: pauseButton, + scaleX: pauseHoverScale, + scaleY: pauseHoverScale, + duration: 115, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + }); + + // Restore original scale when pointer leaves + pauseButton.on("pointerout", () => { + this.tweens.add({ + targets: pauseButton, + scaleX: pauseOriginalScale, + scaleY: pauseOriginalScale, + duration: 115, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + }); + + pauseButton.on("pointerup", () => { + this.sound.play("menu-sound"); + if (!this.isPaused) { + this.pauseTime(); + pauseGroup.setVisible(true); + // Pause all animations and tweens + this.anims.pauseAll(); + this.tweens.pauseAll(); + // Make it so player can't enter keyboard input + if (this.input.keyboard) { + this.input.keyboard.enabled = false; + } + // Make it so player can't click Free Pop button + popButton.disableInteractive(); + } + }); + + // Creating timer + this.timerText = this.add.text(60, 15, "Time: 00:00", { + fontSize: "32px", + color: "#000000", + }); + this.startTime = this.time.now; + this.pausedTime = 0; + + // Level complete popup + const completeExitButton = this.add.circle(790, 185, 35).setDepth(10); + completeExitButton.setInteractive(); + completeExitButton.on("pointerover", () => { + completeExitButton.setFillStyle(0xffff00).setAlpha(0.5); + }); + completeExitButton.on("pointerout", () => { + completeExitButton.setFillStyle(); + }); + + const completeReplayButton = this.add.circle(510, 505, 55).setDepth(10); + completeReplayButton.setInteractive(); + completeReplayButton.on("pointerover", () => { + completeReplayButton.setFillStyle(0xffff00).setAlpha(0.5); + }); + completeReplayButton.on("pointerout", () => { + completeReplayButton.setFillStyle(); + }); + + const completeMenuButton = this.add.circle(655, 530, 55).setDepth(10); + completeMenuButton.setInteractive(); + completeMenuButton.on("pointerover", () => { + completeMenuButton.setFillStyle(0xffff00).setAlpha(0.5); + }); + completeMenuButton.on("pointerout", () => { + completeMenuButton.setFillStyle(); + }); + + const completeNextButton = this.add.circle(800, 505, 55).setDepth(10); + completeNextButton.setInteractive(); + completeNextButton.on("pointerover", () => { + completeNextButton.setFillStyle(0xffff00).setAlpha(0.5); + }); + completeNextButton.on("pointerout", () => { + completeNextButton.setFillStyle(); + }); + + this.threeStarsPopup = this.add.group(); + const threeStars = this.add.image(650, 350, "3stars"); + this.threeStarsPopup.add(threeStars); + this.threeStarsPopup.add(completeExitButton); + this.threeStarsPopup.add(completeReplayButton); + this.threeStarsPopup.add(completeMenuButton); + this.threeStarsPopup.add(completeNextButton); + + this.twoStarsPopup = this.add.group(); + const twoStars = this.add.image(650, 350, "2stars"); + this.twoStarsPopup.add(twoStars); + this.twoStarsPopup.add(completeExitButton); + this.twoStarsPopup.add(completeReplayButton); + this.twoStarsPopup.add(completeMenuButton); + this.twoStarsPopup.add(completeNextButton); + + this.oneStarPopup = this.add.group(); + const oneStar = this.add.image(650, 350, "1star"); + this.oneStarPopup.add(oneStar); + this.oneStarPopup.add(completeExitButton); + this.oneStarPopup.add(completeReplayButton); + this.oneStarPopup.add(completeMenuButton); + this.oneStarPopup.add(completeNextButton); + + completeExitButton.on("pointerup", () => { + this.sound.play("menu-sound"); + this.backgroundMusic.stop(); + this.isPaused = false; + if (threeStars.visible) { + this.threeStarsPopup.setVisible(false); + } + if (twoStars.visible) { + this.twoStarsPopup.setVisible(false); + } + if (oneStar.visible) { + this.oneStarPopup.setVisible(false); + } + }); + + completeReplayButton.on("pointerup", () => { + this.sound.play("menu-sound"); + this.backgroundMusic.stop(); + this.backgroundMusic.destroy(); + this.resetScene(); + this.scene.start("Level2", { + level0State: this.level0State, + level1State: this.level1State, + level2State: this.level2State, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + }); + + completeMenuButton.on("pointerup", () => { + this.sound.play("menu-sound"); + this.backgroundMusic.stop(); + if (data.level3State == 0) { + setTimeout(() => { + this.scene.start("game-map", { + level0State: this.level0State, + level1State: this.level1State, + level2State: 3, + level3State: 1, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + }, 500); + } else { + setTimeout(() => { + this.scene.start("game-map", { + level0State: this.level0State, + level1State: this.level1State, + level2State: 3, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + }, 1000); + } + }); + + completeNextButton.on("pointerup", () => { + this.sound.play("menu-sound"); + this.backgroundMusic.stop(); + this.backgroundMusic.destroy(); + if (data.level3State == 0) { + // If level 3 was locked before, set it to current level status + this.scene.start("Level3", { + level0State: this.level0State, + level1State: this.level1State, + level2State: 3, + level3State: 2, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + } else { + this.scene.start("Level3", { + level0State: this.level0State, + level1State: this.level1State, + level2State: 3, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + } + }); + + this.threeStarsPopup.setVisible(false); + this.twoStarsPopup.setVisible(false); + this.oneStarPopup.setVisible(false); + + // Set the depth of the character/player sprite to a high value + this.player.setDepth(3); + this.bird.setDepth(2); + this.troll.setDepth(2); + + // Set the depth of other game objects to lower values + this.key.setDepth(0); + this.door.setDepth(1); + this.clouds.setDepth(0); + this.smogGroup.setDepth(0); + + // Resize collision boxes of player and everything else that can be collided with + this.player + .setSize(this.player.width - 64, this.player.height) + .setOffset(32, 0); + + cloud1.setSize(cloud1.width - 110, cloud1.height - 50).setOffset(0, 35); + cloud2 + .setSize(cloud2.width - 140, cloud2.height - 30) + .setOffset(70, 38); + cloud3 + .setSize(cloud3.width - 220, cloud3.height - 70) + .setOffset(110, 30); + + this.door + .setSize(this.door.width, this.door.height - 60) + .setOffset(0, 30); + + smog1.setSize(smog1.width - 200, smog1.height - 170).setOffset(100, 70); + smog2.setSize(smog2.width - 200, smog2.height - 170).setOffset(100, 70); + smog3.setSize(smog3.width - 200, smog3.height - 170).setOffset(100, 70); + if (this.smog4 && this.smog5) { + this.smog4 + .setSize(this.smog4.width - 200, this.smog4.height - 170) + .setOffset(100, 70); + this.smog5 + .setSize(this.smog4.width - 200, this.smog4.height - 170) + .setOffset(100, 70); + } + + // Define keys 'E' and 'F' for collecting and using items respectively + this.keyE = this.input.keyboard?.addKey( + Phaser.Input.Keyboard.KeyCodes.E + ); + this.keyF = this.input.keyboard?.addKey( + Phaser.Input.Keyboard.KeyCodes.F + ); + + // Creating detection area when using the wand + this.wandDetectionArea = this.add.rectangle(600, 300, 320, 200); + // Highlight area for wand + this.wandHighlightArea = this.add + .rectangle(780, 435, 275, 60, 0xffff00) + .setAlpha(0.4) + .setVisible(false); + + // Creating detection area when using club + //this.clubDetectionArea = this.add.rectangle(750, 700, 500, 60); + this.clubDetectionArea = this.add.rectangle(750, 500, 500, 150); + this.physics.world.enable(this.clubDetectionArea); + this.physics.add.collider(this.clubDetectionArea, this.ground); + + // Highlight area for club + this.clubHighlightArea = this.add + .rectangle(300, 700, 200, 300, 0xffff00) + .setAlpha(0.4) + .setVisible(false); + + // Detection area for pot + this.potDetectionArea = this.add.rectangle(935, 700, 100, 250); + + // Highlight area for pot + this.potHighlightArea = this.add + .rectangle(1050, 700, 100, 250, 0xffff00) + .setAlpha(0.4) + .setVisible(false); + + // Detection area for seeds + this.seedsDetectionArea = this.add.rectangle(1050, 700, 100, 250); + + // Highlight area for seeds + this.seedsHighlightArea = this.add + .rectangle(1050, 560, 100, 100, 0xffff00) + .setAlpha(0.4) + .setVisible(false); + + // Detection area for can + this.canDetectionArea = this.add.rectangle(1050, 700, 100, 250); + + // Highlight area for can + this.canHighlightArea = this.add + .rectangle(1050, 560, 100, 100, 0xffff00) + .setAlpha(0.4) + .setVisible(false); + + // Detection area for key + this.keyDetectionArea = this.add.rectangle(900, 125, 175, 200); + + // Highlight area for key + this.keyHighlightArea = this.add + .rectangle(910, 113, 170, 200, 0xffff00) + .setAlpha(0.4) + .setVisible(false); + } + + private updateStackView() { + const offsetX = 1170; // starting X position for stack items + const offsetY = 270; // starting Y position for stack items + const padding = 20; + + let currTotalHeight = 0; + + this.stack.forEach((item) => { + // Calculate and set (x, y) position of stack items in stackpack view + item.setOrigin(0.5, 0); + const stackItemX = offsetX; + const stackItemY = + offsetY - item.displayHeight - currTotalHeight - padding; + currTotalHeight += item.displayHeight + padding; + + // Animation to drop the item into its position in the stackpack + this.tweens.add({ + targets: item, + x: stackItemX, + y: stackItemY, + duration: 800, + ease: "Cubic.InOut", + onComplete: () => { + this.isPushingMap[item.name] = false; + }, + }); + }); + } + + private collectItem(item: Phaser.GameObjects.Sprite) { + this.sound.play("collect-sound"); + if (this.collectedItems.includes(item)) { + return; + } + if (this.usingWand) { + return; + } + + this.isPushingMap[item.name] = true; + + // Save the x and y scales of the collected item + const currScaleX = item.scaleX; + const currScaleY = item.scaleY; + + // Animation to make item bigger, then smaller, and then fly up to stackpack + this.tweens.add({ + targets: item, + scaleX: currScaleX * 1.5, // Scale up item size for a bit + scaleY: currScaleY * 1.5, + duration: 180, + ease: "Exponential.InOut", + onComplete: () => { + this.tweens.add({ + targets: item, + scaleX: currScaleX, // Scale down item back to normal + scaleY: currScaleY, + duration: 150, + ease: "Exponential.InOut", + onComplete: () => { + // Move item to the stackpack view + this.tweens.add({ + targets: item, + x: 1170, + y: -10, // Y position of item before it is dropped into its actual position in stackpack + scaleX: currScaleX * 0.5, // Scale down the item for stackpack view + scaleY: currScaleY * 0.5, + rotation: Math.PI * 2, // Rotate the item while moving it to stackpack + duration: 940, + ease: "Cubic.In", + onComplete: () => { + // Add the item to the stack + this.stack.push(item); + this.updateStackView(); + }, + }); + }, + }); + }, + }); + + // Add the item to the grand list of collected items + this.collectedItems.push(item); + this.stopPulsateEffect(); + + this.updateStackView(); + } + + private useItem() { + if (this.isPushingMap[this.stack[this.stack.length - 1].name]) { + return; // Prevent popping if a push is in progress + } + + // Remove the top item from the stackpack + const poppedItem = this.stack.pop(); + + if (poppedItem) { + // Add the item to the list of used items + this.usedItems.push(poppedItem); + + // Animation to fade item out from stackpack and then fade in in its new location + this.tweens.add({ + targets: poppedItem, + alpha: 0, // Fade out + duration: 200, + onComplete: () => { + // Set item origin back to default (center) + poppedItem.setOrigin(0.5, 0.5); + + // Move popped item to location it will be used + if (poppedItem.name === "wand") { + this.sound.play("wand-sound"); + // Wand waving + if (this.wand) { + this.tweens.add({ + targets: poppedItem, + duration: 600, // Adjust the duration of the tween as needed + x: "+=50", // Move the wand to the right + y: "-=50", // Move the wand upward + ease: "Sine.easeInOut", // Use a smooth, sinusoidal ease for a natural waving motion + yoyo: true, // Play the tween in reverse after completing + onStart: () => { + poppedItem.setDepth(3); + poppedItem.setPosition(750, 350); + this.usingWand = true; + }, + onComplete: () => { + if ( + this.smog4 && + this.smog5 && + this.smogGroup + ) { + this.smogGroup.remove(this.smog4); + this.smogGroup.remove(this.smog5); + this.smog4.setPosition(5000, 5000); + this.smog5.setPosition(5000, 5000); + this.smog4.setVisible(false); + this.smog5.setVisible(false); + } + poppedItem.setVisible(false); + this.usingWand = false; + }, + }); + } + this.wandHighlightArea.setVisible(false); + } + if (poppedItem.name === "pot") { + poppedItem.setPosition(1050, 665).setDepth(1); + this.invisiblePot?.enableBody(true); + this.potHighlightArea.setVisible(false); + this.plant = this.physics.add + .sprite(1045, 100, "plant") + .setScale(-0.5, 1.5) + .setVisible(false); + this.plant + .setSize( + this.plant.width * -1, + this.plant.height - 0 + ) + .setOffset(0, 30); + this.plant.setCollideWorldBounds(true); + this.plant.setImmovable(true); + this.physics.world.enable(this.plant); + if (this.pot && this.ground) { + const rect = this.add.rectangle(1050, 675, 100, 75); + this.physics.world.enable(rect); + this.physics.add.collider(rect, this.ground); + this.physics.add.collider(this.plant, rect); + } + } + if (poppedItem.name === "can") { + this.sound.play("can-sound"); + this.tweens.add({ + targets: poppedItem, + angle: 75, // Tilt the water to the side + duration: 500, // Duration of the tilt animation + yoyo: true, // Play the animation in reverse + repeat: 0, // No repeat + onStart: () => { + poppedItem.setPosition(935, 520); + }, + onComplete: () => { + poppedItem.setVisible(false); + if (this.bush && this.player) { + this.tweens.add({ + targets: this.bush, + alpha: 1, // Fade in to fully visible + duration: 3000, + ease: "Power1", + }); + } + }, + }); + this.plant?.play("growing"); + if (this.player && this.plant && this.pot) { + this.physics.add.collider(this.player, this.pot); + } + if (this.bush && this.player) { + this.physics.add.collider(this.player, this.bush); + } + this.canHighlightArea.setVisible(false); + } + if (poppedItem.name === "seeds") { + if (this.player) { + this.tweens.add({ + targets: poppedItem, + x: this.player.x + 85, + y: this.player.y - 100, + duration: 1000, + ease: "Power1", + onComplete: () => { + // Remove the seeds sprite after the throwing animation completes + poppedItem.setVisible(false); + this.tweens.add({ + targets: this.plant, + duration: 1000, + alpha: 1, // Fade in to fully visible + onStart: () => { + // Set the initial alpha to 0 before starting the fade-in + this.plant?.setVisible(true); + this.plant?.setAlpha(0); + }, + onComplete: () => { + // Set the frame of the plant sprite + this.plant?.setFrame(0); + }, + }); + }, + }); + } + this.seedsHighlightArea.setVisible(false); + } + if (poppedItem.name === "club") { + this.usedClub = true; + this.trollDead = true; + poppedItem.setDepth(5); + if (this.club && this.player && this.troll) { + // Set the club's initial position to the player's location + this.club.setPosition(this.player.x, this.player.y); + + if (this.troll.x > this.player.x) { + // If the troll is to the right of the player, rotate the club to face right + this.club.setRotation( + Phaser.Math.DegToRad(132) + ); // Facing right + } else { + // If the troll is to the left of the player, rotate the club to face left + this.club.setRotation( + Phaser.Math.DegToRad(-48) + ); // Facing left + } + + // Make the club move towards the troll and rotate down after passing it + this.tweens.add({ + targets: this.club, + x: this.troll.x + 100, + y: this.troll.y, + duration: 300, + onComplete: () => { + if (this.club) { + this.tweens.add({ + targets: this.club, + scaleX: this.club.scaleX * 0.9, + scaleY: this.club.scaleY * 0.9, + x: 20, + y: 950, + rotation: Phaser.Math.DegToRad(222), + duration: 100, + onComplete: () => { + // Play the die animation for the troll + if ( + this.troll && + this.player && + this.troll.x < this.player.x + ) { + this.troll.anims.play( + "troll_die", + true + ); + this.troll.flipX = false; + } else if ( + this.troll && + this.player && + this.troll.x > this.player.x + ) { + this.troll.anims.play( + "troll_die", + true + ); + this.troll.flipX = + !this.troll.flipX; + } + + // After the die animation completes, remove the troll from the scene + setTimeout(() => { + this.troll?.setVisible( + false + ); + this.troll?.setPosition( + -600, + -600 + ); + this.troll?.destroy(); + }, 1000); + }, + }); + } + }, + }); + } + this.clubHighlightArea.setVisible(false); + } + if (poppedItem.name === "key") { + this.sound.play("dooropen-sound"); + this.door?.setTexture("pinkopendoor"); + this.pauseTime(); + // Make the player get sucked into the door + if (this.player && this.door) { + this.tweens.add({ + targets: this.player, + scaleX: 0.27, + scaleY: 0.27, + rotation: Math.PI * 3, + x: this.door.x - 10, + y: this.door.y + 15, + duration: 800, + onComplete: () => { + this.sound.play("win-sound"); + this.player?.disableBody(true, true); + var completedTime = this.add + .text( + 640, + 345, + this.formatTime(this.elapsedTime), + { + fontSize: "40px", + color: "#000000", + } + ) + .setDepth(11) + .setVisible(false); + // Level popup depends on time it takes to complete + if (this.elapsedTime <= 60000) { + this.starsPopup = this.threeStarsPopup; + this.threeStarsPopup.add(completedTime); + this.threeStarsPopup + .setVisible(true) + .setDepth(10); + this.level2Stars = 3; + } + if ( + this.elapsedTime > 60000 && + this.elapsedTime <= 90000 + ) { + this.starsPopup = this.twoStarsPopup; + this.twoStarsPopup.add(completedTime); + this.twoStarsPopup + .setVisible(true) + .setDepth(10); + // Update stars if its better than previous time + if (this.level2Stars < 2) { + this.level2Stars = 2; + } + } + if (this.elapsedTime > 90000) { + this.starsPopup = this.oneStarPopup; + this.oneStarPopup.add(completedTime); + this.oneStarPopup + .setVisible(true) + .setDepth(10); + // Update stars if its better than previous time + if (this.level2Stars < 1) { + this.level2Stars = 1; + } + } + // Animate level complete text + this.tweens.add({ + targets: this.starsPopup, + alpha: 1, + duration: 5000, + ease: "Linear", + delay: 1000, // Delay the animation slightly + }); + + if (this.level3State == 0) { + this.level2State = 3; + this.level3State = 1; + } else { + this.level2State = 3; + } + }, + }); + } + } + this.tweens.add({ + targets: poppedItem, + scaleX: poppedItem.scaleX * 2, + scaleY: poppedItem.scaleY * 2, + alpha: 1, // Fade in + duration: 300, + onComplete: () => { + this.updateStackView(); + }, + }); + }, + }); + } + } + + // Animation for using free pop + private freePop() { + if (this.isPushingMap[this.stack[this.stack.length - 1].name]) { + return; // Prevent popping if a push is in progress + } + + // Remove the top item from the stackpack + const poppedItem = this.stack.pop(); + + if (poppedItem && this.lives !== 0) { + // Remove popped item from grand list of collected items + const index = this.collectedItems.indexOf(poppedItem); + if (index !== -1) { + this.collectedItems.splice(index, 1); + } + + // Animation to fade item out from stackpack and then fade in in its new location + this.tweens.add({ + targets: poppedItem, + alpha: 0, // Fade out + duration: 200, + onComplete: () => { + // Set item origin back to default (center) + poppedItem.setOrigin(0.5, 0.5); + + let originalScaleX = 0; + let originalScaleY = 0; + // Move popped item to its original location + if (poppedItem.name === "wand") { + poppedItem.setPosition(425, 115); + originalScaleX = 0.06; + originalScaleY = 0.06; + } + if (poppedItem.name === "club") { + this.clubCollected = false; + this.clubOnBird(); + originalScaleX = 0.4; + originalScaleY = 0.4; + } + if (poppedItem.name === "pot") { + poppedItem.setPosition(80, 650); + originalScaleX = 0.065; + originalScaleY = 0.065; + } + if (poppedItem.name === "seeds") { + poppedItem.setPosition(850, 680); + originalScaleX = 0.6; + originalScaleY = 0.6; + } + if (poppedItem.name === "can") { + poppedItem.setPosition(650, 655); + originalScaleX = 0.75; + originalScaleY = 0.75; + } + if (poppedItem.name === "key") { + poppedItem.setPosition(1200, 670); + originalScaleX = 2.5; + originalScaleY = 2.5; + } + + this.tweens.add({ + targets: poppedItem, + scaleX: originalScaleX, + scaleY: originalScaleY, + alpha: 1, // Fade in + duration: 300, + onComplete: () => { + this.updateStackView(); + this.createPulsateEffect( + this, + poppedItem, + 1.15, + 1000 + ); + }, + }); + }, + }); + } + } + + private popWrongItem(usageArea: Phaser.GameObjects.Rectangle) { + if (this.isPushingMap[this.stack[this.stack.length - 1].name]) { + return; // Prevent popping if a push is in progress + } + + this.poppingWrongItem = true; + this.loseLife(); + this.poppingWrongItem = false; + + // Remove the top item from the stackpack + const poppedItem = this.stack.pop(); + + if (poppedItem && this.lives !== 0) { + // Remove popped item from grand list of collected items + const index = this.collectedItems.indexOf(poppedItem); + if (index !== -1) { + this.collectedItems.splice(index, 1); + } + + // Animation to flash red in location player tried to use item + this.tweens.add({ + targets: usageArea, + alpha: 0, + duration: 300, + yoyo: true, + repeat: 1, + onStart: () => { + usageArea.alpha = 0.55; + usageArea.fillColor = 0xff0000; // Make area red + this.flashingRed = true; + }, + onComplete: () => { + usageArea.alpha = 0.4; // Reset area color and alpha + usageArea.fillColor = 0xffff00; + this.flashingRed = false; + }, + }); + + // Animation to fade item out from stackpack and then fade in in its new location + this.tweens.add({ + targets: poppedItem, + alpha: 0, // Fade out + duration: 200, + onComplete: () => { + // Set item origin back to default (center) + poppedItem.setOrigin(0.5, 0.5); + + let originalScaleX = 0; + let originalScaleY = 0; + // Move popped item to its original location + if (poppedItem.name === "wand") { + poppedItem.setPosition(425, 115); + originalScaleX = 0.06; + originalScaleY = 0.06; + } + if (poppedItem.name === "club") { + this.clubCollected = false; + this.clubOnBird(); + originalScaleX = 0.4; + originalScaleY = 0.4; + } + if (poppedItem.name === "pot") { + poppedItem.setPosition(80, 650); + originalScaleX = 0.065; + originalScaleY = 0.065; + } + if (poppedItem.name === "seeds") { + poppedItem.setPosition(850, 680); + originalScaleX = 0.6; + originalScaleY = 0.6; + } + if (poppedItem.name === "can") { + poppedItem.setPosition(650, 655); + originalScaleX = 0.75; + originalScaleY = 0.75; + } + if (poppedItem.name === "key") { + poppedItem.setPosition(1200, 670); + originalScaleX = 2.5; + originalScaleY = 2.5; + } + + this.tweens.add({ + targets: poppedItem, + scaleX: originalScaleX, + scaleY: originalScaleY, + alpha: 1, // Fade in + duration: 300, + onComplete: () => { + this.updateStackView(); + this.createPulsateEffect( + this, + poppedItem, + 1.15, + 1000 + ); + }, + }); + }, + }); + } + } + + private createHearts() { + this.lives = 3; + this.hearts = []; + + for (let i = 0; i < 3; i++) { + this.hearts.push( + this.add.sprite(32 + i * 50, 80, "heart").setScale(0.5) + ); + } + } + + private loseLife() { + if (!this.isColliding && this.player) { + this.isColliding = true; + if (this.poppingWrongItem) { + this.sound.play("wrong-sound"); + } else { + this.sound.play("injure-sound"); + } + + this.player.setVelocity(0, 0); + if (this.lastDirection === "right") { + this.player.anims.play("hurt_right"); + } else { + this.player.anims.play("hurt_left"); + } + this.lives--; + + // Removing hearts from free pop + const heartToRemove = this.hearts?.pop(); + if (heartToRemove) { + //heartToRemove.destroy(); + this.tweens.add({ + targets: heartToRemove, + scaleX: 0.8, + scaleY: 0.8, + duration: 200, + yoyo: true, + onComplete: () => { + heartToRemove.setTint(0x000000); // Make heart black + heartToRemove.setScale(0.5); // Reset the heart's scale + }, + }); + } + + if (this.lives === 0) { + this.playerDie(); + } + + // Reset isColliding flag + this.time.delayedCall( + 500, + () => { + this.isColliding = false; + if (this.collidingWithSmog) { + this.player?.setPosition(50, 200); // Reset player's position + this.collidingWithSmog = false; + } + }, + [], + this + ); + } + } + + private playerDie() { + this.sound.play("death-sound"); + this.player?.setTint(0xff0000); + + this.time.delayedCall(300, () => { + this.scene.launch("YouDiedScene2", { + currentLevelKey: this.scene.key, + level0State: this.level0State, + level1State: this.level1State, + level2State: this.level2State, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + //this.scene.launch("PreloadScene"); + this.player?.clearTint(); + + // Reset the stack and collected items + this.stack = []; + this.updateStackView(); + this.collectedItems = []; + this.usedItems = []; + this.lives = 3; + this.createHearts(); + this.freePopsLeft = 3; + this.clubCollected = false; + this.usedClub = false; + this.usingWand = false; + this.backgroundMusic.stop(); + this.backgroundMusic.destroy(); + this.poppingWrongItem = false; + }); + } + + private resetScene() { + // Reset the stack and collected items + this.stack = []; + this.updateStackView(); + this.collectedItems = []; + this.usedItems = []; + this.lives = 3; + this.createHearts(); + this.freePopsLeft = 3; + this.startTime = this.time.now; + this.pausedTime = 0; + this.isPaused = false; + this.clubCollected = false; + this.usedClub = false; + this.usingWand = false; + this.trollDead = false; + this.onBird = false; + this.climbing = false; + this.flashingRed = false; + this.isColliding = false; + this.collidingWithSmog = false; + this.poppingWrongItem = false; + } + + private createPulsateEffect( + scene: Phaser.Scene, + target: Phaser.GameObjects.GameObject, + scaleFactor: number, + duration: number + ): Phaser.Tweens.Tween | null { + // Check if the item has been collected + if (this.collectedItems.includes(target as Phaser.GameObjects.Sprite)) { + return null; // Don't create the tween if the item has been collected + } + return scene.tweens.add({ + targets: target, + scaleX: `*=${scaleFactor}`, + scaleY: `*=${scaleFactor}`, + duration: duration, + yoyo: true, // Reverse back to original scale + repeat: -1, // Repeat indefinitely + }); + } + + private stopPulsateEffect() { + // Stop pulsating collected items + this.collectedItems.forEach((item) => { + const tween = this.tweens.getTweensOf(item); + if (tween.length > 1) { + tween[0].stop(); + } + }); + } + + private formatTime(milliseconds: number) { + var mins = Math.floor(milliseconds / 60000); + var secs = Math.floor((milliseconds % 60000) / 1000); + return ( + mins.toString().padStart(2, "0") + + ":" + + secs.toString().padStart(2, "0") + ); + } + + private pauseTime() { + this.isPaused = !this.isPaused; + if (this.isPaused) { + this.pausedTime = this.time.now - this.startTime; + } else { + this.startTime = this.time.now - this.pausedTime; + } + } + + private clubOnBird() { + if (this.club && this.bird && !this.clubCollected) { + this.club.x = this.bird.x; + this.club.y = this.bird.y - this.bird.displayHeight / 2; + } + } + + update() { + // Updating timer + if (!this.isPaused) { + var currentTime = this.time.now; + this.elapsedTime = currentTime - this.startTime; + this.timerText.setText( + "Time: " + this.formatTime(this.elapsedTime) + ); + } + + // Key animation + if (this.key) { + this.key.anims.play("turn", true); + } + + // Move the gal with arrow keys + // Inside your update function or wherever you handle player movement + if (this.player && this.cursors) { + if (!this.isColliding) { + if ( + this.cursors.up.isDown && + this.player.body?.touching.down && + !this.climbing + ) { + this.player.anims.play("jump_right", true); + this.player.setVelocityY(-530); + } else if (this.cursors.right.isDown) { + this.player.setVelocityX(290); + this.player.anims.play("right", true); + this.lastDirection = "right"; // Update last direction + } else if (this.cursors.left.isDown) { + this.player.setVelocityX(-290); + this.player.anims.play("left", true); + this.lastDirection = "left"; // Update last direction + } else if (!this.climbing) { + this.player.setVelocityX(0); + // Check last direction and play corresponding idle animation + if (this.lastDirection === "right") { + this.player.anims.play("idle_right", true); + } else { + this.player.anims.play("idle_left", true); + } + } + } + } + + // Collect item if 'E' key is pressed + if ( + this.player && + this.keyE?.isDown && + !this.keyEPressed && + !this.usingWand + ) { + this.keyEPressed = true; // Set the flag for the E key being pressed to true + + // Check if the player is close enough to the key, wand, club, gardening stuff, and if so, collect it + if ( + this.key && + Phaser.Math.Distance.Between( + this.player.x, + this.player.y, + this.key.x, + this.key.y + ) < 30 + ) { + this.collectItem(this.key); + } + if ( + this.wand && + Phaser.Math.Distance.Between( + this.player.x, + this.player.y, + this.wand.x, + this.wand.y + ) < 100 + ) { + this.collectItem(this.wand); + } + if ( + this.club && + Phaser.Math.Distance.Between( + this.player.x, + this.player.y, + this.club.x, + this.club.y + ) < 100 + ) { + this.clubCollected = true; + this.collectItem(this.club); + } + if ( + this.seeds && + Phaser.Math.Distance.Between( + this.player.x, + this.player.y, + this.seeds.x, + this.seeds.y + ) < 100 + ) { + this.collectItem(this.seeds); + } + if ( + this.pot && + Phaser.Math.Distance.Between( + this.player.x, + this.player.y, + this.pot.x, + this.pot.y + ) < 100 + ) { + this.collectItem(this.pot); + } + if ( + this.wateringCan && + Phaser.Math.Distance.Between( + this.player.x, + this.player.y, + this.wateringCan.x, + this.wateringCan.y + ) < 100 + ) { + this.collectItem(this.wateringCan); + } + } + // Check if 'E' key is released + if (this.keyE?.isUp) { + this.keyEPressed = false; // Reset the keyEPressed flag when the E key is released + } + + // Check if 'F' key is released + if (this.keyF?.isUp) { + this.keyFPressed = false; // Reset the keyFPressed flag when the F key is released + } + + // Check if player is near detection area + if (this.player) { + // Wand + if ( + Phaser.Geom.Intersects.RectangleToRectangle( + this.player.getBounds(), + this.wandDetectionArea.getBounds() + ) && + this.wand && + !this.usedItems.includes(this.wand) + ) { + // If player overlaps with wand detection area, show the highlight box + this.wandHighlightArea.setVisible(true); + if ( + this.stack.length > 0 && + this.keyF?.isDown && + !this.keyFPressed + ) { + // If player presses F + if (this.stack[this.stack.length - 1].name === "wand") { + // If the top item is wand, use it + this.keyFPressed = true; + this.useItem(); + } else { + // If the top item is not wand, pop it and lose life + this.keyFPressed = true; + this.popWrongItem(this.wandHighlightArea); + } + } + } else if (!this.flashingRed) { + this.wandHighlightArea.setVisible(false); + } + + // Club + this.clubDetectionArea.setPosition( + this.troll?.x, + this.clubDetectionArea.y + ); + if ( + Phaser.Geom.Intersects.RectangleToRectangle( + this.player.getBounds(), + this.clubDetectionArea.getBounds() + ) && + this.club && + !this.usedItems.includes(this.club) + ) { + // If player overlaps with club detection area, show the highlight box + this.clubHighlightArea.setPosition( + this.troll?.x, + this.clubHighlightArea.y + ); + this.clubHighlightArea.setVisible(true); + if ( + this.stack.length > 0 && + this.keyF?.isDown && + !this.keyFPressed + ) { + // If player presses F + if (this.stack[this.stack.length - 1].name === "club") { + // If the top item is club, use it + this.keyFPressed = true; + this.useItem(); + } else { + // If the top item is not club, pop it and lose life + this.keyFPressed = true; + this.popWrongItem(this.clubHighlightArea); + } + } + } else if (!this.flashingRed) { + this.clubHighlightArea.setVisible(false); + } + + // Seeds + if ( + Phaser.Geom.Intersects.RectangleToRectangle( + this.player.getBounds(), + this.seedsDetectionArea.getBounds() + ) && + this.seeds && + this.pot && + !this.usedItems.includes(this.seeds) && + !this.potHighlightArea.visible + ) { + // If player overlaps with seeds detection area, show the highlight box + this.seedsHighlightArea.setVisible(true); + if ( + this.stack.length > 0 && + this.keyF?.isDown && + !this.keyFPressed + ) { + // If player presses F + if ( + this.stack[this.stack.length - 1].name === "seeds" && + this.usedItems.includes(this.pot) + ) { + // If the top item is seeds, use it + this.keyFPressed = true; + this.useItem(); + } else { + // If the top item is not seeds, pop it and lose life + this.keyFPressed = true; + this.popWrongItem(this.seedsHighlightArea); + } + } + } else if (!this.flashingRed) { + this.seedsHighlightArea.setVisible(false); + } + + // Can + if ( + Phaser.Geom.Intersects.RectangleToRectangle( + this.player.getBounds(), + this.canDetectionArea.getBounds() + ) && + this.wateringCan && + this.pot && + !this.usedItems.includes(this.wateringCan) && + !this.potHighlightArea.visible + ) { + // If player overlaps with wateringCan detection area, show the highlight box + this.canHighlightArea.setVisible(true); + if ( + this.stack.length > 0 && + this.keyF?.isDown && + !this.keyFPressed + ) { + // If player presses F + if ( + this.stack[this.stack.length - 1].name === "can" && + this.usedItems.includes(this.pot) + ) { + // If the top item is wateringCan, use it + this.keyFPressed = true; + this.useItem(); + } else { + // If the top item is not wateringCan, pop it and lose life + this.keyFPressed = true; + this.popWrongItem(this.canHighlightArea); + } + } + } else if (!this.flashingRed) { + this.canHighlightArea.setVisible(false); + } + + // Pot + if ( + Phaser.Geom.Intersects.RectangleToRectangle( + this.player.getBounds(), + this.potDetectionArea.getBounds() + ) && + this.pot && + !this.usedItems.includes(this.pot) + ) { + // If player overlaps with pot detection area, show the highlight box + this.potHighlightArea.setVisible(true); + if ( + this.stack.length > 0 && + this.keyF?.isDown && + !this.keyFPressed + ) { + // If player presses F + if (this.stack[this.stack.length - 1].name === "pot") { + // If the top item is pot, use it + this.keyFPressed = true; + this.useItem(); + } else { + // If the top item is not pot, pop it and lose life + this.keyFPressed = true; + this.popWrongItem(this.potHighlightArea); + } + } + } else if (!this.flashingRed) { + this.potHighlightArea.setVisible(false); + } + + // Key + if ( + Phaser.Geom.Intersects.RectangleToRectangle( + this.player.getBounds(), + this.keyDetectionArea.getBounds() + ) && + this.key && + !this.usedItems.includes(this.key) + ) { + // If player overlaps with key detection area, show highlight box + this.keyHighlightArea.setVisible(true); + if ( + this.stack.length > 0 && + this.keyF?.isDown && + !this.keyFPressed + ) { + // If player presses F + if (this.stack[this.stack.length - 1].name === "key") { + // If the top item is key, use it + this.keyFPressed = true; + this.useItem(); + } else { + // If the top item is not key, pop it and lose life + this.keyFPressed = true; + this.popWrongItem(this.keyHighlightArea); + } + } + } else if (!this.flashingRed) { + this.keyHighlightArea.setVisible(false); + } + } + + // Climbing the plant + if ( + this.player && + this.plant && + this.cursors && + this.plant.visible && + +this.plant.frame.name > 0 + ) { + // Max distance player can be from plant to climb it + const xTolerance = 30; // Tolerance for X position + const yTolerance = 270; // Tolerance for Y position + // Calculate horizontal and vertical distances between player and ladder + const deltaX = Math.abs(this.player.x - this.plant.x); + const deltaY = Math.abs(this.player.y - this.plant.y); + + if ( + this.plant.x === 1045 && + deltaX < xTolerance && + deltaY < yTolerance && + this.cursors.up.isDown + ) { + this.climbing = true; + this.player.anims.play("climb", true); + this.player.setVelocityY(-150); + } else { + this.climbing = false; + } + } + + // Making troll move back and forth + const leftBoundary = 250; + const rightBoundary = 525; + const chaseThreshold = 400; + const attackThreshold = 70; + const trollAttackY = 595; + if (!this.usedClub && !this.isPaused) { + if (this.troll && this.player && !this.trollDead) { + // Calculate the distance between the troll and the player + const distanceX = Math.abs(this.player.x - this.troll.x); + const distanceY = Math.abs(this.player.y - this.troll.y); + // If player is close-ish, move toward player + if ( + distanceX < chaseThreshold && + distanceX > attackThreshold && + distanceY < 40 + ) { + if (this.troll.x < this.player.x) { + this.troll.x += 4; // Move right + this.troll.flipX = false; + this.troll.anims.play("troll_right", true); + } else if (this.troll.x > this.player.x) { + this.troll.x -= 4; // Move left + this.troll.flipX = true; + this.troll.anims.play("troll_right", true); + } + } + // If player is close enough to hit, attack + else if (distanceX <= attackThreshold && distanceY < 100) { + if (this.troll.x < this.player.x) { + this.troll.flipX = false; + this.troll.y = trollAttackY; + this.troll.anims.play("troll_attack", true); // Attack right + } else if (this.troll.x > this.player.x) { + this.troll.flipX = true; + this.troll.y = trollAttackY; + this.troll.anims.play("troll_attack", true); // Attack left + } + if (!this.collidingWithSmog) { + this.time.delayedCall( + 500, + () => { + this.collidingWithSmog = true; + this.loseLife(); + }, + [], + this + ); + } + } + //If player is not close, just walk back and forth + else { + if ( + this.troll.x <= rightBoundary && + this.trollDirection === 1 + ) { + this.troll.x += 1.5; + this.troll.anims.play("troll_right", true); + } else if ( + this.troll.x >= leftBoundary && + this.trollDirection === -1 + ) { + this.troll.x -= 1.5; + this.troll.flipX = true; + this.troll.anims.play("troll_right", true); + } + // If the troll reaches the right boundary, change direction to left + else if (this.troll.x > rightBoundary) { + this.troll.flipX = true; + this.trollDirection = -1; + } + // If the troll reaches the left boundary, change direction to right + else if (this.troll.x < leftBoundary) { + this.troll.flipX = false; + this.trollDirection = 1; + } + } + } + } + + // Club on top of bird + this.clubOnBird(); + + if ( + this.player && + this.bird && + Phaser.Math.Distance.Between( + this.player.x, + this.player.y, + this.bird.x, + this.bird.y + ) < 80 + ) { + this.onBird = true; + } else { + this.onBird = false; + } + + // Making player fly on bird + if ( + this.onBird && + this.player && + this.bird && + this.player.y <= this.bird.y + ) { + this.player.x = this.bird.x; + } + + // Check if player touches smog + if (this.player && this.smogGroup) { + this.physics.add.collider( + this.player, + this.smogGroup, + () => { + this.collidingWithSmog = true; + this.loseLife(); + }, + undefined, + this + ); + } + } +} diff --git a/src/scenes/levelZero.ts b/src/scenes/levelZero.ts new file mode 100644 index 00000000..6b386e6b --- /dev/null +++ b/src/scenes/levelZero.ts @@ -0,0 +1,2028 @@ +import Phaser from "phaser"; + +interface GameMapData { + level0State: number; + level1State: number; + level2State: number; + level3State: number; + level0Stars: number; + level1Stars: number; + level2Stars: number; + level3Stars: number; +} + +export default class LevelZero extends Phaser.Scene { + private player?: Phaser.Physics.Arcade.Sprite; + private cursors?: Phaser.Types.Input.Keyboard.CursorKeys; + private key?: Phaser.GameObjects.Sprite; + private platforms?: Phaser.Physics.Arcade.StaticGroup; + private spikes?: Phaser.Physics.Arcade.StaticGroup; + private ladder?: Phaser.GameObjects.Sprite; + private plank?: Phaser.GameObjects.Sprite; + private door?: Phaser.Physics.Arcade.Image; + private ground?: Phaser.Physics.Arcade.Image; + + private pushDialogue?: Phaser.GameObjects.Image; + private popDialogue?: Phaser.GameObjects.Image; + private pushButton1?: Phaser.GameObjects.Image; + private pushButton2?: Phaser.GameObjects.Image; + private popButton1?: Phaser.GameObjects.Image; + private popButton2?: Phaser.GameObjects.Image; + private movementInstruction?: Phaser.GameObjects.Image; + private topInstruction?: Phaser.GameObjects.Image; + private orderInstruction?: Phaser.GameObjects.Image; + private hintInstruction?: Phaser.GameObjects.Image; + private freepopDialogue?: Phaser.GameObjects.Image; + private lifoInstruction?: Phaser.GameObjects.Image; + private downArrow?: Phaser.GameObjects.Image; + private arrowTween: Phaser.Tweens.Tween; + + private stack: Phaser.GameObjects.Sprite[] = []; + private collectedItems: Phaser.GameObjects.Sprite[] = []; // To track all collected items (even after they're popped from stack) + private usedItems: Phaser.GameObjects.Sprite[] = []; + private keyE?: Phaser.Input.Keyboard.Key; + private keyF?: Phaser.Input.Keyboard.Key; + private keyEPressed: boolean = false; // Flag to check if 'E' was pressed to prevent picking up multiple items from one long key press + private keyFPressed: boolean = false; // Flag to check if 'E' was pressed to prevent using multiple items from one long key press + private lastDirection: string = "right"; + private climbing: boolean = false; + private isPushingMap: { [key: string]: boolean } = {}; // Flags for each item to make sure you can't pop it while it is being pushed + private flashingRed: boolean = false; + private freePopsLeft: number = 2; + private freePopsLeftText: Phaser.GameObjects.Text; + + private ladderDetectionArea: Phaser.GameObjects.Rectangle; + private ladderHighlightBox: Phaser.GameObjects.Rectangle; + private plankDetectionArea1: Phaser.GameObjects.Rectangle; + private plankDetectionArea2: Phaser.GameObjects.Rectangle; + private plankDetectionAreasGroup: Phaser.GameObjects.Container; + private plankHighlightBox: Phaser.GameObjects.Rectangle; + private plankPlatforms?: Phaser.Physics.Arcade.StaticGroup; + private plankPlatform?: Phaser.Physics.Arcade.Image; + private keyDetectionArea: Phaser.GameObjects.Rectangle; + private keyHighlightBox: Phaser.GameObjects.Rectangle; + + private hearts?: Phaser.GameObjects.Sprite[] = []; + private lives: number = 3; + private isColliding: boolean = false; + private collidingWithSpikes: boolean = false; + private poppingWrongItem: boolean = false; + + private level0State: number; + private level1State: number; + private level2State: number; + private level3State: number; + private level0Stars: number; + private level1Stars: number; + private level2Stars: number; + private level3Stars: number; + + private timerText: Phaser.GameObjects.Text; + private startTime: number; + private pausedTime = 0; + private elapsedTime: number; + private isPaused: boolean = false; + + private threeStarsPopup: Phaser.GameObjects.Group; + private twoStarsPopup: Phaser.GameObjects.Group; + private oneStarPopup: Phaser.GameObjects.Group; + private starsPopup: Phaser.GameObjects.Group; + + private backgroundMusic: Phaser.Sound.BaseSound; + private musicMuted: boolean = false; + private soundMuted: boolean = false; + private collectSound: Phaser.Sound.BaseSound; + private plankSound: Phaser.Sound.BaseSound; + private ladderSound: Phaser.Sound.BaseSound; + private climbingLadderSound: Phaser.Sound.BaseSound; + private doorOpenSound: Phaser.Sound.BaseSound; + private injureSound: Phaser.Sound.BaseSound; + private popSound: Phaser.Sound.BaseSound; + private deathSound: Phaser.Sound.BaseSound; + private menuSound: Phaser.Sound.BaseSound; + private winSound: Phaser.Sound.BaseSound; + + constructor() { + super({ key: "Level0" }); + } + + preload() { + this.load.audio("tutorial-music", "assets/level0/tutorialmusic.mp3"); + this.load.audio("collect-sound", "assets/sounds/collectsound.mp3"); + this.load.audio("plank-sound", "assets/level0/planksound.mp3"); + this.load.audio("ladder-sound", "assets/level0/laddersound.mp3"); + this.load.audio( + "climbingladder-sound", + "assets/level0/climbingladder.mp3" + ); + this.load.audio("dooropen-sound", "assets/sounds/dooropensound.mp3"); + this.load.audio("injure-sound", "assets/sounds/injuresound.mp3"); + this.load.audio("wrong-sound", "assets/sounds/wrongsound.mp3"); + this.load.audio("pop-sound", "assets/sounds/popsound.mp3"); + this.load.audio("death-sound", "assets/sounds/playerdiesound.mp3"); + this.load.audio("menu-sound", "assets/sounds/menusound.mp3"); + this.load.audio("win-sound", "assets/sounds/winsound.mp3"); + + this.load.image( + "level0-background", + "assets/level0/level0-background.jpg" + ); + this.load.image("stackpack", "assets/stackpack.png"); + + this.load.image("EF-keys-black", "assets/EF-keys-black.png"); + + this.load.image("downArrow", "assets/level0/down-arrow.png"); + + this.load.spritesheet("key", "assets/key.png", { + frameWidth: 768 / 24, + frameHeight: 32, + }); + + this.load.spritesheet("gal_right", "assets/Pink_Monster_Walk_6.png", { + frameWidth: 128, + frameHeight: 128, + }); + this.load.spritesheet( + "gal_left", + "assets/Pink_Monster_Walk_Left6.png", + { frameWidth: 128, frameHeight: 128 } + ); + this.load.spritesheet( + "gal_idle_right", + "assets/Pink_Monster_Idle_4.png", + { frameWidth: 128, frameHeight: 128 } + ); + this.load.spritesheet( + "gal_idle_left", + "assets/Pink_Monster_Idle_Left4.png", + { frameWidth: 128, frameHeight: 128 } + ); + this.load.spritesheet( + "gal_jump_right", + "assets/Pink_Monster_Jump_8.png", + { frameWidth: 128, frameHeight: 128 } + ); + this.load.spritesheet("gal_climb", "assets/Pink_Monster_Climb_4.png", { + frameWidth: 128, + frameHeight: 128, + }); + this.load.spritesheet( + "gal_hurt_right", + "assets/Pink_Monster_Hurt_4.png", + { frameWidth: 128, frameHeight: 128 } + ); + this.load.spritesheet( + "gal_hurt_left", + "assets/Pink_Monster_Hurt_Left4.png", + { frameWidth: 128, frameHeight: 128 } + ); + + this.load.image("level0-platform", "assets/level0/platform.png"); + this.load.image( + "spike", + "assets/level0/spikes2/keyframes/long_metal_spike.png" + ); + this.load.image("ladder", "assets/level0/ladder.png"); + this.load.image("plank", "assets/level0/plank.png"); + this.load.image("door", "assets/level0/door.png"); + this.load.image("opendoor", "assets/level0/open-door.png"); + this.load.image("heart", "assets/heart_16.png"); + + this.load.image("EButton", "assets/level0/EButton.png"); + this.load.image("FButton", "assets/level0/FButton.png"); + this.load.image("EtoPush", "assets/level0/EtoPush.png"); + this.load.image("FtoPop", "assets/level0/FtoPop.png"); + + this.load.image( + "MovementInstructions", + "assets/level0/Movement-Instructions.png" + ); + this.load.image( + "TopInstructions", + "assets/level0/Top-Instructions.png" + ); + this.load.image( + "OrderInstructions", + "assets/level0/Order-Instructions.png" + ); + this.load.image( + "HintInstructions", + "assets/level0/Hint-Instructions.png" + ); + this.load.image( + "FreePopInstructions", + "assets/level0/FreePop-Instructions.png" + ); + this.load.image( + "LIFOInstructions", + "assets/level0/LIFO-Instructions.png" + ); + + this.load.image("pop-button", "assets/freePop2.png"); + + this.load.image("pause-button", "assets/pause2.png"); + this.load.image("pause-popup", "assets/paused-popup.png"); + + this.load.image("3stars", "assets/FullStars.png"); + this.load.image("2stars", "assets/2Stars.png"); + this.load.image("1star", "assets/1Star.png"); + } + + create(data: GameMapData) { + this.resetScene(); + // Resume all animations and tweens + this.anims.resumeAll(); + this.tweens.resumeAll(); + // Make it so player can enter keyboard input + if (this.input.keyboard) { + this.input.keyboard.enabled = true; + } + + // Temporary fix for time not fully resetting bug + setTimeout(() => (this.startTime = this.time.now)); + + this.level0State = data.level0State; + this.level1State = data.level1State; + this.level2State = data.level2State; + this.level3State = data.level3State; + this.level0Stars = data.level0Stars; + this.level1Stars = data.level1Stars; + this.level2Stars = data.level2Stars; + this.level3Stars = data.level3Stars; + + this.lastDirection = "right"; + + this.freePopsLeftText = this.add + .text(285, 71, `${this.freePopsLeft}`, { + fontFamily: "Arial", + fontSize: 20, + color: "#004f28", + }) + .setDepth(4); + + const backgroundImage = this.add + .image(0, 0, "level0-background") + .setOrigin(0, 0); + backgroundImage.setScale( + this.cameras.main.width / backgroundImage.width, + this.cameras.main.height / backgroundImage.height + ); + + this.backgroundMusic = this.sound.add("tutorial-music"); + this.backgroundMusic.play({ + loop: true, + volume: 0.25, + }); + this.collectSound = this.sound.add("collect-sound"); + this.plankSound = this.sound.add("plank-sound"); + this.ladderSound = this.sound.add("ladder-sound"); + this.climbingLadderSound = this.sound.add("climbingladder-sound"); + this.doorOpenSound = this.sound.add("dooropen-sound"); + this.injureSound = this.sound.add("injure-sound"); + this.popSound = this.sound.add("pop-sound"); + this.deathSound = this.sound.add("death-sound"); + this.menuSound = this.sound.add("menu-sound"); + this.winSound = this.sound.add("win-sound"); + + const stackpack = this.add + .image(0, 0, "stackpack") + .setPosition(1170, 165); + stackpack.setScale(0.26, 0.26); + + const EFkeys = this.add.image(10, 115, "EF-keys-black").setOrigin(0, 0); + EFkeys.setScale(0.35); + + this.downArrow = this.add.image(350, 350, "downArrow"); + this.downArrow.setScale(0.5); + + this.anims.create({ + key: "turn", + frames: this.anims.generateFrameNumbers("key", { + start: 0, + end: 25, + }), + frameRate: 8, + repeat: -1, + }); + this.player = this.physics.add + .sprite(100, 450, "gal_right") + .setScale(0.77, 0.77) + .setOrigin(0.5, 0.5); + this.player.setCollideWorldBounds(true); + + this.anims.create({ + key: "right", + frames: this.anims.generateFrameNumbers("gal_right", { + start: 0, + end: 5, + }), + repeat: -1, + }); + this.anims.create({ + key: "turn", + frames: [{ key: "gal_right", frame: 1 }], + }); + this.anims.create({ + key: "left", + frames: this.anims.generateFrameNumbers("gal_left", { + start: 0, + end: 5, + }), + repeat: -1, + }); + this.anims.create({ + key: "idle_right", + frames: this.anims.generateFrameNumbers("gal_idle_right", { + start: 0, + end: 3, + }), + frameRate: 10, + repeat: -1, + }); + this.anims.create({ + key: "idle_left", + frames: this.anims.generateFrameNumbers("gal_idle_left", { + start: 0, + end: 3, + }), + frameRate: 10, + repeat: -1, + }); + this.anims.create({ + key: "jump_right", + frames: this.anims.generateFrameNumbers("gal_jump_right", { + start: 0, + end: 7, + }), + }); + this.anims.create({ + key: "climb", + frames: this.anims.generateFrameNumbers("gal_climb", { + start: 0, + end: 3, + }), + frameRate: 15, + }); + this.anims.create({ + key: "hurt_right", + frames: this.anims.generateFrameNumbers("gal_hurt_right", { + start: 0, + end: 1, + }), + frameRate: 10, + repeat: 0, + }); + this.anims.create({ + key: "hurt_left", + frames: this.anims.generateFrameNumbers("gal_hurt_left", { + start: 4, + end: 2, + }), + frameRate: 10, + repeat: 0, + }); + + this.cursors = this.input.keyboard?.createCursorKeys(); + + // Create platforms + this.platforms = this.physics.add.staticGroup(); + this.ground = this.platforms.create( + 650, + 790, + "level0-platform" + ) as Phaser.Physics.Arcade.Image; + + this.ground.setScale(5).refreshBody(); + this.ground.setAlpha(0); // Hide the ground platform + + const platform1 = this.platforms + .create(350, 585, "level0-platform") + .setScale(1, 1); + const platform2 = this.platforms + .create(650, 500, "level0-platform") + .setScale(0.75, 0.75); + const platform3 = this.platforms + .create(875, 300, "level0-platform") + .setScale(1, 0.75); + + this.physics.add.collider(this.player, this.platforms); + + // Create objects: key, ladder, plank, spikes, door + this.key = this.add.sprite(1200, 650, "key").setScale(2.5, 2.5); + this.key.setName("key"); + this.physics.add.collider(this.key, this.platforms); + + this.ladder = this.add.sprite(1050, 550, "ladder").setScale(0.5, 0.55); + this.ladder.setName("ladder"); + + this.plank = this.add.sprite(350, 530, "plank").setScale(0.5, 0.5); + this.plank.setName("plank"); + + this.plankPlatforms = this.physics.add.staticGroup(); + this.plankPlatform = this.plankPlatforms.create( + 815, + 600, + "plank" + ) as Phaser.Physics.Arcade.Image; + + this.plankPlatform + .setSize( + this.plankPlatform.width - 246, + this.plankPlatform.height - 60 + ) + .setOffset(123, 55); + + this.physics.add.collider(this.player, this.plankPlatform); + + this.plankPlatform.disableBody(true, true); + this.plankPlatform.setVisible(false); + + this.spikes = this.physics.add.staticGroup(); + const spike1 = this.spikes + .create(740, 675, "spike") + .setScale(0.75, 0.75); + const spike2 = this.spikes + .create(790, 675, "spike") + .setScale(0.75, 0.75); + const spike3 = this.spikes + .create(840, 675, "spike") + .setScale(0.75, 0.75); + const spike4 = this.spikes + .create(890, 675, "spike") + .setScale(0.75, 0.75); + + this.door = this.physics.add.image(887, 150, "door").setScale(0.1, 0.1); + this.physics.add.collider(this.door, this.platforms); + + // Creating lives + this.createHearts(); + + // Creating Free Pop Button + const popButton = this.add.image(225, 80, "pop-button").setScale(0.31); + popButton.setInteractive(); + + const originalScale = popButton.scaleX; + const hoverScale = originalScale * 1.05; + + // Change scale on hover + popButton.on("pointerover", () => { + this.tweens.add({ + targets: popButton, + scaleX: hoverScale, + scaleY: hoverScale, + duration: 115, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + }); + + // Restore original scale when pointer leaves + popButton.on("pointerout", () => { + this.tweens.add({ + targets: popButton, + scaleX: originalScale, + scaleY: originalScale, + duration: 115, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + }); + + popButton.on("pointerup", () => { + this.popSound.play(); + this.freePop(); + this.freePopsLeft -= 1; + this.freePopsLeftText.setText(`${this.freePopsLeft}`); + if (this.freePopsLeft <= 0) { + popButton.setScale(originalScale); + popButton.disableInteractive(); + popButton.setTint(0x696969); + } + }); + + // Creating Pause Group for Buttons and Pause Popup + const pauseGroup = this.add.group(); + + // Creating Pause Popup + const pausePopup = this.add.image(650, 350, "pause-popup"); + pausePopup.setOrigin(0.5); + pausePopup.setDepth(10); + pauseGroup.add(pausePopup); + + // Exit button for Pause popup + const exitButton = this.add.rectangle(640, 530, 200, 75).setDepth(10); + exitButton.setOrigin(0.5); + exitButton.setInteractive(); + pauseGroup.add(exitButton); + + exitButton.on("pointerover", () => { + exitButton.setFillStyle(0xffff00).setAlpha(0.5); + }); + + exitButton.on("pointerout", () => { + exitButton.setFillStyle(); + }); + + exitButton.on("pointerup", () => { + this.menuSound.play(); + this.backgroundMusic.stop(); + this.isPaused = false; + this.resetScene(); + this.scene.start("game-map", { + level0State: this.level0State, + level1State: this.level1State, + level2State: this.level2State, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + }); + + // Return button for Pause popup + const restartButton = this.add + .rectangle(640, 425, 200, 75) + .setDepth(10); + restartButton.setOrigin(0.5); + restartButton.setInteractive(); + pauseGroup.add(restartButton); + + restartButton.on("pointerover", () => { + restartButton.setFillStyle(0xffff00).setAlpha(0.5); + }); + + restartButton.on("pointerout", () => { + restartButton.setFillStyle(); + }); + + restartButton.on("pointerup", () => { + this.menuSound.play(); + this.backgroundMusic.stop(); + this.backgroundMusic.destroy(); + this.resetScene(); + this.scene.start("Level0", { + level0State: this.level0State, + level1State: this.level1State, + level2State: this.level2State, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + }); + + // Resume button for Pause popup + const resumeButton = this.add.rectangle(640, 320, 200, 75).setDepth(10); + resumeButton.setOrigin(0.5); + resumeButton.setInteractive(); + pauseGroup.add(resumeButton); + + resumeButton.on("pointerover", () => { + resumeButton.setFillStyle(0xffff00).setAlpha(0.5); + }); + + resumeButton.on("pointerout", () => { + resumeButton.setFillStyle(); + }); + + resumeButton.on("pointerup", () => { + this.menuSound.play(); + pauseGroup.setVisible(false); + this.pauseTime(); + // Resume all animations and tweens + this.anims.resumeAll(); + this.tweens.resumeAll(); + // Make it so player can enter keyboard input + if (this.input.keyboard) { + this.input.keyboard.enabled = true; + } + // Make it so player can click Free Pop button + if (this.freePopsLeft > 0) { + popButton.setInteractive(); + } + }); + + // No music button for Pause popup + const muteMusic = this.add.rectangle(585, 217, 90, 90).setDepth(10); + muteMusic.setOrigin(0.5); + muteMusic.setInteractive(); + pauseGroup.add(muteMusic); + + muteMusic.on("pointerover", () => { + muteMusic.setFillStyle(0xffff00).setAlpha(0.5); + }); + + muteMusic.on("pointerout", () => { + muteMusic.setFillStyle(); + }); + + // Has to get fixed once we have sound + muteMusic.on("pointerup", () => { + this.menuSound.play(); + this.musicMuted = !this.musicMuted; + if (this.musicMuted) { + this.backgroundMusic.pause(); + } else { + this.backgroundMusic.resume(); + } + }); + + // No sound button for Pause popup + const muteSound = this.add.rectangle(700, 217, 90, 90).setDepth(10); + muteSound.setOrigin(0.5); + muteSound.setInteractive(); + pauseGroup.add(muteSound); + + muteSound.on("pointerover", () => { + muteSound.setFillStyle(0xffff00).setAlpha(0.5); + }); + + muteSound.on("pointerout", () => { + muteSound.setFillStyle(); + }); + + // Has to get fixed once we have sound + muteSound.on("pointerup", () => { + this.menuSound.play(); + /* + this.soundMuted = !this.soundMuted; + if (this.soundMuted) { + this.sound.pauseAll(); + //this.backgroundMusic.resume(); + } else { + this.sound.resumeAll(); + } + */ + }); + + pauseGroup.setVisible(false); + + // Creating Pause Button + const pauseButton = this.add + .image(30, 30, "pause-button") + .setScale(0.25); + pauseButton.setInteractive(); + + const pauseOriginalScale = pauseButton.scaleX; + const pauseHoverScale = pauseOriginalScale * 1.05; + + // Change scale on hover + pauseButton.on("pointerover", () => { + this.tweens.add({ + targets: pauseButton, + scaleX: pauseHoverScale, + scaleY: pauseHoverScale, + duration: 115, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + }); + + // Restore original scale when pointer leaves + pauseButton.on("pointerout", () => { + this.tweens.add({ + targets: pauseButton, + scaleX: pauseOriginalScale, + scaleY: pauseOriginalScale, + duration: 115, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + }); + + pauseButton.on("pointerup", () => { + this.menuSound.play(); + if (!this.isPaused) { + this.pauseTime(); + pauseGroup.setVisible(true); + // Pause all animations and tweens + this.anims.pauseAll(); + this.tweens.pauseAll(); + // Make it so player can't enter keyboard input + if (this.input.keyboard) { + this.input.keyboard.enabled = false; + } + // Make it so player can't click Free Pop button + popButton.disableInteractive(); + } + }); + + // Creating timer + this.timerText = this.add.text(60, 15, "Time: 00:00", { + fontSize: "32px", + color: "#000000", + }); + + // Level complete popup - still working + const completeExitButton = this.add.circle(790, 185, 35).setDepth(10); + completeExitButton.setInteractive(); + completeExitButton.on("pointerover", () => { + completeExitButton.setFillStyle(0xffff00).setAlpha(0.5); + }); + completeExitButton.on("pointerout", () => { + completeExitButton.setFillStyle(); + }); + + const completeReplayButton = this.add.circle(510, 505, 55).setDepth(10); + completeReplayButton.setInteractive(); + completeReplayButton.on("pointerover", () => { + completeReplayButton.setFillStyle(0xffff00).setAlpha(0.5); + }); + completeReplayButton.on("pointerout", () => { + completeReplayButton.setFillStyle(); + }); + + const completeMenuButton = this.add.circle(655, 530, 55).setDepth(10); + completeMenuButton.setInteractive(); + completeMenuButton.on("pointerover", () => { + completeMenuButton.setFillStyle(0xffff00).setAlpha(0.5); + }); + completeMenuButton.on("pointerout", () => { + completeMenuButton.setFillStyle(); + }); + + const completeNextButton = this.add.circle(800, 505, 55).setDepth(10); + completeNextButton.setInteractive(); + completeNextButton.on("pointerover", () => { + completeNextButton.setFillStyle(0xffff00).setAlpha(0.5); + }); + completeNextButton.on("pointerout", () => { + completeNextButton.setFillStyle(); + }); + + this.threeStarsPopup = this.add.group(); + const threeStars = this.add.image(650, 350, "3stars"); + this.threeStarsPopup.add(threeStars); + this.threeStarsPopup.add(completeExitButton); + this.threeStarsPopup.add(completeReplayButton); + this.threeStarsPopup.add(completeMenuButton); + this.threeStarsPopup.add(completeNextButton); + + this.twoStarsPopup = this.add.group(); + const twoStars = this.add.image(650, 350, "2stars"); + this.twoStarsPopup.add(twoStars); + this.twoStarsPopup.add(completeExitButton); + this.twoStarsPopup.add(completeReplayButton); + this.twoStarsPopup.add(completeMenuButton); + this.twoStarsPopup.add(completeNextButton); + + this.oneStarPopup = this.add.group(); + const oneStar = this.add.image(650, 350, "1star"); + this.oneStarPopup.add(oneStar); + this.oneStarPopup.add(completeExitButton); + this.oneStarPopup.add(completeReplayButton); + this.oneStarPopup.add(completeMenuButton); + this.oneStarPopup.add(completeNextButton); + + completeExitButton.on("pointerup", () => { + this.menuSound.play(); + this.backgroundMusic.stop(); + this.isPaused = false; + if (threeStars.visible) { + this.threeStarsPopup.setVisible(false); + } + if (twoStars.visible) { + this.twoStarsPopup.setVisible(false); + } + if (oneStar.visible) { + this.oneStarPopup.setVisible(false); + } + }); + + completeReplayButton.on("pointerup", () => { + this.menuSound.play(); + this.backgroundMusic.stop(); + this.backgroundMusic.destroy(); + this.resetScene(); + this.scene.start("Level0", { + level0State: this.level0State, + level1State: this.level1State, + level2State: this.level2State, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + }); + + completeMenuButton.on("pointerup", () => { + this.menuSound.play(); + this.backgroundMusic.stop(); + console.log(this.level0Stars); + if (data.level1State == 0) { + setTimeout(() => { + this.scene.start("game-map", { + level0State: 3, + level1State: 1, + level2State: this.level2State, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + }, 500); + } else { + setTimeout(() => { + this.scene.start("game-map", { + level0State: 3, + level1State: this.level1State, + level2State: this.level2State, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + }, 1000); + } + }); + + completeNextButton.on("pointerup", () => { + this.menuSound.play(); + this.backgroundMusic.stop(); + this.backgroundMusic.destroy(); + if (data.level1State == 0) { + // If level 1 was locked before, set it to current level status + this.scene.start("Level1", { + level0State: 3, + level1State: 2, + level2State: this.level2State, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + } else { + this.scene.start("Level1", { + level0State: 3, + level1State: this.level1State, + level2State: this.level2State, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + } + }); + + this.threeStarsPopup.setVisible(false); + this.twoStarsPopup.setVisible(false); + this.oneStarPopup.setVisible(false); + + // Set the depth of the character/player sprite to a high value + this.player.setDepth(3); + this.downArrow.setDepth(1); + + // Set the depth of other game objects to lower values + this.key.setDepth(0); + this.ladder.setDepth(0); + this.plank.setDepth(0); + this.spikes.setDepth(0); + this.door.setDepth(1); + + // Resize collision boxes of player and everything else that can be collided with + this.player + .setSize(this.player.width - 64, this.player.height) + .setOffset(32, 0); + + platform1 + .setSize(platform1.width - 12, platform1.height - 28) + .setOffset(8, 5); + platform2 + .setSize(platform2.width - 80, platform2.height - 35) + .setOffset(40, 8); + platform3 + .setSize(platform3.width - 10, platform3.height - 30) + .setOffset(7, 7); + + this.door + .setSize(this.door.width, this.door.height - 60) + .setOffset(0, 0); + + spike1.setSize(spike1.width - 30, spike1.height - 30).setOffset(15, 14); + spike2.setSize(spike2.width - 30, spike2.height - 30).setOffset(15, 14); + spike3.setSize(spike3.width - 30, spike3.height - 30).setOffset(15, 14); + spike4.setSize(spike4.width - 30, spike4.height - 30).setOffset(15, 14); + + // Define keys 'E' and 'F' for collecting and using items respectively + this.keyE = this.input.keyboard?.addKey( + Phaser.Input.Keyboard.KeyCodes.E + ); + this.keyF = this.input.keyboard?.addKey( + Phaser.Input.Keyboard.KeyCodes.F + ); + + // Creating detection areas when using the ladder + this.ladderDetectionArea = this.add.rectangle(680, 400, 100, 150); + this.physics.world.enable(this.ladderDetectionArea); + this.physics.add.collider(this.ladderDetectionArea, this.ground); + this.physics.add.collider(this.ladderDetectionArea, this.platforms); + + // Creating a highlighted rectangle to indicate where ladder can be used + this.ladderHighlightBox = this.add.rectangle( + 680, + 400, + 100, + 150, + 0xffff00 + ); + this.ladderHighlightBox.setAlpha(0.25); + this.ladderHighlightBox.setVisible(false); + + // Creating detection areas when using the plank + this.plankDetectionArea1 = this.add.rectangle(670, 0, 100, 150); + this.physics.world.enable(this.plankDetectionArea1); + this.physics.add.collider(this.plankDetectionArea1, this.ground); + + this.plankDetectionArea2 = this.add.rectangle(920, 0, 100, 150); + this.physics.world.enable(this.plankDetectionArea2); + this.physics.add.collider(this.plankDetectionArea2, this.ground); + + this.plankDetectionAreasGroup = this.add.container(); + this.plankDetectionAreasGroup.add(this.plankDetectionArea1); + this.plankDetectionAreasGroup.add(this.plankDetectionArea2); + + // Creating a highlighted rectangle to indicate where plank can be used + this.plankHighlightBox = this.add.rectangle( + 815, + 210, + 215, + 50, + 0xffff00 + ); + this.physics.world.enable(this.plankHighlightBox); + this.physics.add.collider(this.plankHighlightBox, this.ground); + this.physics.add.collider(this.plankHighlightBox, this.spikes); + this.plankHighlightBox.setAlpha(0.25); + this.plankHighlightBox.setVisible(false); + + // Creating detection area when using key + this.keyDetectionArea = this.add.rectangle(875, 150, 100, 200); + this.physics.world.enable(this.keyDetectionArea); + this.physics.add.collider(this.keyDetectionArea, this.platforms); + + this.keyHighlightBox = this.add.rectangle(877, 195, 170, 200, 0xffff00); + this.keyHighlightBox.setAlpha(0.25); + this.keyHighlightBox.setVisible(false); + + // Text Boxes + this.pushDialogue = this.add + .image(350, 420, "EtoPush") + .setScale(0.65) + .setDepth(2); + this.popDialogue = this.add + .image(750, 510, "FtoPop") + .setScale(0.65) + .setDepth(2); + this.pushButton1 = this.add.image(1100, 650, "EButton").setScale(1, 1); + this.pushButton2 = this.add.image(1250, 730, "EButton").setScale(1, 1); + this.popButton1 = this.add.image(750, 500, "FButton").setScale(1, 1); + this.popButton2 = this.add + .image(900, 300, "FButton") + .setScale(1, 1) + .setDepth(2); + this.movementInstruction = this.add + .image(200, 600, "MovementInstructions") + .setScale(1, 1) + .setDepth(2); + this.topInstruction = this.add + .image(1090, 380, "TopInstructions") + .setScale(0.65) + .setDepth(2); + this.topInstruction.setVisible(false); + this.orderInstruction = this.add + .image(1080, 390, "OrderInstructions") + .setScale(0.65) + .setDepth(2); + this.orderInstruction.setVisible(false); + this.hintInstruction = this.add + .image(1090, 380, "HintInstructions") + .setScale(0.65) + .setDepth(2); + this.hintInstruction.setVisible(false); + this.freepopDialogue = this.add + .image(235, 170, "FreePopInstructions") + .setScale(0.58); + this.freepopDialogue.setVisible(false); + this.lifoInstruction = this.add + .image(550, 240, "LIFOInstructions") + .setScale(0.59); + this.lifoInstruction.setVisible(false); + + this.pushDialogue.setVisible(false); + this.popDialogue.setVisible(false); + this.pushButton1.setVisible(false); + this.pushButton2.setVisible(false); + this.popButton1.setVisible(false); + this.popButton2.setVisible(false); + + // Make plank and ladder items continuously pulsate + this.createPulsateEffect( + this, + this.plank, + 1.15, // Scale factor for pulsating effect + 1000 // Duration of each tween cycle in milliseconds + ); + this.createPulsateEffect( + this, + this.ladder, + 1.1, // Scale factor for pulsating effect + 1000 // Duration of each tween cycle in milliseconds + ); + + this.arrowTween = this.playArrowTween(this.downArrow.x); + } + + private playArrowTween(minY: number) { + let maxY = minY + 30; + return this.tweens.add({ + targets: this.downArrow, + y: [minY, maxY], + duration: 500, + ease: "Cubic.easeOut", + yoyo: true, + repeat: -1, + }); + } + + private updateStackView() { + const offsetX = 1170; // starting X position for stack items + const offsetY = 270; // starting Y position for stack items + const padding = 19.3; + + let currTotalHeight = 0; + + this.stack.forEach((item) => { + // Calculate and set (x, y) position of stack items in stackpack view + item.setOrigin(0.5, 0); + const stackItemX = offsetX; + const stackItemY = + offsetY - item.displayHeight - currTotalHeight - padding; + currTotalHeight += item.displayHeight + padding; + + // Animation to drop the item into its position in the stackpack + this.tweens.add({ + targets: item, + x: stackItemX, + y: stackItemY, + duration: 800, + ease: "Cubic.InOut", + onComplete: () => { + this.isPushingMap[item.name] = false; + }, + }); + }); + } + + private collectItem(item: Phaser.GameObjects.Sprite) { + this.collectSound.play(); + if (this.collectedItems.includes(item)) { + return; + } + + this.isPushingMap[item.name] = true; + + // Save the x and y scales of the collected item + const currScaleX = item.scaleX; + const currScaleY = item.scaleY; + + // Animation to make item bigger, then smaller, and then fly up to stackpack + this.tweens.add({ + targets: item, + scaleX: currScaleX * 1.5, // Scale up item size for a bit + scaleY: currScaleY * 1.5, + duration: 180, + ease: "Exponential.InOut", + onComplete: () => { + this.tweens.add({ + targets: item, + scaleX: currScaleX, // Scale down item back to normal + scaleY: currScaleY, + duration: 150, + ease: "Exponential.InOut", + onComplete: () => { + // Move item to the stackpack view + this.tweens.add({ + targets: item, + x: 1170, + y: -10, // Y position of item before it is dropped into its actual position in stackpack + scaleX: currScaleX * 0.5, // Scale down the item for stackpack view + scaleY: currScaleY * 0.5, + rotation: Math.PI * 2, // Rotate the item while moving it to stackpack + duration: 940, + ease: "Cubic.In", + onComplete: () => { + // Add the item to the stack + this.stack.push(item); + this.updateStackView(); + }, + }); + }, + }); + }, + }); + + // Add the item to the grand list of collected items + this.collectedItems.push(item); + this.stopPulsateEffect(); + + this.updateStackView(); + } + + private useItem() { + if (this.isPushingMap[this.stack[this.stack.length - 1].name]) { + return; // Prevent popping if a push is in progress + } + + // Remove the top item from the stackpack + const poppedItem = this.stack.pop(); + + if (poppedItem) { + // Add the item to the list of used items + this.usedItems.push(poppedItem); + + // Animation to fade item out from stackpack and then fade in in its new location + this.tweens.add({ + targets: poppedItem, + alpha: 0, // Fade out + duration: 200, + onComplete: () => { + // Set item origin back to default (center) + poppedItem.setOrigin(0.5, 0.5); + + // Move popped item to location it will be used + if (poppedItem.name === "ladder") { + this.ladderSound.play(); + poppedItem.setPosition(680, 365); + this.ladderHighlightBox.setVisible(false); + } + if (poppedItem.name === "plank") { + this.plankSound.play(); + poppedItem.setPosition(815, 600); + this.plankHighlightBox.setVisible(false); + this.plankPlatform?.enableBody(true, 938, 650); + } + if (poppedItem.name === "key") { + this.doorOpenSound.play(); + this.keyHighlightBox.setVisible(false); + this.popButton2?.setVisible(false); + this.door?.setTexture("opendoor"); + this.pauseTime(); + // Make the player get sucked into the door + if (this.player && this.door) { + this.tweens.add({ + targets: this.player, + scaleX: 0.27, + scaleY: 0.27, + rotation: Math.PI * 3, + x: this.door.x - 10, + y: this.door.y + 15, + duration: 800, + onComplete: () => { + this.winSound.play(); + if (this.input.keyboard) { + this.input.keyboard.enabled = false; + } + this.player?.setVisible(false); + var completedTime = this.add + .text( + 640, + 345, + this.formatTime(this.elapsedTime), + { + fontSize: "40px", + color: "#000000", + } + ) + .setDepth(11) + .setVisible(false); + // Level popup depends on time it takes to complete + if (this.elapsedTime <= 30000) { + this.starsPopup = this.threeStarsPopup; + this.threeStarsPopup.add(completedTime); + this.threeStarsPopup + .setVisible(true) + .setDepth(10); + this.level0Stars = 3; + } + if ( + this.elapsedTime > 30000 && + this.elapsedTime <= 60000 + ) { + this.starsPopup = this.twoStarsPopup; + this.twoStarsPopup.add(completedTime); + this.twoStarsPopup + .setVisible(true) + .setDepth(10); + // Update stars if its better than previous time + if (this.level0Stars < 2) { + this.level0Stars = 2; + } + } + if (this.elapsedTime > 60000) { + this.starsPopup = this.oneStarPopup; + this.oneStarPopup.add(completedTime); + this.oneStarPopup + .setVisible(true) + .setDepth(10); + // Update stars if its better than previous time + if (this.level0Stars < 1) { + this.level0Stars = 1; + } + } + // Animate level complete popup + this.tweens.add({ + targets: this.starsPopup, + alpha: 1, + duration: 5000, + ease: "Linear", + delay: 1000, // Delay the animation slightly + }); + + if (this.level1State == 0) { + this.level0State = 3; + this.level1State = 1; + } else { + this.level0State = 3; + } + }, + }); + } + } + this.tweens.add({ + targets: poppedItem, + scaleX: poppedItem.scaleX * 2, + scaleY: poppedItem.scaleY * 2, + alpha: 1, // Fade in + duration: 300, + onComplete: () => { + this.updateStackView(); + }, + }); + }, + }); + } + } + + // Animation for using free pop + private freePop() { + if (this.isPushingMap[this.stack[this.stack.length - 1].name]) { + return; // Prevent popping if a push is in progress + } + + // Remove the top item from the stackpack + const poppedItem = this.stack.pop(); + + if (poppedItem && this.lives !== 0) { + // Remove popped item from grand list of collected items + const index = this.collectedItems.indexOf(poppedItem); + if (index !== -1) { + this.collectedItems.splice(index, 1); + } + + // Animation to fade item out from stackpack and then fade in in its new location + this.tweens.add({ + targets: poppedItem, + alpha: 0, // Fade out + duration: 200, + onComplete: () => { + // Set item origin back to default (center) + poppedItem.setOrigin(0.5, 0.5); + + let originalScaleX = 0; + let originalScaleY = 0; + // Move popped item to its original location + if (poppedItem.name === "ladder") { + poppedItem.setPosition(1050, 550); + originalScaleX = 0.5; + originalScaleY = 0.5; + } + if (poppedItem.name === "plank") { + poppedItem.setPosition(350, 530); + originalScaleX = 0.5; + originalScaleY = 0.5; + } + if (poppedItem.name === "key") { + poppedItem.setPosition(1200, 650); + originalScaleX = 2.5; + originalScaleY = 2.5; + } + + this.tweens.add({ + targets: poppedItem, + scaleX: originalScaleX, + scaleY: originalScaleY, + alpha: 1, // Fade in + duration: 300, + onComplete: () => { + this.updateStackView(); + if (poppedItem.name === "ladder") { + this.createPulsateEffect( + this, + poppedItem, + 1.1, + 1000 + ); + } + if (poppedItem.name === "plank") { + this.createPulsateEffect( + this, + poppedItem, + 1.15, + 1000 + ); + } + }, + }); + }, + }); + } + } + + private popWrongItem(usageArea: Phaser.GameObjects.Rectangle) { + if (this.isPushingMap[this.stack[this.stack.length - 1].name]) { + return; // Prevent popping if a push is in progress + } + + this.poppingWrongItem = true; + this.loseLife(); + this.poppingWrongItem = false; + + // Remove the top item from the stackpack + const poppedItem = this.stack.pop(); + + if (poppedItem && this.lives !== 0) { + // Remove popped item from grand list of collected items + const index = this.collectedItems.indexOf(poppedItem); + if (index !== -1) { + this.collectedItems.splice(index, 1); + } + + // Animation to flash red in location player tried to use item + this.tweens.add({ + targets: usageArea, + alpha: 0, + duration: 300, + yoyo: true, + repeat: 1, + onStart: () => { + usageArea.alpha = 0.55; + usageArea.fillColor = 0xff0000; // Make area red + this.flashingRed = true; + }, + onComplete: () => { + usageArea.alpha = 0.25; // Reset area color and alpha + usageArea.fillColor = 0xffff00; + this.flashingRed = false; + }, + }); + + // Animation to fade item out from stackpack and then fade in in its new location + this.tweens.add({ + targets: poppedItem, + alpha: 0, // Fade out + duration: 200, + onComplete: () => { + // Set item origin back to default (center) + poppedItem.setOrigin(0.5, 0.5); + + let originalScaleX = 0; + let originalScaleY = 0; + // Move popped item to its original location + if (poppedItem.name === "ladder") { + poppedItem.setPosition(1050, 550); + originalScaleX = 0.5; + originalScaleY = 0.5; + } + if (poppedItem.name === "plank") { + poppedItem.setPosition(350, 530); + originalScaleX = 0.5; + originalScaleY = 0.5; + } + if (poppedItem.name === "key") { + poppedItem.setPosition(1200, 650); + originalScaleX = 2.5; + originalScaleY = 2.5; + } + + this.tweens.add({ + targets: poppedItem, + scaleX: originalScaleX, + scaleY: originalScaleY, + alpha: 1, // Fade in + duration: 300, + onComplete: () => { + this.updateStackView(); + if (poppedItem.name === "ladder") { + this.createPulsateEffect( + this, + poppedItem, + 1.1, + 1000 + ); + } + if (poppedItem.name === "plank") { + this.createPulsateEffect( + this, + poppedItem, + 1.15, + 1000 + ); + } + }, + }); + }, + }); + } + } + + private createHearts() { + this.lives = 3; + this.hearts = []; + + for (let i = 0; i < 3; i++) { + this.hearts.push( + this.add.sprite(32 + i * 50, 80, "heart").setScale(0.5) + ); + } + } + + private loseLife() { + this.injureSound.play(); + if (!this.isColliding && this.player) { + this.isColliding = true; + if (this.poppingWrongItem) { + this.sound.play("wrong-sound"); + } else { + this.sound.play("injure-sound"); + } + + this.player.setVelocity(0, 0); + if (this.lastDirection === "right") { + this.player.anims.play("hurt_right"); + } else { + this.player.anims.play("hurt_left"); + } + this.lives--; + + // Removing hearts from free pop + const heartToRemove = this.hearts?.pop(); + if (heartToRemove) { + //heartToRemove.destroy(); + this.tweens.add({ + targets: heartToRemove, + scaleX: 0.8, + scaleY: 0.8, + duration: 200, + yoyo: true, + onComplete: () => { + heartToRemove.setTint(0x000000); // Make heart black + heartToRemove.setScale(0.5); // Reset the heart's scale + }, + }); + } + + if (this.lives === 0) { + this.playerDie(); + } + + // Reset isColliding flag + this.time.delayedCall( + 500, + () => { + this.isColliding = false; + if (this.collidingWithSpikes) { + this.player?.setPosition(100, 450); // Reset player's position + this.collidingWithSpikes = false; + } + }, + [], + this + ); + } + } + + private playerDie() { + this.deathSound.play(); + this.player?.setTint(0xff0000); + + this.time.delayedCall(300, () => { + this.scene.launch("YouDiedScene", { + currentLevelKey: this.scene.key, + level0State: this.level0State, + level1State: this.level1State, + level2State: this.level2State, + level3State: this.level3State, + level0Stars: this.level0Stars, + level1Stars: this.level1Stars, + level2Stars: this.level2Stars, + level3Stars: this.level3Stars, + }); + this.player?.clearTint(); + + // Reset the stack and collected items + this.stack = []; + this.updateStackView(); + this.collectedItems = []; + this.usedItems = []; + this.lives = 3; + this.createHearts(); + this.freePopsLeft = 2; + this.backgroundMusic.stop(); + this.backgroundMusic.destroy(); + this.poppingWrongItem = false; + }); + } + + private resetScene() { + // Reset the stack and collected items and down arrow + this.stack = []; + this.updateStackView(); + this.collectedItems = []; + this.usedItems = []; + this.lives = 3; + this.createHearts(); + this.freePopsLeft = 2; + this.downArrow?.setPosition(350, 350); + this.startTime = this.time.now; + this.pausedTime = 0; + this.isPaused = false; + this.climbing = false; + this.flashingRed = false; + this.isColliding = false; + this.collidingWithSpikes = false; + this.poppingWrongItem = false; + } + + private createPulsateEffect( + scene: Phaser.Scene, + target: Phaser.GameObjects.GameObject, + scaleFactor: number, + duration: number + ): Phaser.Tweens.Tween | null { + // Check if the item has been collected + if (this.collectedItems.includes(target as Phaser.GameObjects.Sprite)) { + return null; // Don't create the tween if the item has been collected + } + return scene.tweens.add({ + targets: target, + scaleX: `*=${scaleFactor}`, + scaleY: `*=${scaleFactor}`, + duration: duration, + yoyo: true, // Reverse back to original scale + repeat: -1, // Repeat indefinitely + }); + } + + private stopPulsateEffect() { + // Stop pulsating collected items + this.collectedItems.forEach((item) => { + const tween = this.tweens.getTweensOf(item); + if (tween.length > 1) { + tween[0].stop(); + } + }); + } + + private formatTime(milliseconds: number) { + var mins = Math.floor(milliseconds / 60000); + var secs = Math.floor((milliseconds % 60000) / 1000); + return ( + mins.toString().padStart(2, "0") + + ":" + + secs.toString().padStart(2, "0") + ); + } + + private pauseTime() { + this.isPaused = !this.isPaused; + if (this.isPaused) { + this.pausedTime = this.time.now - this.startTime; + } else { + this.startTime = this.time.now - this.pausedTime; + } + } + + update() { + // Updating timer + if (!this.isPaused) { + //console.log("updating time", this.time.now, this.startTime); + var currentTime = this.time.now; + this.elapsedTime = currentTime - this.startTime; + this.timerText.setText( + "Time: " + this.formatTime(this.elapsedTime) + ); + } + + // Check if the player is at arrow location and if so, move to next location + // After pushing plank + if ( + this.player && + this.downArrow && + this.downArrow.x == 350 && + this.stack.length > 0 && + this.stack[this.stack.length - 1].name === "plank" + ) { + this.downArrow.setPosition(650, 500); + this.arrowTween = this.playArrowTween(this.downArrow.y); + } + // After popping plank + if ( + this.player && + this.downArrow && + this.plank && + this.downArrow.x == 650 && + this.plank.x == 815 + ) { + this.downArrow.setPosition(1115, 380); + this.arrowTween = this.playArrowTween(this.downArrow.y); + } + // After pushing ladder + if ( + this.player && + this.downArrow && + this.downArrow.x == 1115 && + Phaser.Math.Distance.Between( + this.player.x, + this.player.y, + 1115, + 560 + 150 + ) < 90 + ) { + this.topInstruction?.setVisible(true); + this.downArrow.setVisible(false); + this.downArrow.setPosition(0, 560); + setTimeout(() => { + this.topInstruction?.setVisible(false); + this.orderInstruction?.setVisible(true); + }, 4000); + setTimeout(() => { + this.orderInstruction?.setVisible(false); + this.hintInstruction?.setVisible(true); + }, 9000); + setTimeout(() => { + this.hintInstruction?.setVisible(false); + this.freepopDialogue?.setVisible(true); + }, 15500); + setTimeout(() => { + this.freepopDialogue?.setVisible(false); + this.lifoInstruction?.setVisible(true); + }, 19500); + setTimeout(() => { + this.lifoInstruction?.setVisible(false); + }, 24500); + } + + // Key animation + if (this.key) { + this.key.anims.play("turn", true); + } + + // Show movement instructions until any key is pressed + if (this.player && this.cursors) { + if ( + this.cursors.up.isDown || + this.cursors.down.isDown || + this.cursors.right.isDown || + this.cursors.left.isDown || + this.cursors.space.isDown + ) { + setTimeout(() => { + this.movementInstruction?.setVisible(false); + }, 500); + } + } + + // Move the gal with arrow keys + // Inside your update function or wherever you handle player movement + if (this.player && this.cursors) { + if (!this.isColliding) { + if ( + this.cursors.up.isDown && + this.player.body?.touching.down && + !this.climbing + ) { + this.player.anims.play("jump_right", true); + this.player.setVelocityY(-530); + } else if (this.cursors.right.isDown) { + this.player.setVelocityX(290); + this.player.anims.play("right", true); + this.lastDirection = "right"; // Update last direction + } else if (this.cursors.left.isDown) { + this.player.setVelocityX(-290); + this.player.anims.play("left", true); + this.lastDirection = "left"; // Update last direction + } else if (!this.climbing) { + this.player.setVelocityX(0); + // Check last direction and play corresponding idle animation + if (this.lastDirection === "right") { + this.player.anims.play("idle_right", true); + } else { + this.player.anims.play("idle_left", true); + } + } + } + } + + // Collect item if 'E' key is pressed + if (this.player && this.keyE?.isDown && !this.keyEPressed) { + this.keyEPressed = true; // Set the flag for the E key being pressed to true + + // Check if the player is close enough to the key, ladder, or plank, and if so, collect it + if ( + this.key && + Phaser.Math.Distance.Between( + this.player.x, + this.player.y, + this.key.x, + this.key.y + ) < 100 + ) { + this.collectItem(this.key); + } + if ( + this.ladder && + Phaser.Math.Distance.Between( + this.player.x, + this.player.y, + this.ladder.x, + this.ladder.y + ) < 100 + ) { + this.collectItem(this.ladder); + } + if ( + this.plank && + Phaser.Math.Distance.Between( + this.player.x, + this.player.y, + this.plank.x, + this.plank.y + ) < 100 + ) { + this.collectItem(this.plank); + } + } + // Check if 'E' key is released + if (this.keyE?.isUp) { + this.keyEPressed = false; // Reset the keyEPressed flag when the E key is released + } + + // Check if 'F' key is released + if (this.keyF?.isUp) { + this.keyFPressed = false; // Reset the keyFPressed flag when the F key is released + } + + // Check if player is near detection area + if (this.player) { + if ( + Phaser.Geom.Intersects.RectangleToRectangle( + this.player.getBounds(), + this.ladderDetectionArea.getBounds() + ) && + this.ladder && + !this.usedItems.includes(this.ladder) + ) { + // If player overlaps with ladder detection area, show the highlight box + this.ladderHighlightBox.setVisible(true); + if ( + this.keyF?.isDown && + !this.keyFPressed && + this.stack.length > 0 + ) { + // If player presses F + if (this.stack[this.stack.length - 1].name === "ladder") { + // If the top item is ladder, use it + this.keyFPressed = true; + this.useItem(); + } else { + // If the top item is not ladder, pop it and lose life + this.keyFPressed = true; + this.popWrongItem(this.ladderHighlightBox); + } + } + } else if (!this.flashingRed) { + this.ladderHighlightBox.setVisible(false); + } + + if ( + Phaser.Geom.Intersects.RectangleToRectangle( + this.player.getBounds(), + this.plankDetectionAreasGroup.getBounds() + ) && + this.plank && + !this.usedItems.includes(this.plank) + ) { + // If player overlaps with plank detection area, show the highlight box + this.plankHighlightBox.setVisible(true); + if ( + this.keyF?.isDown && + !this.keyFPressed && + this.stack.length > 0 + ) { + // If player presses F + if (this.stack[this.stack.length - 1].name === "plank") { + // If the top item is plank, use it + this.keyFPressed = true; + this.useItem(); + } else { + // If the top item is not plank, pop it and lose life + this.keyFPressed = true; + this.popWrongItem(this.plankHighlightBox); + } + } + } else if (!this.flashingRed) { + this.plankHighlightBox.setVisible(false); + } + + if ( + Phaser.Geom.Intersects.RectangleToRectangle( + this.player.getBounds(), + this.keyDetectionArea.getBounds() + ) && + this.key && + !this.usedItems.includes(this.key) + ) { + // If player overlaps with key detection area, show highlight box + this.keyHighlightBox.setVisible(true); + if ( + this.keyF?.isDown && + !this.keyFPressed && + this.stack.length > 0 + ) { + // If player presses F + if (this.stack[this.stack.length - 1].name === "key") { + // If the top item is key, use it + this.keyFPressed = true; + this.useItem(); + } else { + // If the top item is not key, pop it and lose life + this.keyFPressed = true; + this.popWrongItem(this.keyHighlightBox); + } + } + } else if (!this.flashingRed) { + this.keyHighlightBox.setVisible(false); + } + } + + // Climbing the ladder + if (this.player && this.ladder && this.cursors) { + // Max distance player can be from ladder to climb it + const xTolerance = 30; // Tolerance for X position + const yTolerance = 145; // Tolerance for Y position + // Calculate horizontal and vertical distances between player and ladder + const deltaX = Math.abs(this.player.x - this.ladder.x); + const deltaY = Math.abs(this.player.y - this.ladder.y); + + if ( + this.ladder.x === 680 && + deltaX < xTolerance && + deltaY < yTolerance && + this.cursors.up.isDown + ) { + if (!this.climbing) { + this.climbingLadderSound.play(); + } + this.climbing = true; + this.player.anims.play("climb", true); + this.player.setVelocityY(-150); + } else { + if (this.climbing) { + this.climbingLadderSound.stop(); + } + this.climbing = false; + } + } + + if (this.player && this.plank && this.spikes) { + if (this.plank.x === 815) { + this.physics.world.enable(this.plank); + this.physics.add.collider(this.plank, this.spikes); + } + } + + // Check if player touches the spikes and lose life if so + if (this.player && this.spikes) { + this.physics.add.collider( + this.player, + this.spikes, + () => { + this.collidingWithSpikes = true; + this.loseLife(); + }, + undefined, + this + ); + } + + // FtoPop DIALOGUE + if (this.player && this.stack.length > 0) { + if ( + Phaser.Geom.Intersects.RectangleToRectangle( + this.player.getBounds(), + this.plankDetectionAreasGroup.getBounds() + ) && + this.stack[this.stack.length - 1].name === "plank" + ) { + this.popDialogue?.setVisible(true); + } else { + this.popDialogue?.setVisible(false); + } + } + if (!(this.stack.length > 0)) { + this.popDialogue?.setVisible(false); + } + + // Making Text Boxes appear/disappear: EtoPush DIALOGUE + if (this.player && this.plank) { + if ( + Phaser.Math.Distance.Between( + this.player.x, + this.player.y, + this.plank.x, + this.plank.y + ) < 100 && + !this.collectedItems.includes(this.plank) + ) { + this.pushDialogue?.setVisible(true); + } else { + this.pushDialogue?.setVisible(false); + } + } + + // E to push button1: ladder + if (this.player && this.ladder) { + if ( + Phaser.Math.Distance.Between( + this.player.x, + this.player.y, + this.ladder.x, + this.ladder.y + ) < 100 && + !this.collectedItems.includes(this.ladder) + ) { + this.pushButton1?.setVisible(true); + } else { + this.pushButton1?.setVisible(false); + } + } + + // E to push button2: key + if (this.player && this.key) { + if ( + Phaser.Math.Distance.Between( + this.player.x, + this.player.y, + this.key.x, + this.key.y + ) < 100 && + !this.collectedItems.includes(this.key) + ) { + this.pushButton2?.setVisible(true); + } else { + this.pushButton2?.setVisible(false); + } + } + + // F to pop button1: ladder + if (this.player && this.stack.length > 0) { + if ( + Phaser.Geom.Intersects.RectangleToRectangle( + this.player.getBounds(), + this.ladderDetectionArea.getBounds() + ) && + this.ladder && + !this.usedItems.includes(this.ladder) + ) { + this.popButton1?.setVisible(true); + } else { + this.popButton1?.setVisible(false); + } + } else { + // If the stack is empty or the player is not present, hide the button + this.popButton1?.setVisible(false); + } + + // F to pop button: key + if (this.player && this.stack.length > 0) { + if ( + Phaser.Geom.Intersects.RectangleToRectangle( + this.player.getBounds(), + this.keyDetectionArea.getBounds() + ) && + this.key && + !this.usedItems.includes(this.key) + ) { + this.popButton2?.setVisible(true); + } else { + this.popButton2?.setVisible(false); + } + } else { + // If the stack is empty or the player is not present, hide the button + this.popButton2?.setVisible(false); + } + } +} diff --git a/src/scenes/preloadScene.ts b/src/scenes/preloadScene.ts index c17b81ba..920ccba8 100644 --- a/src/scenes/preloadScene.ts +++ b/src/scenes/preloadScene.ts @@ -10,6 +10,6 @@ export default class PreloadScene extends Phaser.Scene { } create() { - this.scene.start("MainScene"); + this.scene.start("title-screen"); } } diff --git a/src/scenes/startCutScene.ts b/src/scenes/startCutScene.ts new file mode 100644 index 00000000..418af5fa --- /dev/null +++ b/src/scenes/startCutScene.ts @@ -0,0 +1,711 @@ +import Phaser from "phaser"; + +interface GameMapData { + level0State: number; + level1State: number; + level2State: number; + level3State: number; +} + +export default class StartCutScene extends Phaser.Scene { + private player?: Phaser.Physics.Arcade.Sprite; + private dude?: Phaser.Physics.Arcade.Sprite; + private cursors?: Phaser.Types.Input.Keyboard.CursorKeys; + private platforms?: Phaser.Physics.Arcade.StaticGroup; + private door?: Phaser.GameObjects.Image; + private openDoor?: Phaser.GameObjects.Image; + private ground?: Phaser.Physics.Arcade.Image; + private stackpack?: Phaser.GameObjects.Image; + private heart1?: Phaser.GameObjects.Image; + private heart2?: Phaser.GameObjects.Image; + + private flower?: Phaser.GameObjects.Image; + private portal?: Phaser.GameObjects.Image; + + private questionMark1?: Phaser.GameObjects.Text; + private questionMark2?: Phaser.GameObjects.Text; + + private exclamationPoint1?: Phaser.GameObjects.Text; + private exclamationPoint2?: Phaser.GameObjects.Text; + + private galMove: string = "right"; + private dudeMove: string = "left"; + private galLastDirection: string = "right"; + private dudeLastDirection: string = "right"; + + private delay: number; + + private level0State: number; + private level1State: number; + private level2State: number; + private level3State: number; + + constructor() { + super({ key: "StartCutScene" }); + } + + preload() { + this.load.image( + "end-cutscene-background", + "assets/end-cutscene/end-background1.jpeg" + ); + this.load.image( + "final-background", + "assets/end-cutscene/final-background.jpg" + ); + this.load.image("just-stackpack", "assets/backpack.png"); + + this.load.image("cutscene-heart", "assets/end-cutscene/heart.png"); + + this.load.image( + "play-again-button", + "assets/end-cutscene/play-again-button.png" + ); + this.load.image( + "world-map-button", + "assets/end-cutscene/world-map-button.png" + ); + + this.load.spritesheet( + "gal_idle_right", + "assets/Pink_Monster_Idle_4.png", + { frameWidth: 128, frameHeight: 128 } + ); + this.load.spritesheet( + "gal_idle_left", + "assets/Pink_Monster_Idle_Left4.png", + { frameWidth: 128, frameHeight: 128 } + ); + this.load.spritesheet( + "gal_walk_right", + "assets/Pink_Monster_Walk_6.png", + { + frameWidth: 128, + frameHeight: 128, + } + ); + this.load.spritesheet( + "gal_walk_left", + "assets/Pink_Monster_Walk_Left6.png", + { frameWidth: 128, frameHeight: 128 } + ); + /*this.load.spritesheet( + "gal_jump_right", + "assets/Pink_Monster_Jump_8.png", + { frameWidth: 128, frameHeight: 128 } + );*/ + + this.load.spritesheet( + "dude_idle_right", + "assets/Dude_Monster_Idle_4.png", + { frameWidth: 128, frameHeight: 128 } + ); + this.load.spritesheet( + "dude_idle_left", + "assets/Dude_Monster_Idle_Left4.png", + { frameWidth: 128, frameHeight: 128 } + ); + this.load.spritesheet( + "dude_walk_right", + "assets/Dude_Monster_Walk_6.png", + { + frameWidth: 128, + frameHeight: 128, + } + ); + this.load.spritesheet( + "dude_walk_left", + "assets/Dude_Monster_Walk_Left6.png", + { + frameWidth: 128, + frameHeight: 128, + } + ); + this.load.spritesheet( + "dude_run_right", + "assets/Dude_Monster_Run_6.png", + { + frameWidth: 128, + frameHeight: 128, + } + ); + this.load.spritesheet( + "dude_run_left", + "assets/Dude_Monster_Run_Left6.png", + { + frameWidth: 128, + frameHeight: 128, + } + ); + this.load.spritesheet( + "gal_jump_right", + "assets/Pink_Monster_Jump_8.png", + { + frameWidth: 128, + frameHeight: 128, + } + ); + + this.load.image("level0-platform", "assets/level0/platform.png"); + this.load.image("skipButton", "assets/SkipButton.png"); + this.load.image("picnic", "assets/picnic.png"); + this.load.image("grass", "assets/grassStrip.png"); + this.load.image("flower", "assets/flower.png"); + this.load.image("portal", "assets/portal.png"); + this.load.image("cutsceneDoor", "assets/level0/door.png"); + this.load.image("cutsceneDoorOpen", "assets/level0/open-door.png"); + + //this.load.image("red-opendoor", "assets/level3/red-door-open.png"); + } + + create(data: GameMapData) { + // Resume all animations and tweens + this.anims.resumeAll(); + this.tweens.resumeAll(); + // Make it so player can enter keyboard input + if (this.input.keyboard) { + this.input.keyboard.enabled = true; + } + + this.level0State = data.level0State; + this.level1State = data.level1State; + this.level2State = data.level2State; + this.level3State = data.level3State; + + this.galMove = ""; + this.dudeMove = ""; + this.galLastDirection = "right"; + this.dudeLastDirection = "right"; + + this.delay = 0; + + const backgroundImage = this.add + .image(0, 0, "end-cutscene-background") + .setOrigin(0, 0); + backgroundImage.setScale( + this.cameras.main.width / backgroundImage.width + 0.1, + this.cameras.main.height / backgroundImage.height + ); + + const skipButton = this.add.image(150, 100, "skipButton"); + skipButton.setSize(skipButton.width - 100, skipButton.height - 200); + + skipButton.setInteractive(); + + const originalScale = skipButton.scaleX; + const hoverScale = originalScale * 1.05; + + skipButton.on("pointerover", () => { + this.tweens.add({ + targets: skipButton, + scaleX: hoverScale, + scaleY: hoverScale, + duration: 115, + ease: "Linear", + }); + }); + + skipButton.on("pointerout", () => { + this.tweens.add({ + targets: skipButton, + scaleX: originalScale, + scaleY: originalScale, + duration: 115, + ease: "Linear", + }); + }); + + skipButton.on("pointerup", () => { + this.scene.start("game-map"); + }); + + this.heart1 = this.add.image(300, 300, "cutscene-heart"); + this.heart1.setDepth(20); + this.heart1.setScale(0.15, 0.15); + this.heart1.setVisible(false); + this.heart2 = this.add.image(600, 300, "cutscene-heart"); + this.heart2.setDepth(20); + this.heart2.setScale(0.15, 0.15); + this.heart2.setVisible(false); + + // Creating Gal + this.player = this.physics.add + .sprite(120, 430, "gal_walk_right") + .setScale(1.2, 1.2) + .setOrigin(0.5, 0.5); + this.player.setCollideWorldBounds(true); + + this.anims.create({ + key: "jump_right", + frames: this.anims.generateFrameNumbers("gal_jump_right", { + start: 0, + end: 7, + }), + }); + + this.anims.create({ + key: "gal_walk_right", + frames: this.anims.generateFrameNumbers("gal_walk_right", { + start: 0, + end: 5, + }), + repeat: -1, + }); + this.anims.create({ + key: "gal_walk_left", + frames: this.anims.generateFrameNumbers("gal_walk_left", { + start: 0, + end: 5, + }), + repeat: -1, + }); + this.anims.create({ + key: "gal_idle_right", + frames: this.anims.generateFrameNumbers("gal_idle_right", { + start: 0, + end: 3, + }), + frameRate: 10, + repeat: -1, + }); + this.anims.create({ + key: "gal_idle_left", + frames: this.anims.generateFrameNumbers("gal_idle_left", { + start: 0, + end: 3, + }), + frameRate: 10, + repeat: -1, + }); + + // Creating Guy + this.dude = this.physics.add + .sprite(220, 430, "dude_run_right") + .setScale(1.2, 1.2) + .setOrigin(0.5, 0.5); + this.dude.setCollideWorldBounds(true); + + this.anims.create({ + key: "dude_idle_right", + frames: this.anims.generateFrameNumbers("dude_idle_right", { + start: 0, + end: 3, + }), + frameRate: 10, + repeat: -1, + }); + this.anims.create({ + key: "dude_idle_left", + frames: this.anims.generateFrameNumbers("dude_idle_left", { + start: 0, + end: 3, + }), + frameRate: 10, + repeat: -1, + }); + this.anims.create({ + key: "dude_walk_right", + frames: this.anims.generateFrameNumbers("dude_walk_right", { + start: 0, + end: 5, + }), + repeat: -1, + }); + this.anims.create({ + key: "dude_walk_left", + frames: this.anims.generateFrameNumbers("dude_walk_left", { + start: 0, + end: 5, + }), + repeat: -1, + }); + this.anims.create({ + key: "dude_run_right", + frames: this.anims.generateFrameNumbers("dude_run_right", { + start: 0, + end: 5, + }), + repeat: -1, + }); + this.anims.create({ + key: "dude_run_left", + frames: this.anims.generateFrameNumbers("dude_run_left", { + start: 0, + end: 5, + }), + repeat: -1, + }); + + this.cursors = this.input.keyboard?.createCursorKeys(); + + // Create platforms + this.platforms = this.physics.add.staticGroup(); + this.ground = this.platforms.create( + 650, + 633, + "level0-platform" + ) as Phaser.Physics.Arcade.Image; + + this.ground.setScale(5).refreshBody(); + this.ground.setAlpha(0); // Hide the ground platform + + this.physics.add.collider(this.player, this.platforms); + this.physics.add.collider(this.dude, this.platforms); + + const grass = this.add.image(650, 630, "grass"); + grass.setScale(this.cameras.main.width + 10 / grass.width, 1.4); + + this.add.image(600, 600, "picnic"); + this.flower = this.add.image(850, 600, "flower"); + this.flower.setScale(0.25, 0.25); + + this.portal = this.add.image(1000, 260, "portal"); + this.portal.setScale(0); + this.portal.setVisible(false); + + this.door = this.add.image(1000, 200, "cutsceneDoor"); + this.door.setScale(0.01, 0.01); + this.door.setVisible(false); + + this.openDoor = this.add.image(1000, 450, "cutsceneDoorOpen"); + this.openDoor.setScale(0.1, 0.1); + this.openDoor.setVisible(false); + + this.stackpack = this.add + .image(850, 400, "just-stackpack") + //.setPosition(100, 551) + .setOrigin(0.5, 1); + this.stackpack.setScale(0.05, 0.05); + this.stackpack.setVisible(false); + + this.questionMark1 = this.add.text(780, 350, "?", { + fontFamily: "Arial", + fontSize: 60, + color: "#000000", + }); + + this.exclamationPoint1 = this.add.text(500, 300, "!", { + fontFamily: "Arial", + fontSize: 60, + color: "#000000", + }); + + this.questionMark2 = this.add.text(480, 350, "?", { + fontFamily: "Arial", + fontSize: 60, + color: "#000000", + }); + + this.exclamationPoint2 = this.add.text(800, 300, "!", { + fontFamily: "Arial", + fontSize: 60, + color: "#000000", + }); + this.questionMark1.setVisible(false); + this.questionMark2.setVisible(false); + this.exclamationPoint1.setVisible(false); + this.exclamationPoint2.setVisible(false); + + // Resize collision boxes of player and everything else that can be collided with + this.player + .setSize(this.player.width - 64, this.player.height) + .setOffset(32, 0) + .setDepth(10); + + this.dude + .setSize(this.dude.width - 64, this.dude.height) + .setOffset(32, 0) + .setDepth(10); + + /*this.door + .setSize(this.door.width, this.door.height - 60) + .setOffset(0, 0);*/ + + this.animateStart(); + } + + private animateStart() { + // Make stackpack drop to floor + /*this.tweens.add({ + targets: this.stackpack, + y: 551, + duration: 900, + });*/ + + this.delay = 1000; // Wait 1000 millisseconds before starting + + // GAL AND DUDE WALK TO PICNIC + setTimeout(() => { + // Tween for the player + this.tweens.add({ + targets: this.player, + x: 500, + duration: 1200, + onStart: () => { + this.galMove = "right"; + }, + onComplete: () => { + this.galMove = ""; + }, + }); + + // Tween for the dude + this.tweens.add({ + targets: this.dude, + x: 800, + duration: 1500, + onStart: () => { + this.dudeMove = "right"; + }, + onComplete: () => { + this.dudeMove = ""; + }, + }); + }, this.delay); + + this.delay += 2700; + + // DUDE PICKS FLOWER, TURNS LEFT + setTimeout(() => { + this.flower?.setX(860).setY(500); + this.flower?.setDepth(11); + }, this.delay); + this.delay += 1000; + + setTimeout(() => { + this.dudeLastDirection = "left"; + this.flower?.setX(740).setY(500); + }, this.delay); + this.delay += 1000; + + // HEARTS APPEAR + setTimeout(() => { + this.heart1?.setVisible(true); + this.heart2?.setVisible(true); + this.heart1?.setX(500).setY(350); + this.tweens.add({ + targets: this.heart1, + scaleX: `*=${1.2}`, + scaleY: `*=${1.2}`, + duration: 500, + yoyo: true, // Reverse back to original scale + repeat: -1, + }); + this.heart2?.setX(800).setY(350); + this.tweens.add({ + targets: this.heart2, + scaleX: `*=${1.2}`, + scaleY: `*=${1.2}`, + duration: 500, + yoyo: true, // Reverse back to original scale + repeat: -1, + }); + }, this.delay); + this.delay += 1000; + + setTimeout(() => { + this.heart1?.setVisible(false); + this.heart2?.setVisible(false); + this.flower?.setVisible(false); + }, this.delay); + this.delay += 1000; + + // PORTAL APPEARS AND THEY HAVE QUESTION MAKRS + setTimeout(() => { + this.portal?.setVisible(true); + this.tweens.add({ + targets: this.portal, + scaleX: 1, + scaleY: 1, + }); + this.tweens.add({ + targets: this.portal, + rotation: Math.PI * 3, + duration: 1000, + repeat: -1, + }); + }, this.delay); + this.delay += 1000; + + setTimeout(() => { + this.dudeLastDirection = "right"; + this.questionMark1?.setVisible(true); + this.questionMark2?.setVisible(true); + this.tweens.add({ + targets: this.questionMark1, + scaleX: 1.2, + scaleY: 1.2, + y: 300, + alpha: 0, + }); + this.tweens.add({ + targets: this.questionMark2, + scaleX: 1.2, + scaleY: 1.2, + y: 300, + alpha: 0, + }); + }, this.delay); + this.delay += 1000; + + setTimeout(() => { + this.questionMark1?.setVisible(false); + this.questionMark2?.setVisible(false); + }, this.delay); + this.delay += 1000; + + // DUDE GETS SPIRALED UP, EXCLAMATION POINT + setTimeout(() => { + if (this.portal) { + this.tweens.add({ + targets: this.dude, + scaleX: 0.27, + scaleY: 0.27, + rotation: Math.PI * 3, + x: this.portal.x - 10, + y: this.portal.y - 15, + duration: 1200, + onComplete: () => { + this.dude?.setVisible(false); + this.portal?.setVisible(false); + }, + }); + } + this.exclamationPoint1?.setVisible(true); + this.galMove = "jump"; + this.tweens.add({ + targets: this.exclamationPoint1, + scaleX: 1.2, + scaleY: 1.2, + y: 300, + }); + }, this.delay); + this.delay += 500; + + setTimeout(() => { + this.exclamationPoint1?.setVisible(false); + this.galMove = ""; + }, this.delay); + this.delay += 1000; + + // DOOR AND STACKPACK APPEAR + setTimeout(() => { + if (this.platforms) { + this.door?.setVisible(true); + this.stackpack?.setVisible(true); + this.tweens.add({ + targets: this.door, + scaleX: 0.1, + scaleY: 0.1, + y: 450, + }); + this.tweens.add({ + targets: this.stackpack, + scaleX: 0.08, + scaleY: 0.08, + y: 550, + }); + } + }, this.delay); + this.delay += 2000; + + // GAL PICKS UP STACKPACK, GOES IN DOOR, MAIN MENU POPS UP + setTimeout(() => { + this.tweens.add({ + targets: this.player, + x: 830, + duration: 1000, + onStart: () => { + this.galMove = "right"; + }, + onComplete: () => { + this.galMove = ""; + this.tweens.add({ + targets: this.stackpack, + x: this.player?.x, // Move towards the player's x position + y: this.player?.y, + scaleX: 0.03, // Shrink horizontally + scaleY: 0.03, // Shrink vertically + duration: 500, // Duration of animation + onComplete: () => { + this.stackpack?.setVisible(false); + this.tweens.add({ + targets: this.player, + x: 1000, + duration: 1000, + onStart: () => { + this.galMove = "right"; + }, + onComplete: () => { + this.galMove = ""; + this.openDoor?.setVisible(true); + this.tweens.add({ + targets: this.player, + alpha: 0, + onComplete: () => { + this.scene.start("game-map"); + }, + }); + }, + }); + }, + }); + }, + }); + }, this.delay); + this.delay += 1000; + + // DOOR OPEN, GAL GO THROUGH, MAIN MENU SCREEN + } + + update() { + // Gal animations + if (this.player) { + if (this.galMove == "up") { + this.player.setVelocityY(-530); + } else if (this.galMove == "right") { + this.player.anims.play("gal_walk_right", true); + this.galLastDirection = "right"; // Update last direction + } else if (this.galMove == "left") { + this.player.anims.play("gal_walk_left", true); + this.galLastDirection = "left"; // Update last direction + } else if (this.galMove == "jump") { + this.player.anims.play("jump_right", true); + this.galLastDirection = "right"; + } else { + this.player.setVelocityX(0); + // Check last direction and play corresponding idle animation + if (this.galLastDirection === "right") { + this.player.anims.play("gal_idle_right", true); + } else { + this.player.anims.play("gal_idle_left", true); + } + } + } + + // Guy animations + if (this.dude) { + if (this.dudeMove == "up") { + this.dude.anims.play("dude_idle_right", true); + this.dude.setVelocityY(-530); + } else if (this.dudeMove == "right") { + this.dude.anims.play("dude_run_right", true); + this.dudeLastDirection = "right"; // Update last direction + } else if (this.dudeMove == "left") { + this.dude.anims.play("dude_run_left", true); + this.dudeLastDirection = "left"; // Update last direction + } else if (this.dudeMove == "rightWalk") { + this.dude.anims.play("dude_walk_right", true); + this.dudeLastDirection = "right"; // Update last direction + } else { + this.dude.setVelocityX(0); + // Check last direction and play corresponding idle animation + if (this.dudeLastDirection === "right") { + this.dude.anims.play("dude_idle_right", true); + } else { + this.dude.anims.play("dude_idle_left", true); + } + } + } + } +} diff --git a/src/scenes/titleScreen.ts b/src/scenes/titleScreen.ts new file mode 100644 index 00000000..5a491943 --- /dev/null +++ b/src/scenes/titleScreen.ts @@ -0,0 +1,169 @@ +import Phaser from "phaser"; + +export default class TitleScreen extends Phaser.Scene { + private player?: Phaser.Physics.Arcade.Sprite; + private dude?: Phaser.Physics.Arcade.Sprite; + private platforms?: Phaser.Physics.Arcade.StaticGroup; + private ground?: Phaser.Physics.Arcade.Image; + + constructor() { + super({ key: "title-screen" }); + } + + preload() { + this.load.image("title-screen", "assets/title-screen.png"); + this.load.image("play-button", "assets/play-button.png"); + + this.load.spritesheet( + "gal_idle_right", + "assets/Pink_Monster_Idle_4.png", + { frameWidth: 128, frameHeight: 128 } + ); + + this.load.spritesheet( + "dude_idle_left", + "assets/Dude_Monster_Idle_Left4.png", + { frameWidth: 128, frameHeight: 128 } + ); + + this.load.image("level0-platform", "assets/level0/platform.png"); + } + + create() { + // Creating Gal + this.player = this.physics.add + .sprite(360, 485, "gal_idle_right") + .setScale(1.3, 1.3) + .setDepth(35); + this.player.setCollideWorldBounds(true); + + this.anims.create({ + key: "gal_idle_right", + frames: this.anims.generateFrameNumbers("gal_idle_right", { + start: 0, + end: 3, + }), + frameRate: 10, + repeat: -1, + }); + this.player.anims.play("gal_idle_right", true); + + // Creating Guy + this.dude = this.physics.add + .sprite(this.cameras.main.width - 360, 485, "dude_idle_left") + .setScale(1.3, 1.3) + .setDepth(35); + this.dude.setCollideWorldBounds(true); + + this.anims.create({ + key: "dude_idle_left", + frames: this.anims.generateFrameNumbers("dude_idle_left", { + start: 0, + end: 3, + }), + frameRate: 10, + repeat: -1, + }); + this.dude.anims.play("dude_idle_left", true); + + // Create platform + this.platforms = this.physics.add.staticGroup(); + this.ground = this.platforms.create( + 650, + 663, + "level0-platform" + ) as Phaser.Physics.Arcade.Image; + + this.ground.setScale(5).refreshBody(); + this.ground.setAlpha(0); // Hide the ground platform + + this.physics.add.collider(this.player, this.platforms); + this.physics.add.collider(this.dude, this.platforms); + + const backgroundImage = this.add + .image(0, 0, "title-screen") + .setOrigin(0, 0); + backgroundImage.setScale( + this.cameras.main.width / backgroundImage.width, + this.cameras.main.height / backgroundImage.height + ); + + // Go to levels map on play button click + const playButton = this.add.image( + this.cameras.main.width / 2, + 545, + "play-button" + ); + playButton.setScale(0.42); + + playButton.setInteractive(); + + const originalScale = playButton.scaleX; + const hoverScale = originalScale * 1.09; + + const originalPlayerPosition = { x: this.player.x, y: this.player.y }; + const originalDudePosition = { x: this.dude.x, y: this.dude.y }; + + // Change scale on hover + playButton.on("pointerover", () => { + this.tweens.add({ + targets: playButton, + scaleX: hoverScale, + scaleY: hoverScale, + duration: 115, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + + // Tween animation for moving player towards playButton + this.tweens.add({ + targets: this.player, + x: playButton.x - 190, + duration: 1000, // Duration of the tween in milliseconds + ease: "Sine", // Easing function for the tween + }); + + // Tween animation for moving dude towards playButton + this.tweens.add({ + targets: this.dude, + x: playButton.x + 190, + duration: 1000, // Duration of the tween in milliseconds + ease: "Sine", // Easing function for the tween + }); + }); + + // Restore original scale when pointer leaves + playButton.on("pointerout", () => { + this.tweens.add({ + targets: playButton, + scaleX: originalScale, + scaleY: originalScale, + duration: 115, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + + // Tween animation for moving player back to original position + this.tweens.add({ + targets: this.player, + x: originalPlayerPosition.x, + y: originalPlayerPosition.y, + duration: 1000, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + + // Tween animation for moving dude back to original position + this.tweens.add({ + targets: this.dude, + x: originalDudePosition.x, + y: originalDudePosition.y, + duration: 1000, // Duration of the tween in milliseconds + ease: "Linear", // Easing function for the tween + }); + }); + + playButton.on("pointerup", () => { + this.scene.start("StartCutScene"); + }); + } + + update() {} +} diff --git a/src/scenes/youDiedScene.ts b/src/scenes/youDiedScene.ts new file mode 100644 index 00000000..a8abb90c --- /dev/null +++ b/src/scenes/youDiedScene.ts @@ -0,0 +1,70 @@ +import Phaser from "phaser"; + +interface YouDiedData { + currentLevelKey: string; + level0State: number; + level1State: number; + level2State: number; + level3State: number; + level0Stars: number; + level1Stars: number; + level2Stars: number; + level3Stars: number; +} + +export default class youDiedScene extends Phaser.Scene { + constructor() { + super({ key: "YouDiedScene" }); + } + + preload() {} + + create(data: YouDiedData) { + const playerDiedText = this.add.text( + this.cameras.main.width / 2, + this.cameras.main.height / 2, + "You Died", + { fontSize: "96px", color: "#8c0615", fontFamily: "Verdana" } + ); + playerDiedText.setOrigin(0.5); + playerDiedText.setDepth(32); + + playerDiedText.setScale(0); + playerDiedText.setAlpha(0); + + const blackBackground = this.add.rectangle( + this.cameras.main.width / 2, + this.cameras.main.height / 2, + this.cameras.main.width, + this.cameras.main.height, + 0x000000 + ); + blackBackground.setDepth(31); + blackBackground.setAlpha(0); + + console.log(data.level0Stars); + // Animate you died text and black background + this.tweens.add({ + targets: [playerDiedText, blackBackground], + scale: 1, + alpha: 1, + duration: 200, + ease: "Bounce", + onComplete: () => { + this.time.delayedCall(1000, () => { + this.scene.stop("YouDiedScene"); + this.scene.start(data.currentLevelKey, { + level0State: data.level0State, + level1State: data.level1State, + level2State: data.level2State, + level3State: data.level3State, + level0Stars: data.level0Stars, + level1Stars: data.level1Stars, + level2Stars: data.level2Stars, + level3Stars: data.level3Stars, + }); + }); + }, + }); + } +} diff --git a/src/scenes/youDiedScene1.ts b/src/scenes/youDiedScene1.ts new file mode 100644 index 00000000..34a01d72 --- /dev/null +++ b/src/scenes/youDiedScene1.ts @@ -0,0 +1,69 @@ +import Phaser from "phaser"; + +interface YouDied1Data { + currentLevelKey: string; + level0State: number; + level1State: number; + level2State: number; + level3State: number; + level0Stars: number; + level1Stars: number; + level2Stars: number; + level3Stars: number; +} + +export default class youDiedScene1 extends Phaser.Scene { + constructor() { + super({ key: "YouDiedScene1" }); + } + + preload() {} + + create(data: YouDied1Data) { + const playerDiedText = this.add.text( + this.cameras.main.width / 2, + this.cameras.main.height / 2, + "You Died", + { fontSize: "96px", color: "#8c0615", fontFamily: "Verdana" } + ); + playerDiedText.setOrigin(0.5); + playerDiedText.setDepth(32); + + playerDiedText.setScale(0); + playerDiedText.setAlpha(0); + + const blackBackground = this.add.rectangle( + this.cameras.main.width / 2, + this.cameras.main.height / 2, + this.cameras.main.width, + this.cameras.main.height, + 0x000000 + ); + blackBackground.setDepth(31); + blackBackground.setAlpha(0); + + // Animate you died text and black background + this.tweens.add({ + targets: [playerDiedText, blackBackground], + scale: 1, + alpha: 1, + duration: 200, + ease: "Bounce", + onComplete: () => { + this.time.delayedCall(1000, () => { + this.scene.stop("YouDiedScene1"); + this.scene.start(data.currentLevelKey, { + level0State: data.level0State, + level1State: data.level1State, + level2State: data.level2State, + level3State: data.level3State, + level0Stars: data.level0Stars, + level1Stars: data.level1Stars, + level2Stars: data.level2Stars, + level3Stars: data.level3Stars, + }); + }); + }, + }); + } +} diff --git a/src/scenes/youDiedScene2.ts b/src/scenes/youDiedScene2.ts new file mode 100644 index 00000000..2801bd2d --- /dev/null +++ b/src/scenes/youDiedScene2.ts @@ -0,0 +1,69 @@ +import Phaser from "phaser"; + +interface YouDied2Data { + currentLevelKey: string; + level0State: number; + level1State: number; + level2State: number; + level3State: number; + level0Stars: number; + level1Stars: number; + level2Stars: number; + level3Stars: number; +} + +export default class youDiedScene2 extends Phaser.Scene { + constructor() { + super({ key: "YouDiedScene2" }); + } + + preload() {} + + create(data: YouDied2Data) { + const playerDiedText = this.add.text( + this.cameras.main.width / 2, + this.cameras.main.height / 2, + "You Died", + { fontSize: "96px", color: "#8c0615", fontFamily: "Verdana" } + ); + playerDiedText.setOrigin(0.5); + playerDiedText.setDepth(32); + + playerDiedText.setScale(0); + playerDiedText.setAlpha(0); + + const blackBackground = this.add.rectangle( + this.cameras.main.width / 2, + this.cameras.main.height / 2, + this.cameras.main.width, + this.cameras.main.height, + 0x000000 + ); + blackBackground.setDepth(31); + blackBackground.setAlpha(0); + + // Animate you died text and black background + this.tweens.add({ + targets: [playerDiedText, blackBackground], + scale: 1, + alpha: 1, + duration: 200, + ease: "Bounce", + onComplete: () => { + this.time.delayedCall(1000, () => { + this.scene.stop("YouDiedScene2"); + this.scene.start(data.currentLevelKey, { + level0State: data.level0State, + level1State: data.level1State, + level2State: data.level2State, + level3State: data.level3State, + level0Stars: data.level0Stars, + level1Stars: data.level1Stars, + level2Stars: data.level2Stars, + level3Stars: data.level3Stars, + }); + }); + }, + }); + } +} diff --git a/src/scenes/youDiedScene3.ts b/src/scenes/youDiedScene3.ts new file mode 100644 index 00000000..d50057e5 --- /dev/null +++ b/src/scenes/youDiedScene3.ts @@ -0,0 +1,69 @@ +import Phaser from "phaser"; + +interface YouDied3Data { + currentLevelKey: string; + level0State: number; + level1State: number; + level2State: number; + level3State: number; + level0Stars: number; + level1Stars: number; + level2Stars: number; + level3Stars: number; +} + +export default class youDiedScene3 extends Phaser.Scene { + constructor() { + super({ key: "YouDiedScene3" }); + } + + preload() {} + + create(data: YouDied3Data) { + const playerDiedText = this.add.text( + this.cameras.main.width / 2, + this.cameras.main.height / 2, + "You Died", + { fontSize: "96px", color: "#8c0615", fontFamily: "Verdana" } + ); + playerDiedText.setOrigin(0.5); + playerDiedText.setDepth(32); + + playerDiedText.setScale(0); + playerDiedText.setAlpha(0); + + const blackBackground = this.add.rectangle( + this.cameras.main.width / 2, + this.cameras.main.height / 2, + this.cameras.main.width, + this.cameras.main.height, + 0x000000 + ); + blackBackground.setDepth(31); + blackBackground.setAlpha(0); + + // Animate you died text and black background + this.tweens.add({ + targets: [playerDiedText, blackBackground], + scale: 1, + alpha: 1, + duration: 200, + ease: "Bounce", + onComplete: () => { + this.time.delayedCall(1000, () => { + this.scene.stop("YouDiedScene3"); + this.scene.start(data.currentLevelKey, { + level0State: data.level0State, + level1State: data.level1State, + level2State: data.level2State, + level3State: data.level3State, + level0Stars: data.level0Stars, + level1Stars: data.level1Stars, + level2Stars: data.level2Stars, + level3Stars: data.level3Stars, + }); + }); + }, + }); + } +}