From 341ca4f9a7cebdf701031b55207aba15fed64895 Mon Sep 17 00:00:00 2001 From: nadiarvi Date: Sat, 10 May 2025 01:02:21 +0900 Subject: [PATCH] finish level 1 --- public/assets/flag.png | Bin 0 -> 16232 bytes src/Flag.js | 57 +++++++++ src/cat.js | 89 +------------ src/main.js | 7 +- src/scenes/gameScene.js | 95 +++++++++++--- src/scenes/lvl1.js | 270 ++++++++++++++++++++++++++++++++++++++++ src/scenes/lvl2.js | 269 +++++++++++++++++++++++++++++++++++++++ src/utils/components.js | 5 +- src/utils/theme.js | 9 +- 9 files changed, 695 insertions(+), 106 deletions(-) create mode 100644 public/assets/flag.png create mode 100644 src/Flag.js create mode 100644 src/scenes/lvl1.js create mode 100644 src/scenes/lvl2.js diff --git a/public/assets/flag.png b/public/assets/flag.png new file mode 100644 index 0000000000000000000000000000000000000000..093ab9be2628100af2c9517ce329d7101ece8be3 GIT binary patch literal 16232 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 { + this.size = img.height; + this.sprite = new Sprite( + this.x, + this.y - this.size * 0.8, + this.size, + this.size, + "dynamic" + ); + this.sprite.layer = -1; + this.sprite.spriteSheet = img; + this.sprite.addAnis({ + default: { row: 0, frames: 5 }, + }); + this.sprite.anis.frameDelay = 8; + this.sprite.changeAni("default"); + + // Scale the sprite to match target size + const scale = this.targetSize / img.height; + this.sprite.scale = scale; + + // Set sprite properties + this.sprite.collider = 'static'; + this.loaded = true; + }); + } + + update() { + if (this.isLoaded) { + this.sprite.position.x = this.x; + this.sprite.position.y = this.y; + } + } + + draw() { + if (this.isLoaded) { + drawSprite(this.sprite); + } + } + + debug() { + console.log(`Flag position: x=${this.x}, y=${this.y}`); + console.log(`Flag loaded: ${this.isLoaded}`); + } +} \ No newline at end of file diff --git a/src/cat.js b/src/cat.js index 9998d21..23b8048 100644 --- a/src/cat.js +++ b/src/cat.js @@ -2,7 +2,6 @@ export class Cat { constructor(x, y, targetSize, groundRef, obstacleRefs, worldBlockSize) { this.x = x; this.y = y; - // this.size = 171; this.size = 0; this.targetSize = targetSize; this.sprite = null; @@ -19,21 +18,18 @@ export class Cat { this.currentStepIndex = 0; this.isMoving = false; this.stepTimer = 0; - this.stepDuration = 40; // frames to complete one movement + this.stepDuration = 40; this.moveDirection = null; this.lastDirection = null; this.hasJumped = false; loadImage('assets/cat.webp', (img) => { - // debugging - // console.log(`loaded image size: ${img.width} x ${img.height}`); - // console.log(`one sprite size: ${img.height / 5}`); this.size = img.height / 5; this.sprite = new Sprite(this.x, this.y, this.size, this.size, 'dynamic'); this.sprite.rotationLock = true; - this.sprite.bounciness = 0; + this.sprite.bounciness = 0.1; this.sprite.spriteSheet = img; this.sprite.anis.offset.y = 3; this.sprite.anis.frameDelay = 8; @@ -51,9 +47,8 @@ export class Cat { this.sprite.scale = scaleFactor; this.shiftOffset = this.targetSize - this.sprite.width * this.sprite.scale; - this.sprite.anis.offset.x = this.shiftOffset + 50; - // this.sprite.anis.offset.x = 100; - + this.sprite.anis.offset.x = this.shiftOffset + this.targetSize / 4; + // this.sprite.anis.offset.x = this.targetSize / 4; this.loaded = true; }); @@ -72,17 +67,6 @@ export class Cat { this._handleInput(); }; - // update() { - // if (!this.sprite || this.steps.length === 0) return; - - // if (this.isMoving) { - // this._continueMovement(); - // } else if (this.currentStepIndex < this.steps.length) { - // this._startMovement(this.steps[this.currentStepIndex]); - // } - // } - - run(steps) { this.steps = steps.map(e => e.direction); this.currentStepIndex = 0; @@ -90,7 +74,6 @@ export class Cat { } _startMovement(direction) { - // const blockSize = 100; this.moveDirection = direction; this.stepTimer = 0; this.isMoving = true; @@ -141,7 +124,6 @@ export class Cat { _checkPlatform() { if (!this.sprite) return false; - // if (this.ground && this.sprite.colliding(this.ground)) return true; if (this.ground && Array.isArray(this.ground)) { for (let block of this.ground) { @@ -156,59 +138,6 @@ export class Cat { } return false; } - - // _continueMovement() { - // this.stepTimer++; - - // // let isOnPlatform = false; - // let isOnPlatform = this._checkPlatform(); // Use existing _checkPlatform method instead - - - // if (this.ground && this.sprite.colliding(this.ground)) { - // isOnPlatform = true; - // } - - // if (this.obstacles && Array.isArray(this.obstacles)) { - // for (let block of this.obstacles) { - // if (this.sprite.colliding(block)) { - // isOnPlatform = true; - // break; - // } - // } - // } - - // if (this.moveDirection === 'right') { - // this.sprite.vel.x = 3; - // this.lastDirection = 'right'; - // } - - // if (this.moveDirection === 'left') { - // this.sprite.vel.x = -3; - // this.lastDirection = 'left'; - // } - - // if (this.moveDirection === 'up' && isOnPlatform && !this.hasJumped) { - // this.sprite.vel.y = -20; - - // // use lastDirection to add horizontal push - // if (this.lastDirection === 'right') { - // this.sprite.vel.x = 3; - // } else if (this.lastDirection === 'left') { - // this.sprite.vel.x = -3; - // } else { - // this.sprite.vel.x = 0; // jump straight up - // } - // } - - // this.hasJumped = true; - - // if (this.stepTimer >= this.stepDuration) { - // this.isMoving = false; - // this.currentStepIndex++; - // this.sprite.vel.x = 0; - // this.changeAni('i'); - // } - // } _continueMovement() { this.stepTimer++; @@ -260,15 +189,11 @@ export class Cat { if (!this.sprite) return null; - // Reset position to initial coordinates (from constructor) this.sprite.x = this.x; this.sprite.y = this.y; - - // Reset velocities this.sprite.vel.x = 0; this.sprite.vel.y = 0; - // Reset movement states this.steps = []; this.currentStepIndex = 0; this.isMoving = false; @@ -277,7 +202,6 @@ export class Cat { this.targetX = null; this.targetY = null; - // Reset animation this.changeAni('i'); setTimeout(() => { @@ -299,15 +223,12 @@ export class Cat { noFill(); stroke(255); strokeWeight(1); - // let w = this.sprite.width * this.sprite.scale; - // let h = this.sprite.height * this.sprite.scale; let w = this.sprite.width; let h = this.sprite.height; - // console.log(`sprite's actual dimension: ${w}x${h}`); rectMode(CENTER); - // rect(this.sprite.x, this.sprite.y, w, h); + rect(this.sprite.x, this.sprite.y, w, h); }; changeAni(key) { diff --git a/src/main.js b/src/main.js index 1a260d3..97aaada 100644 --- a/src/main.js +++ b/src/main.js @@ -1,8 +1,10 @@ import { colors } from './utils/theme.js'; import { Arrow } from './components/Arrow.js'; +import { ClickableArrow } from './components/ClickableArrow.js'; import StartScene from './scenes/startScene.js'; import GameScene from './scenes/gameScene.js'; -import { ClickableArrow } from './components/ClickableArrow.js'; +import Level1 from './scenes/lvl1.js'; + let mgr; @@ -16,7 +18,8 @@ function setup(){ mgr = new SceneManager(); mgr.addScene(StartScene); mgr.addScene(GameScene); - mgr.showScene(GameScene); + mgr.addScene(Level1); + mgr.showScene(Level1); }; function draw(){ diff --git a/src/scenes/gameScene.js b/src/scenes/gameScene.js index 1ea7a80..e6a376a 100644 --- a/src/scenes/gameScene.js +++ b/src/scenes/gameScene.js @@ -1,20 +1,19 @@ import { colors } from '../utils/theme.js'; import { Cat } from '../cat.js'; -import { buttonS } from '../utils/theme.js'; +import { buttonS, buttonM, buttonL } from '../utils/theme.js'; import { MyButton } from '../utils/components.js'; import { ClickableArrow } from '../components/ClickableArrow.js'; import { ControlPanel } from '../components/controlPanel.js'; +import { Flag } from "../Flag.js"; export default function GameScene() { - // for ground - let ground; let blocksGround = []; let blockSprites = []; let restart = false; - const catSize = 150; + const catSize = 155; const worldBlockSize = 125; const groundHeight = worldBlockSize; // const worldHeight = windowHeight - groundHeight; @@ -31,13 +30,20 @@ export default function GameScene() { ]; let cat; + let flag; + let runButton; + let nextButton; + let exitButton; + let blocks; let steps; - let loops; - let clickArrow; + // let loops; + // let clickArrow; - let clicked; + // let clicked; + + let levelFinished = false; let selectedStepIndex = null; @@ -50,9 +56,6 @@ export default function GameScene() { let selectedSteps; let selectedBlock; - // selectedSteps = ['up', 'right', 'up', 'up', 'right', 'down']; - // selectedSteps = selectedSteps.map((e) => new ClickableArrow(e, true)); - const slots = { blocks: 2, steps: 8, @@ -71,6 +74,15 @@ export default function GameScene() { onPress: () => this.startGame(), }); + nextButton = new MyButton({ + x: width / 2, + y: height / 2.5, + text: "next", + mode: "CENTER", + style: buttonM, + onPress: () => console.log(`redirect to next game`), + }); + blocks = new ControlPanel({ name: 'blocks', x: width / 32 * 7, @@ -94,12 +106,14 @@ export default function GameScene() { // numBoxes: slots.loops, // }); + flag = new Flag(12 * worldBlockSize, height - worldBlockSize * 4, catSize * 0.75); + for (let i = 0; i < width; i += worldBlockSize) { let b = new Sprite( - i + worldBlockSize / 2, // x position for each block - height - groundHeight / 2, // y position - worldBlockSize, // width of each block - worldBlockSize, // height of each block + i + worldBlockSize / 2, + height - groundHeight / 2, + worldBlockSize, + worldBlockSize, 'static' ); const i_cnt = Math.floor(i / worldBlockSize); @@ -126,8 +140,7 @@ export default function GameScene() { } } - // Cat sprite - // cat = new Cat(width / 4, height - catSize * 13/12, catSize, blocksGround, blockSprites); + // Sprites cat = new Cat(2.25 * worldBlockSize, height - catSize * 13/12, catSize, blocksGround, blockSprites, worldBlockSize); }; @@ -142,10 +155,36 @@ export default function GameScene() { text('lvl.1', width / 32, height /32 - 4); runButton.draw(); + flag.draw(); blocks.draw(); steps.draw(); // loops.draw(); cat.draw(); + + if (cat.sprite && flag.sprite) { + if (cat.sprite.overlaps(flag.sprite)) { + levelFinished = true; + }; + }; + + if (!levelFinished) { + // draw the overlay + push(); + fill(35, 20, 45, 190); + rectMode(CORNER); + rect(0, 0, width, height); + + textAlign(CENTER, CENTER); + textSize(128) + fill(colors.tertiary); + stroke(colors.secondary); + strokeWeight(10); + text("~lvl 1 DONE!~", width / 2, height / 4); + + // draw button + nextButton.draw(); + pop(); + } }; this.onResize = () => { @@ -203,4 +242,28 @@ export default function GameScene() { cat.run(steps.contents); restart = true; } + + this.exit = function () { + // Remove cat sprite + if (cat) { + cat.remove(); + } + + // Remove flag sprite + if (flag && flag.sprite) { + flag.sprite.remove(); + } + + // Remove ground blocks + blocksGround.forEach(block => { + if (block) block.remove(); + }); + blocksGround = []; + + // Remove obstacle blocks + blockSprites.forEach(block => { + if (block) block.remove(); + }); + blockSprites = []; + }; } diff --git a/src/scenes/lvl1.js b/src/scenes/lvl1.js new file mode 100644 index 0000000..be558fb --- /dev/null +++ b/src/scenes/lvl1.js @@ -0,0 +1,270 @@ +import { colors } from '../utils/theme.js'; +import { Cat } from '../cat.js'; +import { buttonS, buttonM, buttonL } from '../utils/theme.js'; +import { MyButton } from '../utils/components.js'; +import { ClickableArrow } from '../components/ClickableArrow.js'; +import { ControlPanel } from '../components/controlPanel.js'; +import { Flag } from "../Flag.js"; +import Level2 from "./lvl2.js"; + + +export default function Level1() { + let blocksGround = []; + let blockSprites = []; + + let restart = false; + + const catSize = 155; + const worldBlockSize = 125; + const groundHeight = worldBlockSize; + // const worldHeight = windowHeight - groundHeight; + const maxIdx = { + x: Math.ceil(windowWidth / worldBlockSize), + y: Math.floor(windowHeight / worldBlockSize) + } + + const obstacle = [ + ...Array(maxIdx.y - 3 - 1).fill(...Array(maxIdx.x).fill(0)), + [...Array(9).fill(0), ...Array(maxIdx.x - 9).fill(1)], + [...Array(7).fill(0), ...Array(maxIdx.x - 7).fill(1)], + [...Array(5).fill(0), ...Array(maxIdx.x - 5).fill(1)], + ]; + + let cat; + let flag; + + let runButton; + let nextButton; + let exitButton; + + let blocks; + let steps; + // let loops; + // let clickArrow; + + // let clicked; + + let levelFinished = false; + + let selectedStepIndex = null; + + const buildingBlocks = [ + new ClickableArrow('up', false), + new ClickableArrow('right', false), + ]; + + // changed on user input + let selectedSteps; + let selectedBlock; + + const slots = { + blocks: 2, + steps: 8, + loop: 3, + } + + this.name = "GameScene"; + + this.setup = () => { + runButton = new MyButton({ + x: (width / 32) * 28.5, + y: height / 32, + text: "run >>", + mode: "CORNER", + style: buttonS, + onPress: () => this.startGame(), + }); + + nextButton = new MyButton({ + x: width / 2, + y: height / 2.5, + text: "next", + mode: "CENTER", + style: buttonM, + onPress: () => this.sceneManager.showScene(Level2), + }); + + blocks = new ControlPanel({ + name: 'blocks', + x: width / 32 * 7, + y: height / 32, + numBoxes: slots.blocks, + }); + blocks.setContents(buildingBlocks); + + steps = new ControlPanel({ + name: 'steps', + x: width / 32 * 7 + 48 * (slots.blocks + 1), + y: height / 32, + numBoxes: slots.steps, + }); + + + // loops = new ControlPanel({ + // name: 'loop', + // x: width / 32 * 7 + 48 * (slots.blocks + slots.steps + 2.75), + // y: height / 32, + // numBoxes: slots.loops, + // }); + + flag = new Flag(12 * worldBlockSize, height - worldBlockSize * 4, catSize * 0.75); + + for (let i = 0; i < width; i += worldBlockSize) { + let b = new Sprite( + i + worldBlockSize / 2, + height - groundHeight / 2, + worldBlockSize, + worldBlockSize, + 'static' + ); + const i_cnt = Math.floor(i / worldBlockSize); + + b.color = (i_cnt % 2) === 0 ? color(colors.secondary) : color(colors.darkRed); + b.strokeWeight = 5; + b.stroke = (i_cnt % 2) === 0 ? color(colors.darkRed) : color(colors.darkRed); + blocksGround.push(b); + } + + // Obstacles (physical) + for (let y = windowHeight - groundHeight - 2; y > 0; y -= worldBlockSize) { + for (let x = 0; x < width; x += worldBlockSize) { + const x_cnt = Math.floor(x / worldBlockSize); + const y_cnt = Math.floor(y / worldBlockSize); + const isObs = obstacle[y_cnt] && obstacle[y_cnt][x_cnt]; + + if (isObs) { + let b = new Sprite(x + worldBlockSize / 2, y + worldBlockSize / 2, worldBlockSize, worldBlockSize, 'static'); + b.color = (x_cnt + y_cnt) % 2 === 0 ? color(colors.tertiary) : color(colors.accent); + b.strokeWeight = 0; + blockSprites.push(b); + } + } + } + + // Sprites + cat = new Cat(2.25 * worldBlockSize, height - catSize * 13/12, catSize, blocksGround, blockSprites, worldBlockSize); + }; + + this.draw = () => { + background(colors.primary); + + fill(colors.tertiary); + textSize(128); + stroke(colors.secondary); + strokeWeight(7); + textAlign(LEFT, TOP); + text('lvl.1', width / 32, height /32 - 4); + + runButton.draw(); + flag.draw(); + blocks.draw(); + steps.draw(); + // loops.draw(); + cat.draw(); + + if (cat.sprite && flag.sprite) { + if (cat.sprite.overlaps(flag.sprite)) { + levelFinished = true; + }; + }; + + if (levelFinished) { + // draw the overlay + push(); + fill(35, 20, 45, 190); + rectMode(CORNER); + rect(0, 0, width, height); + + textAlign(CENTER, CENTER); + textSize(128) + fill(colors.tertiary); + stroke(colors.secondary); + strokeWeight(10); + text("~lvl 1 DONE!~", width / 2, height / 4); + + // draw button + nextButton.draw(); + pop(); + } + }; + + this.onResize = () => { + cat.setPosition(width / 2, height - 177.5); + runButton.setPosition((width / 16) * 15, height / 16); + blocks.setPosition(width / 32, height / 32); + steps.setPosition((width / 32) * 4, height / 32); + } + + this.keyPressed = () => { + console.log(`key: ${key}, keyCode: ${keyCode}`); + + const _key = key; + console.log(`key passed: ${_key}`); + + if (keyCode === ESCAPE) { + // Deselect the currently selected arrow if any + if (selectedStepIndex !== null && steps.contents[selectedStepIndex]) { + steps.contents[selectedStepIndex].selected = false; + } + selectedStepIndex = null; + } else { + cat.keyPressed(key); + } + } + + this.mousePressed = function() { + // console.log(`canvas clicked at ${mouseX}, ${mouseY}`); + + steps.contents.forEach((arrow, index) => { + if (arrow._isMouseOver(mouseX, mouseY)) { + selectedStepIndex = index; + steps.contents.forEach(a => a.selected = false); + arrow.select(); + } + }); + + blocks.contents.forEach((arrow) => { + if (arrow._isMouseOver(mouseX, mouseY)) { + // console.log("Clicked arrow", arrow.direction); + if (selectedStepIndex !== null) { + const selectedArrow = steps.contents[selectedStepIndex]; + selectedArrow.set(arrow.direction); + selectedArrow.clickable = true; + selectedArrow.selected = false; + selectedStepIndex = null; + } + } + }); + + }; + + this.startGame = () => { + if (restart) cat.restart(); + cat.run(steps.contents); + restart = true; + }; + + this.exit = function () { + // Remove cat sprite + if (cat) { + cat.remove(); + } + + // Remove flag sprite + if (flag && flag.sprite) { + flag.sprite.remove(); + } + + // Remove ground blocks + blocksGround.forEach(block => { + if (block) block.remove(); + }); + blocksGround = []; + + // Remove obstacle blocks + blockSprites.forEach(block => { + if (block) block.remove(); + }); + blockSprites = []; + }; +} diff --git a/src/scenes/lvl2.js b/src/scenes/lvl2.js new file mode 100644 index 0000000..a64179e --- /dev/null +++ b/src/scenes/lvl2.js @@ -0,0 +1,269 @@ +import { colors } from '../utils/theme.js'; +import { Cat } from '../cat.js'; +import { buttonS, buttonM, buttonL } from '../utils/theme.js'; +import { MyButton } from '../utils/components.js'; +import { ClickableArrow } from '../components/ClickableArrow.js'; +import { ControlPanel } from '../components/controlPanel.js'; +import { Flag } from "../Flag.js"; + + +export default function Level2() { + let blocksGround = []; + let blockSprites = []; + + let restart = false; + + const catSize = 155; + const worldBlockSize = 125; + const groundHeight = worldBlockSize; + // const worldHeight = windowHeight - groundHeight; + const maxIdx = { + x: Math.ceil(windowWidth / worldBlockSize), + y: Math.floor(windowHeight / worldBlockSize) + } + + const obstacle = [ + ...Array(maxIdx.y - 3 - 1).fill(...Array(maxIdx.x).fill(0)), + [...Array(9).fill(0), ...Array(maxIdx.x - 9).fill(1)], + [...Array(7).fill(0), ...Array(maxIdx.x - 7).fill(1)], + [...Array(5).fill(0), ...Array(maxIdx.x - 5).fill(1)], + ]; + + let cat; + let flag; + + let runButton; + let nextButton; + let exitButton; + + let blocks; + let steps; + // let loops; + // let clickArrow; + + // let clicked; + + let levelFinished = false; + + let selectedStepIndex = null; + + const buildingBlocks = [ + new ClickableArrow('up', false), + new ClickableArrow('right', false), + ]; + + // changed on user input + let selectedSteps; + let selectedBlock; + + const slots = { + blocks: 2, + steps: 8, + loop: 3, + } + + this.name = "GameScene"; + + this.setup = () => { + runButton = new MyButton({ + x: (width / 32) * 28.5, + y: height / 32, + text: "run >>", + mode: "CORNER", + style: buttonS, + onPress: () => this.startGame(), + }); + + nextButton = new MyButton({ + x: width / 2, + y: height / 2.5, + text: "next", + mode: "CENTER", + style: buttonM, + onPress: () => console.log(`redirect to next game`), + }); + + blocks = new ControlPanel({ + name: 'blocks', + x: width / 32 * 7, + y: height / 32, + numBoxes: slots.blocks, + }); + blocks.setContents(buildingBlocks); + + steps = new ControlPanel({ + name: 'steps', + x: width / 32 * 7 + 48 * (slots.blocks + 1), + y: height / 32, + numBoxes: slots.steps, + }); + + + // loops = new ControlPanel({ + // name: 'loop', + // x: width / 32 * 7 + 48 * (slots.blocks + slots.steps + 2.75), + // y: height / 32, + // numBoxes: slots.loops, + // }); + + flag = new Flag(12 * worldBlockSize, height - worldBlockSize * 4, catSize * 0.75); + + for (let i = 0; i < width; i += worldBlockSize) { + let b = new Sprite( + i + worldBlockSize / 2, + height - groundHeight / 2, + worldBlockSize, + worldBlockSize, + 'static' + ); + const i_cnt = Math.floor(i / worldBlockSize); + + b.color = (i_cnt % 2) === 0 ? color(colors.secondary) : color(colors.darkRed); + b.strokeWeight = 5; + b.stroke = (i_cnt % 2) === 0 ? color(colors.darkRed) : color(colors.darkRed); + blocksGround.push(b); + } + + // Obstacles (physical) + for (let y = windowHeight - groundHeight - 2; y > 0; y -= worldBlockSize) { + for (let x = 0; x < width; x += worldBlockSize) { + const x_cnt = Math.floor(x / worldBlockSize); + const y_cnt = Math.floor(y / worldBlockSize); + const isObs = obstacle[y_cnt] && obstacle[y_cnt][x_cnt]; + + if (isObs) { + let b = new Sprite(x + worldBlockSize / 2, y + worldBlockSize / 2, worldBlockSize, worldBlockSize, 'static'); + b.color = (x_cnt + y_cnt) % 2 === 0 ? color(colors.tertiary) : color(colors.accent); + b.strokeWeight = 0; + blockSprites.push(b); + } + } + } + + // Sprites + cat = new Cat(2.25 * worldBlockSize, height - catSize * 13/12, catSize, blocksGround, blockSprites, worldBlockSize); + }; + + this.draw = () => { + background(colors.primary); + + fill(colors.tertiary); + textSize(128); + stroke(colors.secondary); + strokeWeight(7); + textAlign(LEFT, TOP); + text('lvl.2', width / 32, height /32 - 4); + + runButton.draw(); + flag.draw(); + blocks.draw(); + steps.draw(); + // loops.draw(); + cat.draw(); + + if (cat.sprite && flag.sprite) { + if (cat.sprite.overlaps(flag.sprite)) { + levelFinished = true; + }; + }; + + if (levelFinished) { + // draw the overlay + push(); + fill(35, 20, 45, 190); + rectMode(CORNER); + rect(0, 0, width, height); + + textAlign(CENTER, CENTER); + textSize(128) + fill(colors.tertiary); + stroke(colors.secondary); + strokeWeight(10); + text("~lvl 2 DONE!~", width / 2, height / 4); + + // draw button + nextButton.draw(); + pop(); + } + }; + + this.onResize = () => { + cat.setPosition(width / 2, height - 177.5); + runButton.setPosition((width / 16) * 15, height / 16); + blocks.setPosition(width / 32, height / 32); + steps.setPosition((width / 32) * 4, height / 32); + } + + this.keyPressed = () => { + console.log(`key: ${key}, keyCode: ${keyCode}`); + + const _key = key; + console.log(`key passed: ${_key}`); + + if (keyCode === ESCAPE) { + // Deselect the currently selected arrow if any + if (selectedStepIndex !== null && steps.contents[selectedStepIndex]) { + steps.contents[selectedStepIndex].selected = false; + } + selectedStepIndex = null; + } else { + cat.keyPressed(key); + } + } + + this.mousePressed = function() { + // console.log(`canvas clicked at ${mouseX}, ${mouseY}`); + + steps.contents.forEach((arrow, index) => { + if (arrow._isMouseOver(mouseX, mouseY)) { + selectedStepIndex = index; + steps.contents.forEach(a => a.selected = false); + arrow.select(); + } + }); + + blocks.contents.forEach((arrow) => { + if (arrow._isMouseOver(mouseX, mouseY)) { + // console.log("Clicked arrow", arrow.direction); + if (selectedStepIndex !== null) { + const selectedArrow = steps.contents[selectedStepIndex]; + selectedArrow.set(arrow.direction); + selectedArrow.clickable = true; + selectedArrow.selected = false; + selectedStepIndex = null; + } + } + }); + + }; + + this.startGame = () => { + if (restart) cat.restart(); + cat.run(steps.contents); + restart = true; + }; + + this.exit = function () { + // Remove cat sprite + if (cat) { + cat.remove(); + } + + // Remove flag sprite + if (flag && flag.sprite) { + flag.sprite.remove(); + } + + // Remove ground blocks + blocksGround.forEach(block => { + if (block) block.remove(); + }); + blocksGround = []; + + // Remove obstacle blocks + blockSprites.forEach(block => { + if (block) block.remove(); + }); + blockSprites = []; + }; +} diff --git a/src/utils/components.js b/src/utils/components.js index 0b10163..d3a3d62 100644 --- a/src/utils/components.js +++ b/src/utils/components.js @@ -1,5 +1,5 @@ export class MyButton { - constructor({ x, y, text, mode = "CORNER", style = {}, onPress = null }) { + constructor({ x, y, text, mode = "CORNER", style = {}, onPress = null, size = 'small' }) { this.button = new Clickable(); // Apply layout @@ -36,5 +36,4 @@ export class MyButton { setPosition(x, y) { this.button.locate(x, y); } -}; - \ No newline at end of file +}; \ No newline at end of file diff --git a/src/utils/theme.js b/src/utils/theme.js index 4df1761..7a58fc3 100644 --- a/src/utils/theme.js +++ b/src/utils/theme.js @@ -21,6 +21,13 @@ const buttonL = { textSize: 32, }; +const buttonM = { + ...button, + width: 200, + height: 60, + textSize: 26, + strokeWeight: 3, +}; const buttonS = { ...button, @@ -30,4 +37,4 @@ const buttonS = { strokeWeight: 3, }; -export { colors, buttonL, buttonS }; +export { colors, buttonL, buttonM, buttonS };