diff --git a/src/assets/img/clubmate_grey.png b/src/assets/img/clubmate_grey.png new file mode 100644 index 0000000..713f47d Binary files /dev/null and b/src/assets/img/clubmate_grey.png differ diff --git a/src/common/dragAndDrop.js b/src/common/dragAndDrop.js index 3aa03a0..2e7f125 100644 --- a/src/common/dragAndDrop.js +++ b/src/common/dragAndDrop.js @@ -30,11 +30,10 @@ function onDragMove(node, event, shift = { x: 0, y: 0 }) { } function onDragEnd(node, onDrop) { - console.log("drop"); node.off("pointerup"); node.off("pointerupoutside"); node.parent.off("mousemove"); - onDrop(); + onDrop(node); } function onDragStart(node, onDrop, event) { @@ -58,4 +57,8 @@ function makeDragable(node, onDrop = () => {}) { }); } -export { makeDragable }; +function makeUnDragable(node, onDrop = () => {}) { + node.off("pointerdown"); +} + +export { makeDragable, makeUnDragable }; diff --git a/src/common/helpers.js b/src/common/helpers.js new file mode 100644 index 0000000..2f70cb7 --- /dev/null +++ b/src/common/helpers.js @@ -0,0 +1,24 @@ +import { Rectangle } from "pixi.js"; + +export function getIntersection(a, b, out = new Rectangle()) { + const x1 = Math.max(a.x, b.x); + const y1 = Math.max(a.y, b.y); + const x2 = Math.min(a.x + a.width, b.x + b.width); + const y2 = Math.min(a.y + a.height, b.y + b.height); + + if (x2 <= x1 || y2 <= y1) { + // no overlap → empty rect + out.x = 0; + out.y = 0; + out.width = 0; + out.height = 0; + return out; + } + + out.x = x1; + out.y = y1; + out.width = x2 - x1; + out.height = y2 - y1; + + return out; +} diff --git a/src/common/index.js b/src/common/index.js index d209f48..aaea758 100644 --- a/src/common/index.js +++ b/src/common/index.js @@ -1,2 +1,3 @@ export { backgroundLayout } from "./layouts"; -export { makeDragable } from "./dragAndDrop"; +export { makeDragable, makeUnDragable } from "./dragAndDrop"; +export { getIntersection } from "./helpers"; diff --git a/src/games/fridge.js b/src/games/fridge.js index 00693da..1aa3b19 100644 --- a/src/games/fridge.js +++ b/src/games/fridge.js @@ -1,10 +1,34 @@ -import { Application, Assets, Container, Sprite } from "pixi.js"; -import { makeDragable, backgroundLayout } from "../common"; +import { Application, Assets, Container, Sprite, Rectangle } from "pixi.js"; +import { + makeDragable, + backgroundLayout, + getIntersection, + makeUnDragable, +} from "../common"; import { Game } from "./game"; +class DropZone { + constructor(sprite) { + this.sprite = sprite; + this.full = false; + } + + drop(bottle) { + if (!this.full) { + this.full = true; + bottle.x = this.sprite.x; + bottle.y = this.sprite.y; + } + } +} + export class Fridge extends Game { constructor() { super(); + + this._bottleCount = 6; + this._bottlePlaced = 0; + const fridgeContainer = new Container(); fridgeContainer.layout = { width: "50%" }; const fridge = Sprite.from("frigo"); @@ -24,21 +48,82 @@ export class Fridge extends Game { this.gameContainer.addChild(fridgeContainer); this.gameContainer.addChild(bottleContainer); - const bottleLayout = { - width: "12%", - height: "16%", - position: "absolute", - }; - for (let i = 0; i < 12; i++) { - const newBottle = Sprite.from("clubmate"); - newBottle.layout = bottleLayout; - makeDragable(newBottle); - this.gameContainer.addChild(newBottle); + const bottles = []; + this._dropZones = []; - newBottle.x = 0; + const widthPercent = 0.12; + this.gameContainer.on("layout", (event) => { + const layoutBox = event.computedLayout; + for (const bottle of bottles) { + const width = layoutBox.width * widthPercent; + bottle.width = width; + bottle.height = width; // aspectRatio = 1 + if (bottle.x + bottle.width > layoutBox.width) { + bottle.x = layoutBox.width - bottle.width; + } + if (bottle.y + bottle.height > layoutBox.height) { + bottle.y = layoutBox.height - bottle.height; + } + } + + let i = 0; + for (const zone of this._dropZones) { + const width = layoutBox.width * (widthPercent + 0.01); + zone.sprite.width = width; + zone.sprite.height = width; // aspectRatio = 1 + zone.sprite.x = i * width; + zone.sprite.y = 0; + i++; + } + }); + + for (let i = 0; i < this._bottleCount; i++) { + const sprite = Sprite.from("clubmate_grey"); + this._dropZones.push(new DropZone(sprite)); + this.gameContainer.addChild(sprite); + + sprite.x = 0; + sprite.y = 0; + } + + for (let i = 0; i < this._bottleCount; i++) { + const newBottle = Sprite.from("clubmate"); + makeDragable(newBottle, (node) => { + this._onBottleDrop(node); + if (this._bottlePlaced >= this._bottleCount) { + console.log("fridge game won"); + } + }); + this.gameContainer.addChild(newBottle); + bottles.push(newBottle); + + newBottle.x = 800; newBottle.y = 0; } } reset() {} + + _onBottleDrop(bottle) { + console.log("dropped at", bottle.position); + + const intersection = new Rectangle(); + for (const dropZone of this._dropZones) { + getIntersection( + dropZone.sprite.getBounds(), + bottle.getBounds(), + intersection, + ); + const intersectArea = intersection.width * intersection.height; + const bottleArea = bottle.width * bottle.height; + if (intersectArea > 0.5 * bottleArea) { + if (!dropZone.full) { + dropZone.drop(bottle); + this._bottlePlaced++; + makeUnDragable(bottle); // maybe not needed + return; + } + } + } + } } diff --git a/src/games/game.js b/src/games/game.js index 8fbb348..bd3d981 100644 --- a/src/games/game.js +++ b/src/games/game.js @@ -3,6 +3,7 @@ import { Container } from "pixi.js"; export class Game { constructor() { this._difficulty = 1; + this.won = false; this.gameContainer = new Container(); // main container, to add the game to a node, add this container as a child this.gameContainer.layout = true; } diff --git a/src/games/opinator.js b/src/games/opinator.js index cd82586..e31a70a 100644 --- a/src/games/opinator.js +++ b/src/games/opinator.js @@ -10,7 +10,6 @@ export class Opinator extends Game { this._pixelGoalScore = undefined; this._goalScore = 10 * this._difficulty; // goal to match, measured in play area, 10 = moving the mouse on the equivalent of 10 play areas this._lastMousePos = undefined; // use to compute the travelled distance - this.won = false; const backgroundSprite = Sprite.from("opinator"); diff --git a/src/main.js b/src/main.js index 2254dcd..0afaca0 100644 --- a/src/main.js +++ b/src/main.js @@ -1,6 +1,5 @@ import "@pixi/layout"; import { Application, Assets, Container, Sprite } from "pixi.js"; - import { Fridge, Opinator } from "./games"; import { backgroundLayout } from "./common"; @@ -44,6 +43,10 @@ async function preload() { alias: "clubmate", src: "assets/img/clubmate.png", }, + { + alias: "clubmate_grey", + src: "assets/img/clubmate_grey.png", + }, { alias: "mate_caisse", src: "assets/img/caisse.jpg", diff --git a/src/package-lock.json b/src/package-lock.json index 82b1cd2..d379b69 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0", "dependencies": { "@pixi/layout": "^3.2.0", + "@pixi/math": "^7.4.3", "pixi.js": "^8.0.0" }, "devDependencies": { @@ -101,6 +102,12 @@ "yoga-layout": "^3" } }, + "node_modules/@pixi/math": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@pixi/math/-/math-7.4.3.tgz", + "integrity": "sha512-/uJOVhR2DOZ+zgdI6Bs/CwcXT4bNRKsS+TqX3ekRIxPCwaLra+Qdm7aDxT5cTToDzdxbKL5+rwiLu3Y1egILDw==", + "license": "MIT" + }, "node_modules/@pixi/react": { "version": "8.0.5", "resolved": "https://registry.npmjs.org/@pixi/react/-/react-8.0.5.tgz", diff --git a/src/package.json b/src/package.json index b05bf03..3a7c0d6 100644 --- a/src/package.json +++ b/src/package.json @@ -3,6 +3,7 @@ "version": "0.0.0", "private": true, "type": "module", + "sideEffects": true, "scripts": { "dev": "vite --host", "build": "vite build",