diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..690ed14a Binary files /dev/null and b/.DS_Store differ diff --git a/README.md b/README.md index e240001b..73dae6b0 100644 --- a/README.md +++ b/README.md @@ -1 +1,61 @@ -REPLACE THIS WITH A DESCRIPTION OF YOUR GAME (in the README.md file). +# Game Name + +Cross the Pond + +# Team Color + +Brown + +# Developers + +- Rachel Sison (rsison@udel.edu) +- Lizzy Kalfas (lizzykal@udel.edu) + +# Blurb + +Oh no! You and your duck family have been seperated! You need to find your way back to each family member and bring them all together. Using Dijkstra's Algorithm you will find the shortest path of stepping stones across a pond to get to a family member on the other side. Complete each level to gain another member of your family back. Pass all five levels to reunite with your mother and become one big happy duck family again. + +# Basic Instructions + +Find the shorteest pathusing Dijkstra's Algorithm. Click each stone on your path starting with the stone labeled start + +# Screenshot + +![Screenshot of level 1](/docs/large.png) + +# Gameplay Video + +[Game demo](https://www.youtube.com/watch?v=pC5k8cKE9ow) + +# Educational Game Design Document + +Link to our [egdd](docs/egdd.md) + +# Credits + +Pond img: https://www.google.com/url?sa=i&url=https%3A%2F%2Fstock.adobe.com%2Fsearch%3Fk%3Dcartoon%2Bpond&psig=AOvVaw3vGnAdxl3dVewlfHfboCob&ust=1712347180978000&source=images&cd=vfe&opi=89978449&ved=0CBIQjRxqFwoTCKDMor-sqYUDFQAAAAAdAAAAABAE + +Duck img: https://www.google.com/url?sa=i&url=https%3A%2F%2Fwww.freepik.com%2Fpremium-vector%2Fcute-duck-cartoon-vector-illustration_44380447.htm&psig=AOvVaw1GgLt8tCokW8d_WevV3Gkl&ust=1712347485292000&source=images&cd=vfe&opi=89978449&ved=0CBIQjRxqFwoTCJDvje6tqYUDFQAAAAAdAAAAABAE + +Stepping Stones:https://www.google.com/url?sa=i&url=https%3A%2F%2Felchighland.com%2Fstepping-stones-2%2F&psig=AOvVaw0mxlnd_Qc6akzH6ufxYGhL&ust=1712350228058000&source=images&cd=vfe&opi=89978449&ved=0CBIQjRxqFwoTCODi7-q3qYUDFQAAAAAdAAAAABAE + +Music: +Pass audio: + +https://www.myinstants.com/en/instant/rubber-duckie/?utm_source=copy&utm_medium=share + +Fail audio: + +https://www.fesliyanstudios.com/royalty-free-sound-effects-download/sad-trombone-242 + +Background music: + +https://pixabay.com/music/search/genre/cartoons/ + +Clicking on each stone sound: + +https://www.myinstants.com/en/instant/duck-toy-sound-18073/?utm_source=copy&utm_medium=share + +Winning audio: + +https://pixabay.com/sound-effects/search/happy/ diff --git a/assets/.DS_Store b/assets/.DS_Store new file mode 100644 index 00000000..fe58b9d2 Binary files /dev/null and b/assets/.DS_Store differ diff --git a/assets/audio/sadTrombone.mp3 b/assets/audio/sadTrombone.mp3 new file mode 100644 index 00000000..eabcd79f Binary files /dev/null and b/assets/audio/sadTrombone.mp3 differ diff --git a/assets/audio/stepstone.mp3 b/assets/audio/stepstone.mp3 new file mode 100644 index 00000000..7cdf2cb9 Binary files /dev/null and b/assets/audio/stepstone.mp3 differ diff --git a/assets/audio/theme.mp3 b/assets/audio/theme.mp3 new file mode 100644 index 00000000..5ff9cdf1 Binary files /dev/null and b/assets/audio/theme.mp3 differ diff --git a/assets/audio/win.mp3 b/assets/audio/win.mp3 new file mode 100644 index 00000000..3cb6f6d6 Binary files /dev/null and b/assets/audio/win.mp3 differ diff --git a/assets/audio/winning.mp3 b/assets/audio/winning.mp3 new file mode 100644 index 00000000..68dad2d8 Binary files /dev/null and b/assets/audio/winning.mp3 differ diff --git a/assets/duck.png b/assets/duck.png new file mode 100644 index 00000000..df344f82 Binary files /dev/null and b/assets/duck.png differ diff --git a/assets/favicon.ico b/assets/favicon.ico index f268a177..bc572249 100644 Binary files a/assets/favicon.ico and b/assets/favicon.ico differ diff --git a/assets/img/Storyboard1.jpg b/assets/img/Storyboard1.jpg new file mode 100644 index 00000000..5bccd742 Binary files /dev/null and b/assets/img/Storyboard1.jpg differ diff --git a/assets/img/Storyboard2.JPG b/assets/img/Storyboard2.JPG new file mode 100644 index 00000000..fc46ecf3 Binary files /dev/null and b/assets/img/Storyboard2.JPG differ diff --git a/assets/img/mutebutton.png b/assets/img/mutebutton.png new file mode 100644 index 00000000..a811d235 Binary files /dev/null and b/assets/img/mutebutton.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/img/unmutebutton.png b/assets/img/unmutebutton.png new file mode 100644 index 00000000..8b6de832 Binary files /dev/null and b/assets/img/unmutebutton.png differ diff --git a/assets/pond.png b/assets/pond.png new file mode 100644 index 00000000..da1abbc5 Binary files /dev/null and b/assets/pond.png differ diff --git a/assets/stone.png b/assets/stone.png new file mode 100644 index 00000000..ccec66c6 Binary files /dev/null and b/assets/stone.png differ diff --git a/docs/.DS_Store b/docs/.DS_Store new file mode 100644 index 00000000..5008ddfc Binary files /dev/null and b/docs/.DS_Store differ diff --git a/docs/egdd.md b/docs/egdd.md index 51ed6536..d5f9c7a8 100644 --- a/docs/egdd.md +++ b/docs/egdd.md @@ -1 +1,165 @@ -REPLACE THIS TEXT WITH YOUR EGDD MARKDOWN. +--- + +waltz: +title: EGDD Example - Cross the Pond +meta: +version: 0.0.3 +edd authors: - Rachel Sison - Elizabeth Kalfas + +--- + +# Cross the Pond + +# Overview + +## Elevator Pitch + +You have this cute duck that has been separated from its duck family by a pond. You aim to get to the other side of the pond using the stepping stones. + +## Influences (Brief) + +- Index the Cat: + - Medium: Game + - Explanation: The Index the Cat game immediately advances to the next level once you beat the previous one, which we’re implementing into ours. +- Crossy Road: + - Medium: phone game + - Explanation: Our game applies the same concept of getting to the other side. +- Quick Route: + - Medium: iPhone Game + - Explanation: The Quick Route game objective is to connect bubbles using the shortest path, which is similar to the objective of our game. + +## Core Gameplay Mechanics (Brief) + +- click stones to select a path for your duck +- click submit to check if the path chosen matches the shortest path +- pass level when you find the shortest path across +- if you cannot find the shortest path it will prompt you to try again +- move on to a higher level when the path is found +- when all levels are complete show the winner screen + +# Learning Aspects + +## Learning Domains + +Introductory Data Structures + +## Target Audiences + +- Students being introduced to graphing algorithms +- Should be appropriate for adults who are young at heart. + +## Target Contexts + +- This would be assigned as supplementary practice in a course formally teaching the Dijksras Algorithm. + +## Learning Objectives + +- By the end of the lesson, players will be able to find the shortest path using Dijkstra’s Algorithm + +## Prerequisite Knowledge + +- Prior to the game, players need to be able to determine the distance between nodes in a graph +- Prior to the game, players should have a basic understanding of Dijkstra’s algorithm. + +## Assessment Measures + +A short pre-test and matching post-test should be designed to assess student learning. + +- Given a graph, calculate the total distance of a path +- Given a graph, determine the shortest path across +- Given a graph, determine if a certain node is stopped during the shortest path + +# What sets this project apart? + +- Most introductory coding activities focus on code-writing, this can have a cute animal and graphics +- The gameplay mechanics of having the duck go to each rock to meet the desired goal of identifying the shortest possible path + +# Player Interaction Patterns and Modes + +## Player Interaction Pattern + +This is a game for one person, they click on the mouse to move to different rocks in the pond. + +## Player Modes + +- Single-player: You repeatedly advance through rounds and levels until you reach the end. + +# Gameplay Objectives + +- Reunite with your family: When the shortest path is found you will be able to get across the pond and reunite with your family +- Advance to the next level: Once the shortest possible path is found you advance to advance to the next level +- Complete all the levels: Get through all the levels to win the game. + +# Procedures/Actions + +You click the mouse through the regions of the screen. + +Some of those regions are different nodes representing a graph + +# Rules + +- If the player clicks on the wrong rocks thus submitting the wrong path, then the player loses +- If the player clicks the correct rocks, creating the correct path, then the duck is happily united with a member of its family + -If the player gets it wrong, they will be given a new path with diffrent values + +# Objects/Entities + +- There's a pond with rocks in it. +- There's a graph implemented using the rocks. +- Some instructions appear periodically at the bottom of the screen. + +## Core Gameplay Mechanics (Detailed) + +- Clicking/selecting rocks: You click/select multiple nodes (rocks) to complete the shortest path possible for the duck to get across the pond. Once the desired rocks are selected, you submit your answer. +- Correct path: If you correctly select/click the right rocks, essentially submitting the shortest possible path, the duck will be reunited with his happy family. Then the game will advance to the next round, switching up the numbers on the nodes to make it more difficult. +- Incorrect path: After selecting and submitting a path, if it is incorrect the duck will make a sad face/noise +- Various levels of increasing difficulty: There will be 5 levels each increasing in difficulty, the first level will just increase the number of nodes(stones) and connections between the nodes, and the last level will require the duck to stop at a certain stone before getting to the other side. +- All levels complete: After you complete all the levels, it displays a victory message. The ducks will all be together again. + +## Feedback + +- Text on the screen shows the current path selected as well as any previous paths submitted +- If an incorrect path is submitted 3 times a text showing the correct path will pop up. +- When you advance to a new level, the level text changes +- When you win the game, the ducks are all united. + +# Story and Gameplay + +## Presentation of Rules + +- Text shown on the main game screen explains the objective and interaction instructions + +## Presentation of Content + +The game does not attempt to teach you Dijkstra’s Algorithm. This should be presented as supplementary content to help reinforce the topic. + +## Story (Brief) + +You are a cute little duck that has been separated from the rest of your duck family on the other side of the pond. To get back across the pond you have to use the stones as steps to find the shortest path back to reunite with your family. After you complete all the levels you win + +# Assets Needed + +## Aethestics + +The aesthetics should be happy and cartoonish. The game should have a light-hearted feel. This should encourage the player to feel okay with their mistakes even as they try to do better. + +## Graphical + +- Characters List + - Duck +- Textures: N/A +- Environment Art/Textures: + - Pond: + - Stone: + +## Audio + +- Music List (Ambient sound) + - General gameplay: light cartoonish elevator music type + - Duck reunification: cheer/happy quacks + - Incorrect path: womp womp + +## Story Boards + +!(/assets/images/Storyboard1.jpg) +!(/assets/images/Storyboard2.JPG) diff --git a/docs/large.png b/docs/large.png new file mode 100644 index 00000000..257ffbe9 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..dafdd5cf Binary files /dev/null and b/docs/small.png differ 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/pwa/icons/android-chrome-192x192.png b/pwa/icons/android-chrome-192x192.png new file mode 100644 index 00000000..55c61e25 Binary files /dev/null and b/pwa/icons/android-chrome-192x192.png differ diff --git a/pwa/icons/android-chrome-512x512.png b/pwa/icons/android-chrome-512x512.png new file mode 100644 index 00000000..cecdafbf Binary files /dev/null and b/pwa/icons/android-chrome-512x512.png differ diff --git a/pwa/icons/icons-192.png b/pwa/icons/icons-192.png deleted file mode 100644 index 16bebab8..00000000 Binary files a/pwa/icons/icons-192.png and /dev/null differ diff --git a/pwa/icons/icons-512.png b/pwa/icons/icons-512.png deleted file mode 100644 index 0e536097..00000000 Binary files a/pwa/icons/icons-512.png and /dev/null differ diff --git a/pwa/manifest.json b/pwa/manifest.json index fa202a58..c7f0f431 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": "Cross the Pone", + "name": "Cross the POnd", "icons": [ { "src": "./icons/icons-192.png", diff --git a/src/config.ts b/src/config.ts index 9776bc5c..6f4942fd 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,6 +1,22 @@ import Phaser from "phaser"; -import MainScene from "./scenes/mainScene"; +import levelOne from "./scenes/levelOne"; +import levelOnePass from "./scenes/levelOnePass"; +import levelTwo from "./scenes/levelTwo"; +import levelTwoPass from "./scenes/levelTwoPass"; +import levelThree from "./scenes/levelThree"; +import levelThreePass from "./scenes/levelThreePass"; +import levelFour from "./scenes/levelFour"; +import levelFourPass from "./scenes/levelFourPass"; +import levelFive from "./scenes/levelFive"; +import endScreen from "./scenes/endScreen"; +import mainMenu from "./scenes/mainMenu"; import PreloadScene from "./scenes/preloadScene"; +import levelTwoFail from "./scenes/levelTwoFail"; +import levelOneFail from "./scenes/levelOneFail"; +import levelThreeFail from "./scenes/levelThreeFail"; +import levelFourFail from "./scenes/levelFourFail"; +import levelFiveFail from "./scenes/levelFiveFail"; +import tutorial from "./scenes/tutorial"; const DEFAULT_WIDTH = 1280; const DEFAULT_HEIGHT = 720; @@ -17,7 +33,26 @@ export const CONFIG = { width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT, }, - scene: [PreloadScene, MainScene], + scene: [ + PreloadScene, + mainMenu, + levelOne, + levelTwo, + levelThree, + levelFour, + levelFive, + levelOnePass, + levelTwoPass, + levelThreePass, + levelFourPass, + endScreen, + levelOneFail, + levelTwoFail, + levelThreeFail, + levelFourFail, + levelFiveFail, + tutorial, + ], physics: { default: "arcade", arcade: { diff --git a/src/scenes/endScreen.ts b/src/scenes/endScreen.ts new file mode 100644 index 00000000..ee2e48a6 --- /dev/null +++ b/src/scenes/endScreen.ts @@ -0,0 +1,70 @@ +import Phaser from "phaser"; + +export default class endScreen extends Phaser.Scene { + private ducks: Phaser.GameObjects.Image[] = []; + constructor() { + super({ key: "endScreen" }); + } + + preload() { + this.load.audio("end", ["assets/audio/winning.mp3"]); + } + create() { + const { width, height } = this.sys.game.config; + const screenWidth = Number(width); + const screenHeight = Number(height); + + this.add + .image(screenWidth / 2, screenHeight / 2, "pond") + .setDisplaySize(screenWidth, screenHeight); + + const celebrationMusic = this.sound.add("end", { + loop: true, + volume: 0.5, + }); + celebrationMusic.play(); + + this.add + .text(screenWidth / 2, 50, "Congratulations! You Won!", { + fontFamily: "Arial Black", + fontSize: "40px", + color: "#ffffe0", + }) + .setOrigin(0.5, 0) + .setStroke("#ffd700", 16); + + for (let i = 0; i < 5; i++) { + const duck = this.add + .image( + Phaser.Math.Between(100, screenWidth - 100), + Phaser.Math.Between(100, screenHeight - 100), + "duck" + ) + .setScale(0.6); + this.ducks.push(duck); + this.tweens.add({ + targets: duck, + y: { value: "-=100", duration: 500, ease: "Power2" }, + yoyo: true, + repeat: -1, + delay: i * 100, + }); + } + + const backButton = this.add + .text(screenWidth - 50, screenHeight - 50, "Back to Menu", { + fontFamily: "Arial Black", + fontSize: "30px", + color: "#ffffe0", + }) + .setOrigin(1) + .setStroke("#ffd700", 16) + .setInteractive(); + + backButton.on("pointerdown", () => { + celebrationMusic.stop(); + this.scene.start("mainMenu"); + }); + } + update() {} +} diff --git a/src/scenes/levelFive.ts b/src/scenes/levelFive.ts new file mode 100644 index 00000000..a869a8cf --- /dev/null +++ b/src/scenes/levelFive.ts @@ -0,0 +1,482 @@ +import Phaser from "phaser"; +export default class levelFive extends Phaser.Scene { + private score: number = 0; + private scoreText?: Phaser.GameObjects.Text; + private isMuted: boolean = false; + private muteButton!: Phaser.GameObjects.Image; + private stepStoneSound!: Phaser.Sound.BaseSound; + + constructor() { + super({ key: "levelFive" }); + } + + preload() { + this.load.image("mute", "assets/img/mutebutton.png"); + this.load.image("unmute", "assets/img/unmutebutton.png"); + this.load.audio("stepstone", ["assets/audio/stepstone.mp3"]); + } + + create() { + const { width, height } = this.sys.game.config; + const screenWidth: number = Number(width); + const screenHeight: number = Number(height); + + this.stepStoneSound = this.sound.add("stepstone"); + + this.add + .image(screenWidth / 2, screenHeight / 2, "pond") + .setDisplaySize(screenWidth, screenHeight); + + const levelName = this.add.text(25, 25, "Level 5", { + fontFamily: "Arial Black", + fontSize: "40px", + color: "#ffffe0", + }); + levelName.setStroke("#ffd700", 16); + + this.muteButton = this.add + .image(1150, 120, "unmute") + .setScale(0.15) + .setInteractive(); + this.muteButton.on("pointerdown", this.toggleMute, this); + + this.add.image(150, 500, "duck").setScale(0.4); + this.add.image(50, 500, "duck").setScale(0.4); + this.add.image(80, 550, "duck").setScale(0.4); + this.add.image(150, 550, "duck").setScale(0.4); + this.add.image(950, 250, "duck").setScale(0.4); + + // connection lines + const graphics = this.add.graphics(); + graphics.lineStyle(2, 0x000000); + + graphics.beginPath(); + graphics.moveTo(500, 400); + graphics.lineTo(275, 435); + graphics.lineTo(400, 525); + graphics.lineTo(650, 600); + graphics.lineTo(740, 480); + graphics.lineTo(790, 375); + graphics.lineTo(900, 450); + graphics.lineTo(850, 575); + graphics.lineTo(740, 480); + graphics.lineTo(400, 525); + graphics.lineTo(500, 400); + graphics.lineTo(650, 600); + graphics.lineTo(740, 480); + graphics.lineTo(500, 400); + graphics.lineTo(790, 375); + + graphics.strokePath(); + function generateValues(): number[] { + const randomList: number[] = []; + for (let i = 0; i < 15; i++) { + const randomNumber = + Math.floor(Math.random() * (10 - 1 + 1)) + 1; + randomList.push(randomNumber); + } + return randomList; + } + + const values = generateValues(); + let paths: number[][] = [ + [values[0] + values[8] + values[9]], + [values[0] + values[7] + values[2] + values[12] + values[11]], + [values[0] + values[7] + values[2] + values[3] + values[9]], + [values[0] + values[6] + values[10] + values[12] + values[11]], + [ + values[0] + + values[6] + + values[1] + + values[2] + + values[3] + + values[9], + ], + [values[0] + values[4] + values[3] + values[9]], + [values[0] + values[4] + values[12] + values[11]], + [values[5] + values[1] + values[2] + values[3] + values[9]], + [values[5] + values[1] + values[2] + values[12] + values[11]], + [values[5] + values[6] + values[4] + values[3] + values[9]], + [values[5] + values[6] + values[4] + values[12] + values[11]], + [values[5] + values[10] + values[3] + values[9]], + [values[5] + values[10] + values[12] + values[11]], + [values[5] + values[6] + values[8] + values[9]], + ]; + function getPathValue(path: number[]): number { + let value: number = 0; + for (let v of path) { + value += v; + } + return value; + } + + function shortestPath(paths: number[][]): { + path: number[]; + value: number; + } { + let shortestPath: number[] = paths[0]; // Assume the first path is the shortest initially + let shortestLength: number = getPathValue(paths[0]); // Get the value of the first path + + for (let path of paths) { + let value: number = getPathValue(path); + if (value < shortestLength) { + shortestPath = path; + shortestLength = value; + } + } + + return { path: shortestPath, value: shortestLength }; + } + + let correct = shortestPath(paths); + + console.log(values); + this.add.text(387, 420, values[0].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(480, 535, values[1].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(695, 540, values[2].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(765, 427, values[3].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(600, 428, values[4].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(335, 463, values[5].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(450, 462, values[6].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(576, 479, values[7].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(645, 387, values[8].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(845, 400, values[9].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(650, 487, values[10].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(875, 512, values[11].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(805, 512, values[12].toString(), { + fontSize: "20px", + color: "000000", + }); + // add stones + let duck1 = this.add.image(150, 500, "duck"); + let duck2 = this.add.image(950, 250, "duck"); + duck2 + .setScale(0.4) + .setInteractive() + .on("pointerdown", () => { + if (duck1.x == 275) { + this.score += 0; + duck1 + .setX(duck2.x + 10) + .setY(duck2.y + 10) + .setDepth(1); + } + if (duck1.x == 750) { + this.score += 0; + duck1 + .setX(duck2.x + 10) + .setY(duck2.y + 10) + .setDepth(1); + } + this.scoreText?.setText("Path Length" + this.score); + //if (this.score > 3) { + + //} + }); + duck1.setScale(0.4); + + const restart = this.add.text(1240, 25, "Restart", { + fontFamily: "Arial Black", + fontSize: "40px", + color: "#ffffe0", + }); + restart.setStroke("#ffd700", 16); + restart.setOrigin(1, 0).setInteractive(); + restart.on("pointerdown", () => { + duck1.setX(150).setY(450).setDepth(1); + this.score = 0; + this.scoreText?.setText("Path Length: " + this.score); + }); + + let stone1 = this.add + .image(500, 400, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 275) { + this.score = values[0]; + duck1.setX(stone1.x).setY(stone1.y).setDepth(1); + } + if (duck1.x == 400) { + this.score += values[6]; + duck1.setX(stone1.x).setY(stone1.y).setDepth(1); + } + if (duck1.x == 650) { + this.score += values[7]; + duck1.setX(stone1.x).setY(stone1.y).setDepth(1); + } + if (duck1.x == 790) { + this.score += values[8]; + duck1.setX(stone1.x).setY(stone1.y).setDepth(1); + } + if (duck1.x == 740) { + this.score += values[4]; + duck1.setX(stone1.x).setY(stone1.y).setDepth(1); + } + this.scoreText?.setText("Path Length: " + this.score); + }) + .on("pointerover", () => stone1.setScale(0.5)) + .on("pointerout", () => stone1.setScale(0.4)); + + let stone2 = this.add + .image(275, 435, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 500) { + duck1.setX(stone2.x).setY(stone2.y).setDepth(1); + this.score += values[0]; + } + if (duck1.x == 400) { + this.score += values[5]; + duck1.setX(stone2.x).setY(stone2.y).setDepth(1); + } + }) + .on("pointerover", () => stone2.setScale(0.5)) + .on("pointerout", () => stone2.setScale(0.4)); + + this.add.text(220, 450, "Start"); + + let stone3 = this.add + .image(650, 600, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 500) { + duck1.setX(stone3.x).setY(stone3.y).setDepth(1); + this.score += values[7]; + } + if (duck1.x == 400) { + this.score += values[1]; + duck1.setX(stone3.x).setY(stone3.y).setDepth(1); + } + if (duck1.x == 740) { + this.score += values[2]; + duck1.setX(stone3.x).setY(stone3.y).setDepth(1); + } + }) + .on("pointerover", () => stone3.setScale(0.5)) + .on("pointerout", () => stone3.setScale(0.4)); + + let stone4 = this.add + .image(740, 480, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 500) { + duck1.setX(stone4.x).setY(stone4.y).setDepth(1); + this.score += values[4]; + } + if (duck1.x == 650) { + this.score += values[2]; + duck1.setX(stone4.x).setY(stone4.y).setDepth(1); + } + if (duck1.x == 790) { + this.score += values[3]; + duck1.setX(stone4.x).setY(stone4.y).setDepth(1); + } + if (duck1.x == 850) { + this.score += values[12]; + duck1.setX(stone4.x).setY(stone4.y).setDepth(1); + } + }) + .on("pointerover", () => stone4.setScale(0.5)) + .on("pointerout", () => stone4.setScale(0.4)); + + let stone5 = this.add + .image(790, 375, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 500) { + duck1.setX(stone5.x).setY(stone5.y).setDepth(1); + this.score += values[8]; + } + if (duck1.x == 740) { + this.score += values[3]; + duck1.setX(stone5.x).setY(stone5.y).setDepth(1); + } + if (duck1.x == 900) { + this.score += values[9]; + duck1.setX(stone5.x).setY(stone5.y).setDepth(1); + } + }) + .on("pointerover", () => stone5.setScale(0.5)) + .on("pointerout", () => stone5.setScale(0.4)); + + let stone6 = this.add + .image(400, 525, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 275) { + duck1.setX(stone6.x).setY(stone6.y).setDepth(1); + this.score += values[5]; + } + if (duck1.x == 500) { + this.score += values[6]; + duck1.setX(stone6.x).setY(stone6.y).setDepth(1); + } + if (duck1.x == 740) { + this.score += values[10]; + duck1.setX(stone6.x).setY(stone6.y).setDepth(1); + } + if (duck1.x == 650) { + this.score += values[1]; + duck1.setX(stone6.x).setY(stone6.y).setDepth(1); + } + }) + .on("pointerover", () => stone6.setScale(0.5)) + .on("pointerout", () => stone6.setScale(0.4)); + + let stone7 = this.add + .image(900, 450, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 790) { + duck1.setX(stone7.x).setY(stone7.y).setDepth(1); + this.score += values[9]; + } + if (duck1.x == 850) { + this.score += values[11]; + duck1.setX(stone7.x).setY(stone7.y).setDepth(1); + } + if (this.score === correct.value) { + this.sound.stopAll; + this.scene.start("endScreen"); + } else { + this.scene.start("levelFiveFail"); + this.score = 0; + } + }) + .on("pointerover", () => stone7.setScale(0.5)) + .on("pointerout", () => stone7.setScale(0.4)); + + this.add.text(900, 400, "END"); + + let stone8 = this.add + .image(850, 575, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 740) { + duck1.setX(stone8.x).setY(stone8.y).setDepth(1); + this.score += values[12]; + } + if (duck1.x == 900) { + this.score += values[11]; + duck1.setX(stone8.x).setY(stone8.y).setDepth(1); + } + }) + .on("pointerover", () => stone8.setScale(0.5)) + .on("pointerout", () => stone8.setScale(0.4)); + + const stones = [ + stone1, + stone2, + stone3, + stone4, + stone5, + stone6, + stone7, + stone8, + ]; + stones.forEach((stone) => { + stone.setInteractive().on("pointerdown", () => { + duck1.setPosition(stone.x, stone.y).setDepth(1); + if (duck1.x == 275 || duck1.x == 750) { + this.score += 0; + } else if (duck1.x == 700) { + this.score += 0; + } else { + this.score += 0; + } + this.scoreText?.setText("Path Length: " + this.score); + }); + }); + this.scoreText = this.add.text(25, 70, "Path Length: " + this.score, { + fontFamily: "Arial Black", + fontSize: "40px", + color: "#ffffe0", + }); + this.scoreText.setStroke("#ffd700", 16); + } + + private playStepStoneSound() { + if (!this.isMuted) { + this.stepStoneSound.play(); + } + } + + toggleMute() { + this.isMuted = !this.isMuted; + + if (this.isMuted) { + this.sound.mute = true; + this.muteButton.setTexture("mute"); + } else { + this.sound.mute = false; + this.muteButton.setTexture("unmute"); + } + } +} diff --git a/src/scenes/levelFiveFail.ts b/src/scenes/levelFiveFail.ts new file mode 100644 index 00000000..044243f9 --- /dev/null +++ b/src/scenes/levelFiveFail.ts @@ -0,0 +1,69 @@ +import Phaser from "phaser"; + +export default class levelOnePass extends Phaser.Scene { + constructor() { + super({ key: "levelFiveFail", active: false }); + } + + preload() { + this.load.audio("fail", ["assets/audio/sadTrombone.mp3"]); + } + + create() { + const { width, height } = this.sys.game.config; + const screenWidth: number = Number(width); + const screenHeight: number = Number(height); + // Add a semi-transparent background rectangle to dim the game scene + const background = this.add.rectangle( + 0, + 0, + screenWidth, + screenHeight, + 0x000000, + 0.5 + ); + background.setOrigin(0, 0); + + const failSound = this.sound.add("fail"); + failSound.play(); + + // Add popup content + const popupText = this.add.text( + screenWidth / 2, + screenHeight / 2, + "That wasn't the shortest path.", + { fontSize: "32px", color: "#fff" } + ); + popupText.setOrigin(0.5); + this.add + .text( + screenWidth / 2, + screenHeight / 2 + 30, + "Remember to use Dijkstra's Algorithm to find the shortest path", + { fontSize: "32px", color: "#fff" } + ) + .setOrigin(0.5); + + // make buttons to change to level two + this.add.image(700, 200, "duck").setScale(0.6); + this.add.image(550, 200, "duck").setScale(0.6); + + const tryAgain = this.add + .text(500, 500, "Try a different problem", { + color: "#ffffff", + fontSize: "32px", + fixedWidth: 575, + backgroundColor: "#87ceeb", + }) + .setPadding(32) + .setOrigin(0.2); + + tryAgain.setInteractive({ useHandCursor: true }); + + console.log("attaching listener to button"); + tryAgain.on("pointerdown", () => { + console.log("Button clicked!"); + this.scene.start("levelFive"); + }); + } +} diff --git a/src/scenes/levelFour.ts b/src/scenes/levelFour.ts new file mode 100644 index 00000000..08627b93 --- /dev/null +++ b/src/scenes/levelFour.ts @@ -0,0 +1,476 @@ +import Phaser from "phaser"; +export default class levelFour extends Phaser.Scene { + private score: number = 0; + private scoreText?: Phaser.GameObjects.Text; + private isMuted: boolean = false; + private muteButton!: Phaser.GameObjects.Image; + private stepStoneSound!: Phaser.Sound.BaseSound; + + constructor() { + super({ key: "levelFour" }); + } + + preload() { + this.load.image("mute", "assets/img/mutebutton.png"); + this.load.image("unmute", "assets/img/unmutebutton.png"); + this.load.audio("stepstone", ["assets/audio/stepstone.mp3"]); + } + + create() { + const { width, height } = this.sys.game.config; + const screenWidth: number = Number(width); + const screenHeight: number = Number(height); + + this.stepStoneSound = this.sound.add("stepstone"); + + this.add + .image(screenWidth / 2, screenHeight / 2, "pond") + .setDisplaySize(screenWidth, screenHeight); + + const levelName = this.add.text(25, 25, "Level 4", { + fontFamily: "Arial Black", + fontSize: "40px", + color: "#ffffe0", + }); + levelName.setStroke("#ffd700", 16); + + const restart = this.add.text(1240, 25, "Restart", { + fontFamily: "Arial Black", + fontSize: "40px", + color: "#ffffe0", + }); + restart.setStroke("#ffd700", 16); + restart.setOrigin(1, 0).setInteractive(); + restart.on("pointerdown", () => { + this.score = 0; + this.scene.start("levelFour"); + }); + + this.muteButton = this.add + .image(1150, 120, "unmute") + .setScale(0.15) + .setInteractive(); + this.muteButton.on("pointerdown", this.toggleMute, this); + + this.add.image(50, 500, "duck").setScale(0.4); + this.add.image(80, 550, "duck").setScale(0.4); + this.add.image(150, 550, "duck").setScale(0.4); + this.add.image(950, 250, "duck").setScale(0.4); + + // connection lines + const graphics = this.add.graphics(); + graphics.lineStyle(2, 0x000000); + + graphics.beginPath(); + graphics.moveTo(500, 400); + graphics.lineTo(275, 435); + graphics.lineTo(400, 525); + graphics.lineTo(650, 600); + graphics.lineTo(740, 480); + graphics.lineTo(790, 375); + graphics.lineTo(900, 450); + graphics.lineTo(850, 575); + graphics.lineTo(740, 480); + graphics.lineTo(400, 525); + graphics.lineTo(500, 400); + graphics.lineTo(650, 600); + graphics.lineTo(740, 480); + graphics.lineTo(500, 400); + graphics.lineTo(790, 375); + + graphics.strokePath(); + function generateValues(): number[] { + const randomList: number[] = []; + for (let i = 0; i < 15; i++) { + const randomNumber = + Math.floor(Math.random() * (10 - 1 + 1)) + 1; + randomList.push(randomNumber); + } + return randomList; + } + + const values = generateValues(); + let paths: number[][] = [ + [values[0] + values[8] + values[9]], + [values[0] + values[7] + values[2] + values[12] + values[11]], + [values[0] + values[7] + values[2] + values[3] + values[9]], + [values[0] + values[6] + values[10] + values[12] + values[11]], + [ + values[0] + + values[6] + + values[1] + + values[2] + + values[3] + + values[9], + ], + [values[0] + values[4] + values[3] + values[9]], + [values[0] + values[4] + values[12] + values[11]], + [values[5] + values[1] + values[2] + values[3] + values[9]], + [values[5] + values[1] + values[2] + values[12] + values[11]], + [values[5] + values[6] + values[4] + values[3] + values[9]], + [values[5] + values[6] + values[4] + values[12] + values[11]], + [values[5] + values[10] + values[3] + values[9]], + [values[5] + values[10] + values[12] + values[11]], + [values[5] + values[6] + values[8] + values[9]], + ]; + function getPathValue(path: number[]): number { + let value: number = 0; + for (let v of path) { + value += v; + } + return value; + } + + function shortestPath(paths: number[][]): { + path: number[]; + value: number; + } { + let shortestPath: number[] = paths[0]; // Assume the first path is the shortest initially + let shortestLength: number = getPathValue(paths[0]); // Get the value of the first path + + for (let path of paths) { + let value: number = getPathValue(path); + if (value < shortestLength) { + shortestPath = path; + shortestLength = value; + } + } + + return { path: shortestPath, value: shortestLength }; + } + + let correct = shortestPath(paths); + + console.log(values); + this.add.text(387, 420, values[0].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(480, 535, values[1].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(695, 540, values[2].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(765, 427, values[3].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(600, 428, values[4].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(335, 463, values[5].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(450, 462, values[6].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(576, 479, values[7].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(645, 387, values[8].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(845, 400, values[9].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(650, 487, values[10].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(875, 512, values[11].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(805, 512, values[12].toString(), { + fontSize: "20px", + color: "000000", + }); + // add stones + let duck1 = this.add.image(150, 500, "duck"); + let duck2 = this.add.image(950, 250, "duck"); + duck2 + .setScale(0.4) + .setInteractive() + .on("pointerdown", () => { + if (duck1.x == 275) { + this.score += 0; + duck1 + .setX(duck2.x + 10) + .setY(duck2.y + 10) + .setDepth(1); + } + if (duck1.x == 750) { + this.score += 0; + duck1 + .setX(duck2.x + 10) + .setY(duck2.y + 10) + .setDepth(1); + } + this.scoreText?.setText("Path Length" + this.score); + }); + duck1.setScale(0.4); + + let stone1 = this.add + .image(500, 400, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 275) { + this.score = values[0]; + duck1.setX(stone1.x).setY(stone1.y).setDepth(1); + } + if (duck1.x == 400) { + this.score += values[6]; + duck1.setX(stone1.x).setY(stone1.y).setDepth(1); + } + if (duck1.x == 650) { + this.score += values[7]; + duck1.setX(stone1.x).setY(stone1.y).setDepth(1); + } + if (duck1.x == 790) { + this.score += values[8]; + duck1.setX(stone1.x).setY(stone1.y).setDepth(1); + } + if (duck1.x == 740) { + this.score += values[4]; + duck1.setX(stone1.x).setY(stone1.y).setDepth(1); + } + this.scoreText?.setText("Path Length: " + this.score); + }) + .on("pointerover", () => stone1.setScale(0.5)) + .on("pointerout", () => stone1.setScale(0.4)); + + let stone2 = this.add + .image(275, 435, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 500) { + duck1.setX(stone2.x).setY(stone2.y).setDepth(1); + this.score += values[0]; + } + if (duck1.x == 400) { + this.score += values[5]; + duck1.setX(stone2.x).setY(stone2.y).setDepth(1); + } + }) + .on("pointerover", () => stone2.setScale(0.5)) + .on("pointerout", () => stone2.setScale(0.4)); + + this.add.text(220, 450, "Start"); + + let stone3 = this.add + .image(650, 600, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 500) { + duck1.setX(stone3.x).setY(stone3.y).setDepth(1); + this.score += values[7]; + } + if (duck1.x == 400) { + this.score += values[1]; + duck1.setX(stone3.x).setY(stone3.y).setDepth(1); + } + if (duck1.x == 740) { + this.score += values[2]; + duck1.setX(stone3.x).setY(stone3.y).setDepth(1); + } + }) + .on("pointerover", () => stone3.setScale(0.5)) + .on("pointerout", () => stone3.setScale(0.4)); + + let stone4 = this.add + .image(740, 480, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 500) { + duck1.setX(stone4.x).setY(stone4.y).setDepth(1); + this.score += values[4]; + } + if (duck1.x == 650) { + this.score += values[2]; + duck1.setX(stone4.x).setY(stone4.y).setDepth(1); + } + if (duck1.x == 790) { + this.score += values[3]; + duck1.setX(stone4.x).setY(stone4.y).setDepth(1); + } + if (duck1.x == 850) { + this.score += values[12]; + duck1.setX(stone4.x).setY(stone4.y).setDepth(1); + } + }) + .on("pointerover", () => stone4.setScale(0.5)) + .on("pointerout", () => stone4.setScale(0.4)); + + let stone5 = this.add + .image(790, 375, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 500) { + duck1.setX(stone5.x).setY(stone5.y).setDepth(1); + this.score += values[8]; + } + if (duck1.x == 740) { + this.score += values[3]; + duck1.setX(stone5.x).setY(stone5.y).setDepth(1); + } + if (duck1.x == 900) { + this.score += values[9]; + duck1.setX(stone5.x).setY(stone5.y).setDepth(1); + } + }) + .on("pointerover", () => stone5.setScale(0.5)) + .on("pointerout", () => stone5.setScale(0.4)); + + let stone6 = this.add + .image(400, 525, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 275) { + duck1.setX(stone6.x).setY(stone6.y).setDepth(1); + this.score += values[5]; + } + if (duck1.x == 500) { + this.score += values[6]; + duck1.setX(stone6.x).setY(stone6.y).setDepth(1); + } + if (duck1.x == 740) { + this.score += values[10]; + duck1.setX(stone6.x).setY(stone6.y).setDepth(1); + } + if (duck1.x == 650) { + this.score += values[1]; + duck1.setX(stone6.x).setY(stone6.y).setDepth(1); + } + }) + .on("pointerover", () => stone6.setScale(0.5)) + .on("pointerout", () => stone6.setScale(0.4)); + + let stone7 = this.add + .image(900, 450, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 790) { + duck1.setX(stone7.x).setY(stone7.y).setDepth(1); + this.score += values[9]; + } + if (duck1.x == 850) { + this.score += values[11]; + duck1.setX(stone7.x).setY(stone7.y).setDepth(1); + } + if (this.score === correct.value) { + this.scene.start("levelFourPass"); + } else { + this.scene.start("levelFourFail"); + this.score = 0; + } + }) + .on("pointerover", () => stone7.setScale(0.5)) + .on("pointerout", () => stone7.setScale(0.4)); + + this.add.text(900, 400, "END"); + + let stone8 = this.add + .image(850, 575, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 740) { + duck1.setX(stone8.x).setY(stone8.y).setDepth(1); + this.score += values[12]; + } + if (duck1.x == 900) { + this.score += values[11]; + duck1.setX(stone8.x).setY(stone8.y).setDepth(1); + } + }) + .on("pointerover", () => stone8.setScale(0.5)) + .on("pointerout", () => stone8.setScale(0.4)); + + const stones = [ + stone1, + stone2, + stone3, + stone4, + stone5, + stone6, + stone7, + stone8, + ]; + stones.forEach((stone) => { + stone.setInteractive().on("pointerdown", () => { + duck1.setPosition(stone.x, stone.y).setDepth(1); + if (duck1.x == 275 || duck1.x == 750) { + this.score += 0; + } else if (duck1.x == 700) { + this.score += 0; + } else { + this.score += 0; + } + this.scoreText?.setText("Path Length: " + this.score); + }); + }); + this.scoreText = this.add.text(25, 70, "Path Length: " + this.score, { + fontFamily: "Arial Black", + fontSize: "40px", + color: "#ffffe0", + }); + this.scoreText.setStroke("#ffd700", 16); + } + + private playStepStoneSound() { + if (!this.isMuted) { + this.stepStoneSound.play(); + } + } + + toggleMute() { + this.isMuted = !this.isMuted; + + if (this.isMuted) { + this.sound.mute = true; + this.muteButton.setTexture("mute"); + } else { + this.sound.mute = false; + this.muteButton.setTexture("unmute"); + } + } +} diff --git a/src/scenes/levelFourFail.ts b/src/scenes/levelFourFail.ts new file mode 100644 index 00000000..771c8fb0 --- /dev/null +++ b/src/scenes/levelFourFail.ts @@ -0,0 +1,68 @@ +import Phaser from "phaser"; + +export default class levelOnePass extends Phaser.Scene { + constructor() { + super({ key: "levelFourFail", active: false }); + } + + preload() { + this.load.audio("fail", ["assets/audio/sadTrombone.mp3"]); + } + + create() { + const { width, height } = this.sys.game.config; + const screenWidth: number = Number(width); + const screenHeight: number = Number(height); + // Add a semi-transparent background rectangle to dim the game scene + const background = this.add.rectangle( + 0, + 0, + screenWidth, + screenHeight, + 0x000000, + 0.5 + ); + background.setOrigin(0, 0); + + const failSound = this.sound.add("fail"); + failSound.play(); + + // Add popup content + const popupText = this.add.text( + screenWidth / 2, + screenHeight / 2, + "That wasn't the shortest path.", + { fontSize: "32px", color: "#fff" } + ); + popupText.setOrigin(0.5); + this.add + .text( + screenWidth / 2, + screenHeight / 2 + 30, + "Remember to use Dijkstra's Algorithm to find the shortest path", + { fontSize: "32px", color: "#fff" } + ) + .setOrigin(0.5); + // make buttons to change to level two + this.add.image(700, 200, "duck").setScale(0.6); + this.add.image(550, 200, "duck").setScale(0.6); + + const tryAgain = this.add + .text(500, 500, "Try a different problem", { + color: "#ffffff", + fontSize: "32px", + fixedWidth: 575, + backgroundColor: "#87ceeb", + }) + .setPadding(32) + .setOrigin(0.2); + + tryAgain.setInteractive({ useHandCursor: true }); + + console.log("attaching listener to button"); + tryAgain.on("pointerdown", () => { + console.log("Button clicked!"); + this.scene.start("levelFour"); + }); + } +} diff --git a/src/scenes/levelFourPass.ts b/src/scenes/levelFourPass.ts new file mode 100644 index 00000000..e4282c87 --- /dev/null +++ b/src/scenes/levelFourPass.ts @@ -0,0 +1,65 @@ +import Phaser from "phaser"; + +export default class levelFourPass extends Phaser.Scene { + constructor() { + super({ key: "levelFourPass", active: false }); + } + + preload() { + this.load.audio("win", ["assets/audio/win.mp3"]); + } + + create() { + const { width, height } = this.sys.game.config; + const screenWidth: number = Number(width); + const screenHeight: number = Number(height); + + const music = this.sound.add("win"); + music.play; + + // Add a semi-transparent background rectangle to dim the game scene + const background = this.add.rectangle( + 0, + 0, + screenWidth, + screenHeight, + 0x000000, + 0.5 + ); + background.setOrigin(0, 0); + + // Add popup content + this.add.image(600, 100, "duck").setScale(0.5); + this.add.image(700, 100, "duck").setScale(0.5); + this.add.image(725, 200, "duck").setScale(0.5); + this.add.image(525, 200, "duck").setScale(0.5); + this.add.image(625, 200, "duck").setScale(0.5); + + const popupText = this.add.text( + screenWidth / 2, + screenHeight / 2, + "You Passed!", + { fontSize: "32px", color: "#fff" } + ); + popupText.setOrigin(0.5); + + // make buttons to change to level two + const nextLevel = this.add + .text(500, 500, "Next Level", { + color: "#ffffff", + fontSize: "32px", + fixedWidth: 425, + backgroundColor: "#87ceeb", + }) + .setPadding(32) + .setOrigin(0.2); + + nextLevel.setInteractive({ useHandCursor: true }); + + console.log("attaching listener to button"); + nextLevel.on("pointerdown", () => { + console.log("Button clicked!"); + this.scene.start("levelFive"); + }); + } +} diff --git a/src/scenes/levelOne.ts b/src/scenes/levelOne.ts new file mode 100644 index 00000000..877c1c55 --- /dev/null +++ b/src/scenes/levelOne.ts @@ -0,0 +1,384 @@ +import Phaser from "phaser"; + +/*interface Edge { + start: string; + end: string; + weight: number; +} +*/ +export default class levelOne extends Phaser.Scene { + private score: number = 0; + private scoreText?: Phaser.GameObjects.Text; + private isMuted: boolean = false; + private muteButton!: Phaser.GameObjects.Image; + private stepStoneSound!: Phaser.Sound.BaseSound; + + constructor() { + super({ key: "levelOne" }); + } + + preload() { + this.load.image("mute", "assets/img/mutebutton.png"); + this.load.image("unmute", "assets/img/unmutebutton.png"); + this.load.audio("stepstone", ["assets/audio/stepstone.mp3"]); + } + + create() { + const { width, height } = this.sys.game.config; + const screenWidth: number = Number(width); + const screenHeight: number = Number(height); + + this.stepStoneSound = this.sound.add("stepstone"); + + this.add + .image(screenWidth / 2, screenHeight / 2, "pond") + .setDisplaySize(screenWidth, screenHeight); + + const levelName = this.add.text(25, 25, "Level 1", { + fontFamily: "Arial Black", + fontSize: "40px", + color: "#ffffe0", + }); + levelName.setStroke("#ffd700", 16); + + this.scoreText = this.add.text(25, 75, "Path Length: 0", { + fontFamily: "Arial Black", + fontSize: "40px", + color: "#ffffe0", + }); + this.scoreText.setStroke("#ffd700", 16); + + this.muteButton = this.add + .image(1150, 120, "unmute") + .setScale(0.15) + .setInteractive(); + this.muteButton.on("pointerdown", this.toggleMute, this); + + // connection lines + const graphics = this.add.graphics(); + graphics.lineStyle(2, 0x000000); + + graphics.beginPath(); + graphics.moveTo(500, 400); + graphics.lineTo(275, 500); + graphics.lineTo(700, 600); + graphics.lineTo(750, 450); + graphics.lineTo(500, 400); + graphics.lineTo(700, 600); + graphics.strokePath(); + + this.add.text(220, 450, "Start"); + this.add.text(760, 490, "End"); + let duck1 = this.add.image(150, 500, "duck"); + let duck2 = this.add.image(950, 250, "duck"); + + duck2 + .setScale(0.4) + .setInteractive() + .on("pointerdown", () => { + if (duck1.x == 275) { + this.score += 0; + duck1 + .setX(duck2.x + 10) + .setY(duck2.y + 10) + .setDepth(1); + } + if (duck1.x == 750) { + this.score += 0; + duck1 + .setX(duck2.x + 10) + .setY(duck2.y + 10) + .setDepth(1); + } + this.scoreText?.setText("Path Length" + this.score); + }); + duck1.setScale(0.4); + const restart = this.add.text(1240, 25, "Restart", { + fontFamily: "Arial Black", + fontSize: "40px", + color: "#ffffe0", + }); + restart.setStroke("#ffd700", 16); + restart.setOrigin(1, 0).setInteractive(); + restart.on("pointerdown", () => { + duck1.setX(150).setY(450).setDepth(1); + this.score = 0; + this.scoreText?.setText("Path Length: " + this.score); + }); + + function generateValues(): number[] { + const randomList: number[] = []; + for (let i = 0; i < 10; i++) { + const randomNumber = + Math.floor(Math.random() * (10 - 1 + 1)) + 1; + randomList.push(randomNumber); + } + return randomList; + } + const values = generateValues(); + + let paths: number[][] = [ + [values[0] + values[3]], + [values[1] + values[2]], + [values[2] + values[4] + values[3]], + [values[0] + values[4] + values[2]], + ]; + function getPathValue(path: number[]): number { + let value: number = 0; + for (let v of path) { + value += v; + } + return value; + } + + function shortestPath(paths: number[][]): { + path: number[]; + value: number; + } { + let shortestPath: number[] = paths[0]; // Assume the first path is the shortest initially + let shortestLength: number = getPathValue(paths[0]); // Get the value of the first path + + for (let path of paths) { + let value: number = getPathValue(path); + if (value < shortestLength) { + shortestPath = path; + shortestLength = value; + } + } + + return { path: shortestPath, value: shortestLength }; + } + + let correct = shortestPath(paths); + + let stone1 = this.add + .image(500, 400, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 700) { + this.score += values[4]; + duck1.setX(stone1.x).setY(stone1.y).setDepth(1); + } + if (duck1.x == 750) { + this.score += values[3]; + duck1.setX(stone1.x).setY(stone1.y).setDepth(1); + } + if (duck1.x == 275) { + this.score += values[0]; + duck1.setX(stone1.x).setY(stone1.y).setDepth(1); + } + this.scoreText?.setText("Path Length: " + this.score); + }) + .on("pointerover", () => stone1.setScale(0.5)) + .on("pointerout", () => stone1.setScale(0.4)); + + let stone2 = this.add + .image(275, 500, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 500) { + duck1.setX(stone2.x).setY(stone2.y).setDepth(1); + this.score += values[0]; + } + if (duck1.x == 700) { + this.score += values[1]; + duck1.setX(stone2.x).setY(stone2.y).setDepth(1); + } + }) + .on("pointerover", () => stone2.setScale(0.5)) + .on("pointerout", () => stone2.setScale(0.4)); + + let stone3 = this.add + .image(700, 600, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 275) { + duck1.setX(stone3.x).setY(stone3.y).setDepth(1); + this.score += values[1]; + } + if (duck1.x == 750) { + this.score += values[2]; + duck1.setX(stone3.x).setY(stone3.y).setDepth(1); + } + if (duck1.x == 500) { + this.score += values[4]; + duck1.setX(stone3.x).setY(stone3.y).setDepth(1); + } + }) + .on("pointerover", () => stone3.setScale(0.5)) + .on("pointerout", () => stone3.setScale(0.4)); + + let stone4 = this.add + .image(750, 450, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 500) { + duck1.setX(stone4.x).setY(stone4.y).setDepth(1); + this.score += values[3]; + } + if (duck1.x == 700) { + this.score += values[2]; + duck1.setX(stone4.x).setY(stone4.y).setDepth(1); + } + if (this.score === correct.value) { + this.scene.start("levelOnePass"); + } else { + this.scene.start("levelOneFail"); + this.score = 0; + } + }) + .on("pointerover", () => stone4.setScale(0.5)) + .on("pointerout", () => stone4.setScale(0.4)); + + const stones = [stone1, stone2, stone3, stone4]; + stones.forEach((stone) => { + stone.setInteractive().on("pointerdown", () => { + duck1.setPosition(stone.x, stone.y).setDepth(1); + if (duck1.x == 275 || duck1.x == 750) { + this.score += 0; + } else if (duck1.x == 700) { + this.score += 0; + } else { + this.score += 0; + } + this.scoreText?.setText("Path Length: " + this.score); + }); + }); + + console.log(values); + this.add.text(387, 450, values[0].toString(), { + fontSize: "30px", + color: "000000", + }); + this.add.text(487, 550, values[1].toString(), { + fontSize: "30px", + color: "000000", + }); + this.add.text(725, 525, values[2].toString(), { + fontSize: "30px", + color: "000000", + }); + this.add.text(625, 435, values[3].toString(), { + fontSize: "30px", + color: "000000", + }); + this.add.text(550, 450, values[4].toString(), { + fontSize: "30px", + color: "000000", + }); + + /* dijkstras + + const vertices: string[] = ["stone1", "stone2", "stone3", "stone4"]; + const edges: Edge[] = [ + { start: "stone2", end: "stone4", weight: values[0] + values[4] }, + { start: "stone2", end: "stone4", weight: values[1] + values[2] }, + { + start: "stone2", + end: "stone4", + weight: values[2] + values[4] + values[3], + }, + { + start: "stone2", + end: "stone4", + weight: values[0] + values[4] + values[2], + }, + // Add more edges as needed + ]; + + // Dijkstra's algorithm function + function dijkstra( + graph: Record, + start: string + ): Record { + const distances: Record = {}; + const previous: Record = {}; + const queue: string[] = []; + for (let vertex of vertices) { + distances[vertex] = Infinity; + previous[vertex] = null; + queue.push(vertex); + } + distances[start] = 0; + + while (queue.length) { + queue.sort((a, b) => distances[a] - distances[b]); + const smallest = queue.shift(); + + if (!smallest) break; + + for (let neighbor of graph[smallest]) { + const alt = distances[smallest] + neighbor.weight; + if (alt < distances[neighbor.end]) { + distances[neighbor.end] = alt; + previous[neighbor.end] = smallest; + } + } + } + return distances; + } + console.log(dijkstra); + + // Build adjacency list representation of the graph + const adjacencyList: Record = {}; + vertices.forEach((vertex) => { + adjacencyList[vertex] = []; + }); + edges.forEach((edge) => { + adjacencyList[edge.start].push({ + start: edge.start, + end: edge.end, + weight: edge.weight, + }); + adjacencyList[edge.end].push({ + start: edge.end, + end: edge.start, + weight: edge.weight, + }); // For undirected graph + }); + + // Run Dijkstra's algorithm from a starting vertex + const startVertex = "stone1"; // Choose your starting vertex + const shortestDistances = dijkstra(adjacencyList, startVertex); + console.log( + "Shortest distances from vertex", + startVertex + ":", + shortestDistances + );*/ + } + + private playStepStoneSound() { + if (!this.isMuted) { + this.stepStoneSound.play(); + } + } + + toggleMute() { + this.isMuted = !this.isMuted; + + if (this.isMuted) { + this.sound.mute = true; + this.muteButton.setTexture("mute"); + } else { + this.sound.mute = false; + this.muteButton.setTexture("unmute"); + } + } + + update() {} +} diff --git a/src/scenes/levelOneFail.ts b/src/scenes/levelOneFail.ts new file mode 100644 index 00000000..ea5316b4 --- /dev/null +++ b/src/scenes/levelOneFail.ts @@ -0,0 +1,67 @@ +import Phaser from "phaser"; + +export default class levelOnePass extends Phaser.Scene { + constructor() { + super({ key: "levelOneFail", active: false }); + } + + preload() { + this.load.audio("fail", ["assets/audio/sadTrombone.mp3"]); + } + + create() { + const { width, height } = this.sys.game.config; + const screenWidth: number = Number(width); + const screenHeight: number = Number(height); + // Add a semi-transparent background rectangle to dim the game scene + const background = this.add.rectangle( + 0, + 0, + screenWidth, + screenHeight, + 0x000000, + 0.5 + ); + background.setOrigin(0, 0); + + const popupText = this.add.text( + screenWidth / 2, + screenHeight / 2, + "That wasn't the shortest path.", + { fontSize: "32px", color: "#fff" } + ); + popupText.setOrigin(0.5); + this.add + .text( + screenWidth / 2, + screenHeight / 2 + 30, + "Remember to use Dijkstra's Algorithm to find the shortest path", + { fontSize: "32px", color: "#fff" } + ) + .setOrigin(0.5); + + const failSound = this.sound.add("fail"); + failSound.play(); + // make buttons to change to level two + this.add.image(1000, 200, "duck").setScale(0.6); + this.add.image(300, 200, "duck").setScale(0.6); + + const tryAgain = this.add + .text(500, 500, "Try a different problem", { + color: "#ffffff", + fontSize: "32px", + fixedWidth: 575, + backgroundColor: "#87ceeb", + }) + .setPadding(32) + .setOrigin(0.2); + + tryAgain.setInteractive({ useHandCursor: true }); + + console.log("attaching listener to button"); + tryAgain.on("pointerdown", () => { + console.log("Button clicked!"); + this.scene.start("levelOne"); + }); + } +} diff --git a/src/scenes/levelOnePass.ts b/src/scenes/levelOnePass.ts new file mode 100644 index 00000000..fd6bfc28 --- /dev/null +++ b/src/scenes/levelOnePass.ts @@ -0,0 +1,62 @@ +import Phaser from "phaser"; + +export default class levelOnePass extends Phaser.Scene { + constructor() { + super({ key: "levelOnePass", active: false }); + } + + preload() { + this.load.audio("win", ["assets/audio/win.mp3"]); + } + + create() { + const { width, height } = this.sys.game.config; + const screenWidth: number = Number(width); + const screenHeight: number = Number(height); + + // Add a semi-transparent background rectangle to dim the game scene + const background = this.add.rectangle( + 0, + 0, + screenWidth, + screenHeight, + 0x000000, + 0.5 + ); + background.setOrigin(0, 0); + + // Add popup content + const popupText = this.add.text( + screenWidth / 2, + screenHeight / 2, + "You Passed!", + { fontSize: "32px", color: "#fff" } + ); + popupText.setOrigin(0.5); + + // make buttons to change to level two + this.add.image(700, 200, "duck").setScale(0.6); + this.add.image(550, 200, "duck").setScale(0.6); + + const nextLevel = this.add + .text(500, 500, "Next Level", { + color: "#ffffff", + fontSize: "32px", + fixedWidth: 425, + backgroundColor: "#87ceeb", + }) + .setPadding(32) + .setOrigin(0.2); + + nextLevel.setInteractive({ useHandCursor: true }); + + const music = this.sound.add("win"); + music.play; + + console.log("attaching listener to button"); + nextLevel.on("pointerdown", () => { + console.log("Button clicked!"); + this.scene.start("levelTwo"); + }); + } +} diff --git a/src/scenes/levelThree.ts b/src/scenes/levelThree.ts new file mode 100644 index 00000000..f64dc873 --- /dev/null +++ b/src/scenes/levelThree.ts @@ -0,0 +1,422 @@ +import Phaser from "phaser"; + +export default class levelThree extends Phaser.Scene { + private score: number = 0; + private scoreText?: Phaser.GameObjects.Text; + private isMuted: boolean = false; + private muteButton!: Phaser.GameObjects.Image; + private stepStoneSound!: Phaser.Sound.BaseSound; + + constructor() { + super({ key: "levelThree" }); + } + + preload() { + this.load.image("mute", "assets/img/mutebutton.png"); + this.load.image("unmute", "assets/img/unmutebutton.png"); + this.load.audio("stepstone", ["assets/audio/stepstone.mp3"]); + } + + create() { + const { width, height } = this.sys.game.config; + const screenWidth: number = Number(width); + const screenHeight: number = Number(height); + + this.stepStoneSound = this.sound.add("stepstone"); + + this.add + .image(screenWidth / 2, screenHeight / 2, "pond") + .setDisplaySize(screenWidth, screenHeight); + + const levelName = this.add.text(25, 25, "Level 3", { + fontFamily: "Arial Black", + fontSize: "40px", + color: "#ffffe0", + }); + levelName.setStroke("#ffd700", 16); + + this.muteButton = this.add + .image(1150, 120, "unmute") + .setScale(0.15) + .setInteractive(); + this.muteButton.on("pointerdown", this.toggleMute, this); + + this.add.image(50, 500, "duck").setScale(0.4); + this.add.image(100, 400, "duck").setScale(0.4); + + // Add connection lines + const graphics = this.add.graphics(); + graphics.lineStyle(2, 0x000000); + + const linePoints = [ + { x: 500, y: 400 }, + { x: 275, y: 435 }, + { x: 650, y: 600 }, + { x: 740, y: 480 }, + { x: 790, y: 375 }, + { x: 400, y: 525 }, + { x: 500, y: 400 }, + { x: 650, y: 600 }, + { x: 500, y: 400 }, + { x: 790, y: 375 }, + { x: 400, y: 525 }, + { x: 740, y: 480 }, + { x: 500, y: 400 }, + ]; + + graphics.beginPath(); + graphics.moveTo(linePoints[0].x, linePoints[0].y); + for (let i = 1; i < linePoints.length; i++) { + graphics.lineTo(linePoints[i].x, linePoints[i].y); + } + graphics.strokePath(); + // Add numbers + function generateValues(): number[] { + const randomList: number[] = []; + for (let i = 0; i < 15; i++) { + const randomNumber = + Math.floor(Math.random() * (10 - 1 + 1)) + 1; + randomList.push(randomNumber); + } + return randomList; + } + + const values = generateValues(); + + let paths: number[][] = [ + [values[0] + values[8]], + [values[0] + values[10] + values[3]], + [values[0] + values[10] + values[2] + values[7] + values[8]], + [ + values[0] + + values[10] + + values[2] + + values[1] + + values[6] + + values[8], + ], + [values[0] + values[7] + values[2] + values[3]], + [values[0] + values[7] + values[1] + values[6] + values[8]], + [values[0] + values[6] + values[5]], + [values[0] + values[6] + values[9] + values[3]], + [values[0] + values[6] + values[1] + values[2] + values[3]], + [values[0] + values[6] + values[1] + values[7] + values[8]], + [ + values[0] + + values[6] + + values[1] + + values[7] + + values[10] + + values[3], + ], + [values[4] + values[5]], + [values[4] + values[1] + values[2] + values[3]], + [values[4] + values[6] + values[8]], + [values[4] + values[6] + values[10] + values[3]], + [values[4] + values[6] + values[7] + values[2] + values[3]], + [values[4] + values[1] + values[7] + values[8]], + ]; + + function getPathValue(path: number[]): number { + let value: number = 0; + for (let v of path) { + value += v; + } + return value; + } + + function shortestPath(paths: number[][]): { + path: number[]; + value: number; + } { + let shortestPath: number[] = paths[0]; // Assume the first path is the shortest initially + let shortestLength: number = getPathValue(paths[0]); // Get the value of the first path + + for (let path of paths) { + let value: number = getPathValue(path); + if (value < shortestLength) { + shortestPath = path; + shortestLength = value; + } + } + + return { path: shortestPath, value: shortestLength }; + } + + // Helper function to calculate the value of a path + + let correct = shortestPath(paths); + console.log(values); + this.add.text(387, 420, values[0].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(462, 520, values[1].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(695, 540, values[2].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(765, 427, values[3].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(330, 450, values[4].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(595, 450, values[5].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(450, 462, values[6].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(575, 475, values[7].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(645, 387, values[8].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(570, 505, values[9].toString(), { + fontSize: "20px", + color: "000000", + }); + this.add.text(600, 415, values[10].toString(), { + fontSize: "20px", + color: "000000", + }); + + let duck1 = this.add.image(150, 500, "duck"); + let duck2 = this.add.image(950, 250, "duck"); + duck2 + .setScale(0.4) + .setInteractive() + .on("pointerdown", () => { + if (duck1.x == 275) { + this.score += 0; + duck1 + .setX(duck2.x + 10) + .setY(duck2.y + 10) + .setDepth(1); + } + if (duck1.x == 750) { + this.score += 0; + duck1 + .setX(duck2.x + 10) + .setY(duck2.y + 10) + .setDepth(1); + } + this.scoreText?.setText("Path Length" + this.score); + }); + duck1.setScale(0.4); + + const restart = this.add.text(1240, 25, "Restart", { + fontFamily: "Arial Black", + fontSize: "40px", + color: "#ffffe0", + }); + restart.setStroke("#ffd700", 16); + restart.setOrigin(1, 0).setInteractive(); + restart.on("pointerdown", () => { + duck1.setX(150).setY(450).setDepth(1); + this.score = 0; + this.scoreText?.setText("Path Length: " + this.score); + }); + + let stone1 = this.add + .image(500, 400, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 275) { + this.score += values[0]; + duck1.setX(stone1.x).setY(stone1.y).setDepth(1); + } + if (duck1.x == 400) { + this.score += values[4]; + duck1.setX(stone1.x).setY(stone1.y).setDepth(1); + } + if (duck1.x == 650) { + this.score += values[7]; + duck1.setX(stone1.x).setY(stone1.y).setDepth(1); + } + if (duck1.x == 790) { + this.score += values[8]; + duck1.setX(stone1.x).setY(stone1.y).setDepth(1); + } + this.scoreText?.setText("Path Length: " + this.score); + }) + .on("pointerover", () => stone1.setScale(0.5)) + .on("pointerout", () => stone1.setScale(0.4)); + + let stone2 = this.add + .image(275, 435, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 500) { + duck1.setX(stone2.x).setY(stone2.y).setDepth(1); + this.score += values[0]; + } + if (duck1.x == 400) { + this.score += values[6]; + duck1.setX(stone2.x).setY(stone2.y).setDepth(1); + } + }) + .on("pointerover", () => stone2.setScale(0.5)) + .on("pointerout", () => stone2.setScale(0.4)); + this.add.text(220, 455, "START"); + + let stone3 = this.add + .image(650, 600, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 740) { + duck1.setX(stone3.x).setY(stone3.y).setDepth(1); + this.score += values[2]; + } + if (duck1.x == 400) { + this.score += values[1]; + duck1.setX(stone3.x).setY(stone3.y).setDepth(1); + } + if (duck1.x == 500) { + this.score += values[7]; + duck1.setX(stone1.x).setY(stone1.y).setDepth(1); + } + }) + .on("pointerover", () => stone3.setScale(0.5)) + .on("pointerout", () => stone3.setScale(0.4)); + + let stone4 = this.add + .image(740, 480, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 790) { + duck1.setX(stone4.x).setY(stone4.y).setDepth(1); + this.score += values[3]; + } + if (duck1.x == 650) { + this.score += values[2]; + duck1.setX(stone1.x).setY(stone1.y).setDepth(1); + } + }) + .on("pointerover", () => stone4.setScale(0.5)) + .on("pointerout", () => stone4.setScale(0.4)); + + let stone5 = this.add + .image(790, 375, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 500) { + duck1.setX(stone5.x).setY(stone5.y).setDepth(1); + this.score += values[8]; + } + if (duck1.x == 400) { + this.score += values[5]; + duck1.setX(stone5.x).setY(stone5.y).setDepth(1); + } + if (duck1.x == 740) { + this.score += values[3]; + duck1.setX(stone1.x).setY(stone1.y).setDepth(1); + } + if (this.score === correct.value) { + this.scene.start("levelThreePass"); + } else { + this.scene.start("levelThreeFail"); + this.score = 0; + } + }) + .on("pointerover", () => stone5.setScale(0.5)) + .on("pointerout", () => stone5.setScale(0.4)); + this.add.text(815, 390, "END"); + + let stone6 = this.add + .image(400, 525, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 275) { + duck1.setX(stone6.x).setY(stone6.y).setDepth(1); + this.score += values[2]; + } + if (duck1.x == 500) { + this.score += values[6]; + duck1.setX(stone6.x).setY(stone6.y).setDepth(1); + } + if (duck1.x == 790) { + this.score += values[5]; + duck1.setX(stone6.x).setY(stone6.y).setDepth(1); + } + }) + .on("pointerover", () => stone6.setScale(0.5)) + .on("pointerout", () => stone6.setScale(0.4)); + + const stones = [stone1, stone2, stone3, stone4, stone5, stone6]; + stones.forEach((stone) => { + stone.setInteractive().on("pointerdown", () => { + duck1.setPosition(stone.x, stone.y).setDepth(1); + if (duck1.x == 275 || duck1.x == 750) { + this.score += 0; + } else if (duck1.x == 700) { + this.score += 0; + } else { + this.score += 0; + } + this.scoreText?.setText("Path Length: " + this.score); + }); + }); + + this.scoreText = this.add.text(25, 70, "Path Length: " + this.score, { + fontFamily: "Arial Black", + fontSize: "40px", + color: "#ffffe0", + }); + this.scoreText.setStroke("#ffd700", 16); + } + + private playStepStoneSound() { + if (!this.isMuted) { + this.stepStoneSound.play(); + } + } + + toggleMute() { + this.isMuted = !this.isMuted; + + if (this.isMuted) { + this.sound.mute = true; + this.muteButton.setTexture("mute"); + } else { + this.sound.mute = false; + this.muteButton.setTexture("unmute"); + } + } + + update() {} +} diff --git a/src/scenes/levelThreeFail.ts b/src/scenes/levelThreeFail.ts new file mode 100644 index 00000000..3e136d75 --- /dev/null +++ b/src/scenes/levelThreeFail.ts @@ -0,0 +1,68 @@ +import Phaser from "phaser"; + +export default class levelOnePass extends Phaser.Scene { + constructor() { + super({ key: "levelThreeFail", active: false }); + } + + preload() { + this.load.audio("fail", ["assets/audio/sadTrombone.mp3"]); + } + + create() { + const { width, height } = this.sys.game.config; + const screenWidth: number = Number(width); + const screenHeight: number = Number(height); + // Add a semi-transparent background rectangle to dim the game scene + const background = this.add.rectangle( + 0, + 0, + screenWidth, + screenHeight, + 0x000000, + 0.5 + ); + background.setOrigin(0, 0); + + // Add popup content + const popupText = this.add.text( + screenWidth / 2, + screenHeight / 2, + "That wasn't the shortest path.", + { fontSize: "32px", color: "#fff" } + ); + popupText.setOrigin(0.5); + this.add + .text( + screenWidth / 2, + screenHeight / 2 + 30, + "Remember to use Dijkstra's Algorithm to find the shortest path", + { fontSize: "32px", color: "#fff" } + ) + .setOrigin(0.5); + + const failSound = this.sound.add("fail"); + failSound.play(); + // make buttons to change to level two + this.add.image(700, 200, "duck").setScale(0.6); + this.add.image(550, 200, "duck").setScale(0.6); + + const tryAgain = this.add + .text(500, 500, "Try a different problem", { + color: "#ffffff", + fontSize: "32px", + fixedWidth: 575, + backgroundColor: "#87ceeb", + }) + .setPadding(32) + .setOrigin(0.2); + + tryAgain.setInteractive({ useHandCursor: true }); + + console.log("attaching listener to button"); + tryAgain.on("pointerdown", () => { + console.log("Button clicked!"); + this.scene.start("levelThree"); + }); + } +} diff --git a/src/scenes/levelThreePass.ts b/src/scenes/levelThreePass.ts new file mode 100644 index 00000000..b30ce76e --- /dev/null +++ b/src/scenes/levelThreePass.ts @@ -0,0 +1,64 @@ +import Phaser from "phaser"; + +export default class levelThreePass extends Phaser.Scene { + constructor() { + super({ key: "levelThreePass", active: false }); + } + + preload() { + this.load.audio("win", ["assets/audio/win.mp3"]); + } + + create() { + const { width, height } = this.sys.game.config; + const screenWidth: number = Number(width); + const screenHeight: number = Number(height); + + const music = this.sound.add("win"); + music.play; + + // Add a semi-transparent background rectangle to dim the game scene + const background = this.add.rectangle( + 0, + 0, + screenWidth, + screenHeight, + 0x000000, + 0.5 + ); + background.setOrigin(0, 0); + + // Add popup content + this.add.image(650, 100, "duck").setScale(0.5); + this.add.image(725, 200, "duck").setScale(0.5); + this.add.image(525, 200, "duck").setScale(0.5); + this.add.image(625, 200, "duck").setScale(0.5); + + const popupText = this.add.text( + screenWidth / 2, + screenHeight / 2, + "You Passed!", + { fontSize: "32px", color: "#fff" } + ); + popupText.setOrigin(0.5); + + // make buttons to change to level four + const nextLevel = this.add + .text(500, 500, "Next Level", { + color: "#ffffff", + fontSize: "32px", + fixedWidth: 425, + backgroundColor: "#87ceeb", + }) + .setPadding(32) + .setOrigin(0.2); + + nextLevel.setInteractive({ useHandCursor: true }); + + console.log("attaching listener to button"); + nextLevel.on("pointerdown", () => { + console.log("Button clicked!"); + this.scene.start("levelFour"); + }); + } +} diff --git a/src/scenes/levelTwo.ts b/src/scenes/levelTwo.ts new file mode 100644 index 00000000..73a86970 --- /dev/null +++ b/src/scenes/levelTwo.ts @@ -0,0 +1,406 @@ +import Phaser from "phaser"; +export default class levelTwo extends Phaser.Scene { + private score: number = 0; + private scoreText?: Phaser.GameObjects.Text; + private isMuted: boolean = false; + private muteButton!: Phaser.GameObjects.Image; + private stepStoneSound!: Phaser.Sound.BaseSound; + + constructor() { + super({ key: "levelTwo" }); + } + + preload() { + this.load.image("mute", "assets/img/mutebutton.png"); + this.load.image("unmute", "assets/img/unmutebutton.png"); + this.load.audio("stepstone", ["assets/audio/stepstone.mp3"]); + } + + create() { + const { width, height } = this.sys.game.config; + const screenWidth: number = Number(width); + const screenHeight: number = Number(height); + + this.stepStoneSound = this.sound.add("stepstone"); + + this.add + .image(screenWidth / 2, screenHeight / 2, "pond") + .setDisplaySize(screenWidth, screenHeight); + + const levelName = this.add.text(25, 25, "Level 2", { + fontFamily: "Arial Black", + fontSize: "40px", + color: "#ffffe0", + }); + levelName.setStroke("#ffd700", 16); + + this.scoreText = this.add.text(25, 75, "Path Length: 0", { + fontFamily: "Arial Black", + fontSize: "40px", + color: "#ffffe0", + }); + this.scoreText.setStroke("#ffd700", 16); + + this.muteButton = this.add + .image(1150, 120, "unmute") + .setScale(0.15) + .setInteractive(); + this.muteButton.on("pointerdown", this.toggleMute, this); + + this.add.image(50, 500, "duck").setScale(0.4); + this.add.image(950, 250, "duck").setScale(0.4); + + // connection lines + const graphics = this.add.graphics(); + graphics.lineStyle(2, 0x000000); + + graphics.beginPath(); + graphics.moveTo(500, 400); + graphics.lineTo(275, 435); + graphics.lineTo(650, 600); + graphics.lineTo(740, 480); + graphics.lineTo(790, 375); + graphics.lineTo(400, 525); + graphics.lineTo(500, 400); + graphics.lineTo(650, 600); + graphics.lineTo(500, 400); + graphics.lineTo(790, 375); + + graphics.strokePath(); + + function generateValues(): number[] { + const randomList: number[] = []; + for (let i = 0; i < 20; i++) { + const randomNumber = + Math.floor(Math.random() * (10 - 1 + 1)) + 1; + randomList.push(randomNumber); + } + return randomList; + } + const values = generateValues(); + + let paths: number[][] = [ + [values[0] + values[8]], + [values[1] + values[4]], + [values[0] + values[5] + values[4]], + [values[0] + values[5] + values[2] + values[7] + values[3]], + [values[0] + values[6] + values[7] + values[3]], + [values[0] + values[5] + values[4]], + [values[1] + values[5] + values[8]], + [values[1] + values[5] + values[6] + values[7] + values[3]], + [values[1] + values[4]], + [values[1] + values[2] + values[7] + values[3]], + [values[1] + values[2] + values[6] + values[8]], + ]; + + function getPathValue(path: number[]): number { + let value: number = 0; + for (let v of path) { + value += v; + } + return value; + } + + function shortestPath(paths: number[][]): { + path: number[]; + value: number; + } { + let shortestPath: number[] = paths[0]; // Assume the first path is the shortest initially + let shortestLength: number = getPathValue(paths[0]); // Get the value of the first path + + for (let path of paths) { + let value: number = getPathValue(path); + if (value < shortestLength) { + shortestPath = path; + shortestLength = value; + } + } + + return { path: shortestPath, value: shortestLength }; + } + + // Helper function to calculate the value of a path + + let correct = shortestPath(paths); + + // add stones + //this.stone = this.physics.add.staticGroup(); + this.add.text(220, 375, "START", { + fontSize: "30px", + color: "000000", + }); + this.add.text(780, 320, "END", { + fontSize: "30px", + color: "000000", + }); + let duck1 = this.add.image(150, 500, "duck"); + let duck2 = this.add.image(950, 250, "duck"); + + duck2 + .setScale(0.4) + .setInteractive() + .on("pointerdown", () => { + if (duck1.x == 275) { + this.score += 0; + duck1 + .setX(duck2.x + 10) + .setY(duck2.y + 10) + .setDepth(1); + } + if (duck1.x == 750) { + this.score += 0; + duck1 + .setX(duck2.x + 10) + .setY(duck2.y + 10) + .setDepth(1); + } + this.scoreText?.setText("Path Length" + this.score); + }); + duck1.setScale(0.4); + + const restart = this.add.text(1240, 25, "Restart", { + fontFamily: "Arial Black", + fontSize: "40px", + color: "#ffffe0", + }); + restart.setStroke("#ffd700", 16); + restart.setOrigin(1, 0).setInteractive(); + restart.on("pointerdown", () => { + duck1.setX(150).setY(450).setDepth(1); + this.score = 0; + this.scoreText?.setText("Path Length: " + this.score); + }); + + let stone1 = this.add + .image(500, 400, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 275) { + this.score += values[0]; + duck1.setX(stone1.x).setY(stone1.y).setDepth(1); + } + if (duck1.x == 400) { + this.score += values[5]; + duck1.setX(stone1.x).setY(stone1.y).setDepth(1); + } + if (duck1.x == 650) { + this.score += values[6]; + duck1.setX(stone1.x).setY(stone1.y).setDepth(1); + } + if (duck1.x == 790) { + this.score += values[8]; + duck1.setX(stone1.x).setY(stone1.y).setDepth(1); + } + this.scoreText?.setText("Path Length: " + this.score); + }) + .on("pointerover", () => stone1.setScale(0.5)) + .on("pointerout", () => stone1.setScale(0.4)); + + let stone2 = this.add + .image(275, 435, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 500) { + duck1.setX(stone2.x).setY(stone2.y).setDepth(1); + this.score += values[0]; + } + if (duck1.x == 400) { + duck1.setX(stone2.x).setY(stone2.y).setDepth(1); + this.score += values[1]; + } + }) + .on("pointerover", () => stone2.setScale(0.5)) + .on("pointerout", () => stone2.setScale(0.4)); + + let stone3 = this.add + .image(650, 600, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 740) { + duck1.setX(stone3.x).setY(stone3.y).setDepth(1); + this.score += values[7]; + } + if (duck1.x == 500) { + this.score += values[6]; + duck1.setX(stone3.x).setY(stone3.y).setDepth(1); + } + if (duck1.x == 400) { + this.score += values[2]; + duck1.setX(stone3.x).setY(stone3.y).setDepth(1); + } + }) + .on("pointerover", () => stone3.setScale(0.5)) + .on("pointerout", () => stone3.setScale(0.4)); + + let stone4 = this.add + .image(740, 480, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 790) { + duck1.setX(stone4.x).setY(stone4.y).setDepth(1); + this.score += values[3]; + } + if (duck1.x == 650) { + this.score += values[7]; + duck1.setX(stone4.x).setY(stone4.y).setDepth(1); + } + }) + .on("pointerover", () => stone4.setScale(0.5)) + .on("pointerout", () => stone4.setScale(0.4)); + + let stone5 = this.add + .image(790, 375, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 740) { + duck1.setX(stone5.x).setY(stone5.y).setDepth(1); + this.score += values[3]; + } + if (duck1.x == 400) { + this.score += values[4]; + duck1.setX(stone5.x).setY(stone5.y).setDepth(1); + } + if (duck1.x == 500) { + this.score += values[8]; + duck1.setX(stone5.x).setY(stone5.y).setDepth(1); + } + if (this.score === correct.value) { + this.scene.start("levelTwoPass"); + } /*else if (tries < 3) { + this.score = 0; + this.add.text(225, 350, "Not Quite, Try Again", { + fontFamily: "Arial Black", + fontSize: "70px", + color: "#ffffe0", + }); + tries++; + } + */ else { + this.scene.start("levelTwoFail"); + this.score = 0; + } + }) + .on("pointerover", () => stone5.setScale(0.5)) + .on("pointerout", () => stone5.setScale(0.4)); + + let stone6 = this.add + .image(400, 525, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + this.playStepStoneSound(); + if (duck1.x == 275) { + duck1.setX(stone6.x).setY(stone6.y).setDepth(1); + this.score += values[1]; + } + if (duck1.x == 500) { + this.score += values[5]; + duck1.setX(stone6.x).setY(stone6.y).setDepth(1); + } + if (duck1.x == 790) { + this.score += values[4]; + duck1.setX(stone6.x).setY(stone6.y).setDepth(1); + } + if (duck1.x == 650) { + this.score += values[2]; + duck1.setX(stone6.x).setY(stone6.y).setDepth(1); + } + }) + .on("pointerover", () => stone6.setScale(0.5)) + .on("pointerout", () => stone6.setScale(0.4)); + + const stones = [stone1, stone2, stone3, stone4, stone5, stone6]; + stones.forEach((stone) => { + stone.setInteractive().on("pointerdown", () => { + duck1.setPosition(stone.x, stone.y).setDepth(1); + if (duck1.x == 275 || duck1.x == 750) { + this.score += 0; + } else if (duck1.x == 700) { + this.score += 0; + } else { + this.score += 0; + } + this.scoreText?.setText("Path Length: " + this.score); + }); + }); + + console.log(values); + this.add.text(387, 417, values[0].toString(), { + fontSize: "30px", + color: "000000", + }); + this.add.text(350, 470, values[1].toString(), { + fontSize: "30px", + color: "000000", + }); + this.add.text(529, 540, values[2].toString(), { + fontSize: "30px", + color: "000000", + }); + this.add.text(765, 427, values[3].toString(), { + fontSize: "30px", + color: "000000", + }); + this.add.text(595, 450, values[4].toString(), { + fontSize: "30px", + color: "000000", + }); + this.add.text(450, 462, values[5].toString(), { + fontSize: "30px", + color: "000000", + }); + this.add.text(575, 500, values[6].toString(), { + fontSize: "30px", + color: "000000", + }); + this.add.text(695, 540, values[7].toString(), { + fontSize: "30px", + color: "000000", + }); + this.add.text(635, 385, values[8].toString(), { + fontSize: "30px", + color: "000000", + }); + } + + private playStepStoneSound() { + if (!this.isMuted) { + this.stepStoneSound.play(); + } + } + + toggleMute() { + this.isMuted = !this.isMuted; + + if (this.isMuted) { + this.sound.mute = true; + this.muteButton.setTexture("mute"); + } else { + this.sound.mute = false; + this.muteButton.setTexture("unmute"); + } + } + + update() {} +} diff --git a/src/scenes/levelTwoFail.ts b/src/scenes/levelTwoFail.ts new file mode 100644 index 00000000..82759441 --- /dev/null +++ b/src/scenes/levelTwoFail.ts @@ -0,0 +1,68 @@ +import Phaser from "phaser"; + +export default class levelOnePass extends Phaser.Scene { + constructor() { + super({ key: "levelTwoFail", active: false }); + } + + preload() { + this.load.audio("fail", ["assets/audio/sadTrombone.mp3"]); + } + + create() { + const { width, height } = this.sys.game.config; + const screenWidth: number = Number(width); + const screenHeight: number = Number(height); + // Add a semi-transparent background rectangle to dim the game scene + const background = this.add.rectangle( + 0, + 0, + screenWidth, + screenHeight, + 0x000000, + 0.5 + ); + background.setOrigin(0, 0); + + // Add popup content + const popupText = this.add.text( + screenWidth / 2, + screenHeight / 2, + "That wasn't the shortest path.", + { fontSize: "32px", color: "#fff" } + ); + popupText.setOrigin(0.5); + this.add + .text( + screenWidth / 2, + screenHeight / 2 + 30, + "Remember to use Dijkstra's Algorithm to find the shortest path", + { fontSize: "32px", color: "#fff" } + ) + .setOrigin(0.5); + + this.add.image(700, 200, "duck").setScale(0.6); + this.add.image(550, 200, "duck").setScale(0.6); + + const tryAgain = this.add + .text(500, 500, "Try a different problem", { + color: "#ffffff", + fontSize: "32px", + fixedWidth: 575, + backgroundColor: "#87ceeb", + }) + .setPadding(32) + .setOrigin(0.2); + + tryAgain.setInteractive({ useHandCursor: true }); + + const failSound = this.sound.add("fail"); + failSound.play(); + + console.log("attaching listener to button"); + tryAgain.on("pointerdown", () => { + console.log("Button clicked!"); + this.scene.start("levelTwo"); + }); + } +} diff --git a/src/scenes/levelTwoPass.ts b/src/scenes/levelTwoPass.ts new file mode 100644 index 00000000..ad4aa10f --- /dev/null +++ b/src/scenes/levelTwoPass.ts @@ -0,0 +1,62 @@ +import Phaser from "phaser"; + +export default class levelTwoPass extends Phaser.Scene { + constructor() { + super({ key: "levelTwoPass", active: false }); + } + + preload() { + this.load.audio("win", ["assets/audio/win.mp3"]); + } + + create() { + const { width, height } = this.sys.game.config; + const screenWidth: number = Number(width); + const screenHeight: number = Number(height); + + const music = this.sound.add("win"); + music.play; + + // Add a semi-transparent background rectangle to dim the game scene + const background = this.add.rectangle( + 0, + 0, + screenWidth, + screenHeight, + 0x000000, + 0.5 + ); + background.setOrigin(0, 0); + + // Add popup content + this.add.image(725, 200, "duck").setScale(0.5); + this.add.image(525, 200, "duck").setScale(0.5); + this.add.image(625, 200, "duck").setScale(0.5); + const popupText = this.add.text( + screenWidth / 2, + screenHeight / 2, + "You Passed!", + { fontSize: "32px", color: "#fff" } + ); + popupText.setOrigin(0.5); + + // make buttons to change to level two + const nextLevel = this.add + .text(500, 500, "Next Level", { + color: "#ffffff", + fontSize: "32px", + fixedWidth: 425, + backgroundColor: "#87ceeb", + }) + .setPadding(32) + .setOrigin(0.2); + + nextLevel.setInteractive({ useHandCursor: true }); + + console.log("attaching listener to button"); + nextLevel.on("pointerdown", () => { + console.log("Button clicked!"); + this.scene.start("levelThree"); + }); + } +} diff --git a/src/scenes/mainMenu.ts b/src/scenes/mainMenu.ts new file mode 100644 index 00000000..3e7cc358 --- /dev/null +++ b/src/scenes/mainMenu.ts @@ -0,0 +1,107 @@ +import Phaser from "phaser"; + +export default class mainMenu extends Phaser.Scene { + //private button?: Phaser.GameObjects.Text; + + constructor() { + super({ key: "mainMenu" }); + } + + preload() { + this.load.spritesheet("duck", "assets/duck.png", { + frameWidth: 37, + frameHeight: 45, + }); + + this.load.audio("theme", ["assets/audio/theme.mp3"]); + } + + create() { + const { width, height } = this.sys.game.config; + const screenWidth: number = Number(width); + const screenHeight: number = Number(height); + + const music = this.sound.add("theme"); + music.loop = true; + music.play(); + + this.add + .image(screenWidth / 2, screenHeight / 2, "pond") + .setDisplaySize(screenWidth, screenHeight); + + //duck jumping animation + this.anims.create({ + key: "jump", + frames: this.anims.generateFrameNumbers("duck"), + frameRate: 20, + }); + + const sprite = this.add.sprite(450, 200, "duck"); + + this.tweens.add({ + targets: sprite, + x: 750, + duration: 8800, + ease: "Linear", + }); + + const title = this.add.text(225, 350, "CROSS THE POND", { + fontFamily: "Arial Black", + fontSize: "70px", + color: "#ffffe0", + }); + title.setStroke("#ffd700", 16); + + const button = this.add + .text(500, 500, "Click Here to Start", { + color: "#ffffff", + fontSize: "32px", + fixedWidth: 425, + backgroundColor: "#87ceeb", + }) + .setPadding(32) + .setOrigin(0.2); + + button.setInteractive({ useHandCursor: true }); + + button.on("pointerover", () => { + button.setBackgroundColor("#1e90ff"); + }); + + button.on("pointerout", () => { + button.setBackgroundColor("#87ceeb"); + }); + + console.log("attaching listener to button"); + button.on("pointerdown", () => { + console.log("Button clicked!"); + this.scene.start("levelOne"); + }); + + const tutorialButton = this.add + .text(screenWidth / 2, screenHeight / 2 + 270, "Tutorial", { + color: "#ffffff", + fontSize: "32px", + fixedWidth: 180, + backgroundColor: "#87ceeb", + }) + .setPadding(16) + .setOrigin(0.5); + + tutorialButton.setInteractive({ useHandCursor: true }); + + tutorialButton.on("pointerover", () => { + tutorialButton.setBackgroundColor("#1e90ff"); + }); + + tutorialButton.on("pointerout", () => { + tutorialButton.setBackgroundColor("#87ceeb"); + }); + + tutorialButton.on("pointerdown", () => { + this.scene.start("tutorial"); + }); + } + + update() {} +} diff --git a/src/scenes/mainScene.ts b/src/scenes/mainScene.ts deleted file mode 100644 index 1c6b6089..00000000 --- a/src/scenes/mainScene.ts +++ /dev/null @@ -1,28 +0,0 @@ -import Phaser from "phaser"; -import PhaserLogo from "../objects/phaserLogo"; -import FpsText from "../objects/fpsText"; - -export default class MainScene extends Phaser.Scene { - fpsText: FpsText; - - constructor() { - super({ key: "MainScene" }); - } - - create() { - new PhaserLogo(this, this.cameras.main.width / 2, 0); - this.fpsText = new FpsText(this); - - const message = `Phaser v${Phaser.VERSION}`; - this.add - .text(this.cameras.main.width - 15, 15, message, { - color: "#000000", - fontSize: "24px", - }) - .setOrigin(1, 0); - } - - update() { - this.fpsText.update(); - } -} diff --git a/src/scenes/preloadScene.ts b/src/scenes/preloadScene.ts index c17b81ba..a36d7c82 100644 --- a/src/scenes/preloadScene.ts +++ b/src/scenes/preloadScene.ts @@ -6,10 +6,14 @@ export default class PreloadScene extends Phaser.Scene { } preload() { - this.load.image("phaser-logo", "assets/img/phaser-logo.png"); + this.load.image("duck", "assets/duck.png"); + this.load.image("pond", "assets/pond.png"); + this.load.image("stone", "assets/stone.png"); + this.load.image("mute", "assets/img/mutebutton.png"); + this.load.image("unmute", "assets/img/unmutebutton.png"); } create() { - this.scene.start("MainScene"); + this.scene.start("mainMenu"); } } diff --git a/src/scenes/tutorial.ts b/src/scenes/tutorial.ts new file mode 100644 index 00000000..211d7237 --- /dev/null +++ b/src/scenes/tutorial.ts @@ -0,0 +1,440 @@ +import Phaser from "phaser"; + +export default class Tutorial extends Phaser.Scene { + private score: number = 0; + private scoreText?: Phaser.GameObjects.Text; + private showSuccessPopup!: Phaser.GameObjects.Container; + private showTryAgainPopup!: Phaser.GameObjects.Container; + private currentStep: number = 0; + private steps: Phaser.GameObjects.Text[] = []; + + constructor() { + super({ key: "tutorial" }); + } + + create() { + const { width, height } = this.sys.game.config; + const screenWidth = Number(width); + const screenHeight = Number(height); + + this.add + .image(screenWidth / 2, screenHeight / 2, "pond") + .setDisplaySize(screenWidth, screenHeight); + + const tutorialText = this.add.text(screenWidth / 2, 50, "Tutorial", { + fontFamily: "Arial Black", + fontSize: "40px", + color: "#ffffe0", + }); + tutorialText.setStroke("#ffd700", 16).setOrigin(0.5, 0); + + this.steps = [ + this.add.text(200, 100, "Step 1: Click on the START stone", { + fontFamily: "Arial", + fontSize: "24px", + color: "#ffffe0", + align: "left", + }), + this.add.text( + 515, + 140, + "Step 2: Look at the END stone and determine which stones would make up the smallest path", + { + fontFamily: "Arial", + fontSize: "24px", + color: "#ffffe0", + align: "left", + } + ), + this.add.text( + 360, + 180, + "Step 3: Then, click the stones you think create the smallest path", + { + fontFamily: "Arial", + fontSize: "24px", + color: "#ffffe0", + align: "left", + } + ), + this.add.text( + 540, + 220, + "Step 4: If you notice you mess up before the end, click the RESTART button in the top right corner", + { + fontFamily: "Arial", + fontSize: "24px", + color: "#ffffe0", + align: "left", + } + ), + this.add.text( + 280, + 260, + "Step 5: If you get the path wrong, the level resets", + { + fontFamily: "Arial", + fontSize: "24px", + color: "#ffffe0", + align: "left", + } + ), + this.add.text( + 370, + 300, + "Step 6: If you get the path correct, you'll move on to the next level", + { + fontFamily: "Arial", + fontSize: "24px", + color: "#ffffe0", + align: "left", + } + ), + ]; + + this.steps.forEach((step) => { + step.setStroke("#ffd700", 16).setOrigin(0.5, 0).setVisible(false); + }); + + this.showNextStep(); + + const restart = this.add.text(1240, 25, "Restart", { + fontFamily: "Arial Black", + fontSize: "40px", + color: "#ffffe0", + }); + restart.setStroke("#ffd700", 16); + restart.setOrigin(1, 0).setInteractive(); + restart.on("pointerdown", () => { + this.score = 0; + this.scene.start("tutorial"); + }); + + const backButton = this.add.text( + screenWidth - 50, + screenHeight - 50, + "Back", + { + fontFamily: "Arial Black", + fontSize: "30px", + color: "#ffffe0", + } + ); + backButton.setStroke("#ffd700", 20).setOrigin(1).setInteractive(); + backButton.on("pointerdown", () => { + this.sound.stopAll(); + this.scene.start("mainMenu"); + }); + + const graphics = this.add.graphics(); + graphics.lineStyle(2, 0x000000); + + graphics.beginPath(); + graphics.moveTo(500, 400); + graphics.lineTo(275, 500); + graphics.lineTo(700, 600); + graphics.lineTo(750, 450); + graphics.lineTo(500, 400); + graphics.lineTo(700, 600); + graphics.strokePath(); + + this.add.text(220, 450, "Start"); + this.add.text(760, 490, "End"); + let duck1 = this.add.image(150, 500, "duck"); + let duck2 = this.add.image(1010, 320, "duck"); + + duck2 + .setScale(0.4) + .setInteractive() + .on("pointerdown", () => { + if (duck1.x == 275) { + this.score += 0; + duck1 + .setX(duck2.x + 10) + .setY(duck2.y + 10) + .setDepth(1); + } + if (duck1.x == 750) { + this.score += 0; + duck1 + .setX(duck2.x + 10) + .setY(duck2.y + 10) + .setDepth(1); + } + this.scoreText?.setText("Path Length" + this.score); + //if (this.score > 3) { + + //} + }); + duck1.setScale(0.4); + + function generateValues(): number[] { + const randomList: number[] = []; + for (let i = 0; i < 10; i++) { + const randomNumber = + Math.floor(Math.random() * (10 - 1 + 1)) + 1; + randomList.push(randomNumber); + } + return randomList; + } + const values = generateValues(); + + let paths: number[][] = [ + [values[0] + values[3]], + [values[1] + values[2]], + [values[2] + values[4] + values[3]], + [values[0] + values[4] + values[2]], + ]; + + function getPathValue(path: number[]): number { + let value: number = 0; + for (let v of path) { + value += v; + } + return value; + } + + function shortestPath(paths: number[][]): { + path: number[]; + value: number; + } { + let shortestPath: number[] = paths[0]; + let shortestLength: number = getPathValue(paths[0]); + + for (let path of paths) { + let value: number = getPathValue(path); + if (value < shortestLength) { + shortestPath = path; + shortestLength = value; + } + } + + return { path: shortestPath, value: shortestLength }; + } + + let correct = shortestPath(paths); + //let tries = 0; + + const showSuccessPopup = () => { + const { width, height } = this.sys.game.config; + const screenWidth = Number(width); + const screenHeight = Number(height); + + const bgRect = this.add + .rectangle( + screenWidth / 2, + screenHeight / 2, + 400, + 200, + 0x000000 + ) + .setAlpha(0.8); + + const successText = this.add + .text(screenWidth / 2, screenHeight / 2 - 50, "You did it!", { + fontFamily: "Arial Black", + fontSize: "40px", + color: "#00ff00", + }) + .setOrigin(0.5); + + const closeButton = this.add + .text(screenWidth / 2, screenHeight / 2 + 50, "Close", { + fontFamily: "Arial", + fontSize: "30px", + color: "#ffffff", + backgroundColor: "#ff0000", + padding: { x: 10, y: 5 }, + }) + .setOrigin(0.5) + .setInteractive(); + + closeButton.on("pointerdown", () => { + bgRect.destroy(); + successText.destroy(); + closeButton.destroy(); + this.showNextStep(); + }); + }; + + const showTryAgainPopup = () => { + const { width, height } = this.sys.game.config; + const screenWidth = Number(width); + const screenHeight = Number(height); + + const bgRect = this.add + .rectangle( + screenWidth / 2, + screenHeight / 2, + 400, + 200, + 0x000000 + ) + .setAlpha(0.8); + const tryAgainText = this.add + .text(screenWidth / 2, screenHeight / 2 - 50, "Try Again!", { + fontFamily: "Arial Black", + fontSize: "40px", + color: "#ff0000", + }) + .setOrigin(0.5); + + const closeButton = this.add + .text(screenWidth / 2, screenHeight / 2 + 50, "Close", { + fontFamily: "Arial", + fontSize: "30px", + color: "#ffffff", + backgroundColor: "#ff0000", + padding: { x: 10, y: 5 }, + }) + .setOrigin(0.5) + .setInteractive(); + + closeButton.on("pointerdown", () => { + bgRect.destroy(); + tryAgainText.destroy(); + closeButton.destroy(); + this.showNextStep(); + }); + }; + + let stone1 = this.add + .image(500, 400, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .on("pointerdown", () => { + this.showNextStep(); + if (duck1.x == 700) { + this.score += values[4]; + duck1.setX(stone1.x).setY(stone1.y).setDepth(1); + } + if (duck1.x == 750) { + this.score += values[3]; + duck1.setX(stone1.x).setY(stone1.y).setDepth(1); + } + if (duck1.x == 275) { + this.score += values[0]; + duck1.setX(stone1.x).setY(stone1.y).setDepth(1); + } + this.scoreText?.setText("Path Length: " + this.score); + }) + .on("pointerover", () => stone1.setScale(0.5)) + .on("pointerout", () => stone1.setScale(0.4)); + + let stone2 = this.add + .image(275, 500, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + this.showNextStep(); + this.showNextStep(); + if (duck1.x == 500) { + duck1.setX(stone2.x).setY(stone2.y).setDepth(1); + this.score += values[0]; + } + if (duck1.x == 700) { + this.score += values[1]; + duck1.setX(stone2.x).setY(stone2.y).setDepth(1); + } + }) + .on("pointerover", () => stone2.setScale(0.5)) + .on("pointerout", () => stone2.setScale(0.4)); + + let stone3 = this.add + .image(700, 600, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + this.showNextStep(); + if (duck1.x == 275) { + duck1.setX(stone3.x).setY(stone3.y).setDepth(1); + this.score += values[1]; + } + if (duck1.x == 750) { + this.score += values[2]; + duck1.setX(stone3.x).setY(stone3.y).setDepth(1); + } + if (duck1.x == 500) { + this.score += values[4]; + duck1.setX(stone3.x).setY(stone3.y).setDepth(1); + } + }) + .on("pointerover", () => stone3.setScale(0.5)) + .on("pointerout", () => stone3.setScale(0.4)); + + let stone4 = this.add + .image(750, 450, "stone") + .setScale(0.5, 0.4) + .setAngle(0) + .setInteractive() + .setDepth(0) + .on("pointerdown", () => { + if (duck1.x == 500) { + duck1.setX(stone4.x).setY(stone4.y).setDepth(1); + this.score += values[3]; + } + if (duck1.x == 700) { + this.score += values[2]; + duck1.setX(stone4.x).setY(stone4.y).setDepth(1); + } + if (this.score === correct.value) { + showSuccessPopup(); + } else { + showTryAgainPopup(); + } + }) + .on("pointerover", () => stone4.setScale(0.5)) + .on("pointerout", () => stone4.setScale(0.4)); + + const stones = [stone1, stone2, stone3, stone4]; + stones.forEach((stone) => { + stone.setInteractive().on("pointerdown", () => { + duck1.setPosition(stone.x, stone.y).setDepth(1); + if (duck1.x == 275 || duck1.x == 750) { + this.score += 0; + } else if (duck1.x == 700) { + this.score += 0; + } else { + this.score += 0; + } + this.scoreText?.setText("Path Length: " + this.score); + }); + }); + + //this.stone = this.physics.add.staticGroup(); + + console.log(values); + this.add.text(387, 450, values[0].toString(), { + fontSize: "30px", + color: "000000", + }); + this.add.text(487, 550, values[1].toString(), { + fontSize: "30px", + color: "000000", + }); + this.add.text(725, 525, values[2].toString(), { + fontSize: "30px", + color: "000000", + }); + this.add.text(625, 435, values[3].toString(), { + fontSize: "30px", + color: "000000", + }); + this.add.text(550, 450, values[4].toString(), { + fontSize: "30px", + color: "000000", + }); + } + + showNextStep() { + if (this.currentStep < this.steps.length) { + this.steps[this.currentStep].setVisible(true); + this.currentStep++; + } + } +}