Compare commits

...

3 commits

Author SHA1 Message Date
lagaffe
f5eee290fb add a ui layer to games, add time display to all games 2026-04-28 15:31:52 +02:00
lagaffe
002c49b7fc add display of time in cleanupHS 2026-04-26 16:25:55 +02:00
lagaffe
0e1444ca47 make all games timed games 2026-04-26 15:09:57 +02:00
11 changed files with 159 additions and 45 deletions

View file

@ -1,6 +1,6 @@
import { Container } from "pixi.js"; import { Container } from "pixi.js";
let maxZIndex = 0; // heretic method to bring dragged sprite to the front, let maxZIndex = 100; // heretic method to bring dragged sprite to the front,
// will technically overflow if you play long enough, // will technically overflow if you play long enough,
// it is also shitty because if you externally use zIndex on the sprites in the same container, it may not bring to front // it is also shitty because if you externally use zIndex on the sprites in the same container, it may not bring to front
// clean solution would be to make a draggable container class // clean solution would be to make a draggable container class

View file

@ -1,12 +1,19 @@
import { Application, Assets, Container, Sprite, Rectangle } from "pixi.js"; import {
import { Game } from "./game"; Application,
Assets,
Container,
Sprite,
Rectangle,
Text,
} from "pixi.js";
import { TimedGame } from "./timedGame";
import { KeyBoardListener, makeDragable, getIntersection } from "../common"; import { KeyBoardListener, makeDragable, getIntersection } from "../common";
const itemTypes = { TRASH: "TRASH", HARDWARE: "HARDWARE" }; const itemTypes = { TRASH: "TRASH", HARDWARE: "HARDWARE" };
export class CleanupHS extends Game { export class CleanupHS extends TimedGame {
constructor(width, height) { constructor(width, height, ticker) {
super(); super(10000, ticker);
this._elementCount = this._difficulty * 6; this._elementCount = this._difficulty * 6;
this._elementsPlaced = 0; this._elementsPlaced = 0;
@ -14,6 +21,8 @@ export class CleanupHS extends Game {
this._scree_w = width; this._scree_w = width;
this._scree_h = height; this._scree_h = height;
this.timeout = this._elementCount * 1000; // 1s per element
this.gameContainer.layout = { this.gameContainer.layout = {
width: "100%", width: "100%",
height: "100%", height: "100%",
@ -34,8 +43,8 @@ export class CleanupHS extends Game {
trashcan.layout = { objectFit: "contain" }; trashcan.layout = { objectFit: "contain" };
shelf.layout = { objectFit: "contain" }; shelf.layout = { objectFit: "contain" };
this.gameContainer.addChild(this.trashLandingZone); this._addGameObjectChild(this.trashLandingZone);
this.gameContainer.addChild(this.shelfLandingZone); this._addGameObjectChild(this.shelfLandingZone);
} }
start() { start() {
@ -67,10 +76,14 @@ export class CleanupHS extends Game {
makeDragable(item.sprite, (node) => { makeDragable(item.sprite, (node) => {
this._onItemDrop(item); this._onItemDrop(item);
}); });
this.gameContainer.addChild(item.sprite); this._addGameObjectChild(item.sprite);
} }
} }
end() {
super.end();
}
_onItemDrop(item) { _onItemDrop(item) {
console.log("dropped", item.type); console.log("dropped", item.type);
@ -109,13 +122,17 @@ export class CleanupHS extends Game {
} }
if (this._elementsPlaced >= this._elementCount) { if (this._elementsPlaced >= this._elementCount) {
this._win(); this.end(true);
} }
} }
_onError() { _onError() {
console.log("wrong placement"); console.log("wrong placement");
// apply penalty for putting item in the wrong place // apply penalty for putting item in the wrong place
//probably a time loss this.removeTime(5000);
}
_timerConsoleDisplay() {
console.log("time left", this.getRemainingTime());
} }
} }

View file

@ -5,7 +5,7 @@ import {
getIntersection, getIntersection,
makeUnDragable, makeUnDragable,
} from "../common"; } from "../common";
import { Game } from "./game"; import { TimedGame } from "./timedGame";
class DropZone { class DropZone {
constructor(sprite) { constructor(sprite) {
@ -22,13 +22,15 @@ class DropZone {
} }
} }
export class Fridge extends Game { export class Fridge extends TimedGame {
constructor() { constructor(ticker) {
super(); super(10000, ticker);
this._bottleCount = 6; this._bottleCount = 6;
this._bottlePlaced = 0; this._bottlePlaced = 0;
this.timeout = this._bottleCount * 1500; // 1.5s per bottle
const fridgeContainer = new Container(); const fridgeContainer = new Container();
fridgeContainer.layout = { width: "50%" }; fridgeContainer.layout = { width: "50%" };
const fridge = Sprite.from("frigo"); const fridge = Sprite.from("frigo");
@ -45,8 +47,8 @@ export class Fridge extends Game {
crate.layout = backgroundLayout; crate.layout = backgroundLayout;
bottleContainer.addChild(crate); bottleContainer.addChild(crate);
this.gameContainer.addChild(fridgeContainer); this._addGameObjectChild(fridgeContainer);
this.gameContainer.addChild(bottleContainer); this._addGameObjectChild(bottleContainer);
this.bottles = []; this.bottles = [];
this._dropZones = []; this._dropZones = [];
@ -80,21 +82,21 @@ export class Fridge extends Game {
for (let i = 0; i < this._bottleCount; i++) { for (let i = 0; i < this._bottleCount; i++) {
const sprite = Sprite.from("clubmate_grey"); const sprite = Sprite.from("clubmate_grey");
this._dropZones.push(new DropZone(sprite)); this._dropZones.push(new DropZone(sprite));
this.gameContainer.addChild(sprite); this._addGameObjectChild(sprite);
sprite.x = 0; sprite.x = 0;
sprite.y = 0; sprite.y = 0;
} }
for (let i = 0; i < this._bottleCount; i++) { for (let i = 0; i < this._bottleCount; i++) {
const newBottle = Sprite.from("clubmate"); const newBottle = Sprite.from("clubmate");
makeDragable(newBottle, (node) => { makeDragable(newBottle, (node) => {
this._onBottleDrop(node); this._onBottleDrop(node);
if (this._bottlePlaced >= this._bottleCount) { if (this._bottlePlaced >= this._bottleCount) {
this._win(); this.end(true);
} }
}); });
this.gameContainer.addChild(newBottle); this._addGameObjectChild(newBottle);
this.bottles.push(newBottle); this.bottles.push(newBottle);
} }
} }

View file

@ -1,4 +1,4 @@
import { Container } from "pixi.js"; import { Container, RenderLayer } from "pixi.js";
export class Game { export class Game {
constructor() { constructor() {
@ -6,6 +6,14 @@ export class Game {
this.won = false; this.won = false;
this.gameContainer = new Container(); // main container, to add the game to a node, add this container as a child this.gameContainer = new Container(); // main container, to add the game to a node, add this container as a child
this.gameContainer.layout = true; this.gameContainer.layout = true;
/*
this._uiLayer = new Container();
this._gameObjectsLayer = new Container();*/
this._uiLayer = new RenderLayer();
this._gameObjectsLayer = new RenderLayer();
this._gameObjectsLayer.sortableChildren = true;
this.gameContainer.addChild(this._gameObjectsLayer);
this.gameContainer.addChild(this._uiLayer);
} }
/* should reset the game to its initial position*/ /* should reset the game to its initial position*/
@ -24,4 +32,14 @@ export class Game {
console.log(status ? "game won" : "game lost"); console.log(status ? "game won" : "game lost");
// we probably want to call a callback here // we probably want to call a callback here
} }
_addGameObjectChild(node) {
this.gameContainer.addChild(node);
this._gameObjectsLayer.attach(node);
}
_addUiChild(node) {
this.gameContainer.addChild(node);
this._uiLayer.attach(node);
}
} }

View file

@ -1,15 +1,18 @@
import { Application, Assets, Container, Sprite } from "pixi.js"; import { Application, Assets, Container, Sprite } from "pixi.js";
import { Game } from "./game"; import { TimedGame } from "./timedGame";
export class Opinator extends Game { export class Opinator extends TimedGame {
constructor() { constructor(ticker) {
super(); super(10000, ticker);
this._inMove = false; // indicates if player has grabed the wire (is mouse down) this._inMove = false; // indicates if player has grabed the wire (is mouse down)
this._currentScore = 0; this._currentScore = 0;
this._pixelGoalScore = undefined; this._pixelGoalScore = undefined;
this._goalScore = 8 * this._difficulty; // goal to match, measured in play area, 10 = moving the mouse on the equivalent of 10 play areas this._baseGoalScore = 8;
this._goalScore = this._baseGoalScore * 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._lastMousePos = undefined; // use to compute the travelled distance
this.timeout = this._baseGoalScore * 1000; // 1s per sprite len
this._backgroundSprite = Sprite.from("opinator"); this._backgroundSprite = Sprite.from("opinator");
this._backgroundSprite.eventMode = "static"; this._backgroundSprite.eventMode = "static";
@ -48,14 +51,14 @@ export class Opinator extends Game {
this._pixelGoalScore, this._pixelGoalScore,
); );
if (this._pixelGoalScore < this._currentScore) { if (this._pixelGoalScore < this._currentScore) {
this._win(); this.end(true);
} }
} //else ignore, not enough move to get some distance } //else ignore, not enough move to get some distance
} }
}); });
this._backgroundSprite.layout = { objectFit: "contain" }; this._backgroundSprite.layout = { objectFit: "contain" };
this.gameContainer.layout = { width: "100%", height: "100%" }; this.gameContainer.layout = { width: "100%", height: "100%" };
this.gameContainer.addChild(this._backgroundSprite); this._addGameObjectChild(this._backgroundSprite);
} }
reset() { reset() {

View file

@ -3,8 +3,8 @@ import { TimedGame } from "./timedGame";
import { KeyBoardListener } from "../common"; import { KeyBoardListener } from "../common";
export class SmartMonday extends TimedGame { export class SmartMonday extends TimedGame {
constructor() { constructor(ticker) {
super(10000); // 10 chars per second super(10000, ticker);
this._goal = 100 * this._difficulty; // number of characters this._goal = 100 * this._difficulty; // number of characters
this.timeout = 1000 * (this._goal / 10); // 10 chars per second this.timeout = 1000 * (this._goal / 10); // 10 chars per second

View file

@ -1,28 +1,66 @@
import { Game } from "./game"; import { Game } from "./game";
import { TimerDisplay } from "../ui";
export class TimedGame extends Game { export class TimedGame extends Game {
constructor(time) { constructor(time, ticker) {
super(); super();
this._timeoutId = undefined; this._timeoutId = undefined;
this.timeout = time; this.baseTimeout = time;
this.currentTimeout = time;
this._timeoutStartTime = undefined;
this.timerDisplay = new TimerDisplay(() => {
return this.getRemainingTime();
}, ticker);
this._addUiChild(this.timerDisplay.node);
} }
start() { start() {
super.start(); super.start();
this.timerDisplay.start();
this._timeoutStartTime = Date.now();
this._timeoutId = setInterval(() => { this._timeoutId = setInterval(() => {
console.log("time end"); this._onTimeout();
this.end(this._win); }, this.baseTimeout);
}, this.timeout); }
/* remove t milliseconds to the current timeout (if any) */
removeTime(t) {
if (this._timeoutId != undefined) {
this.currentTimeout = this.getRemainingTime() - t;
clearTimeout(this._timeoutId);
if (t <= 0) {
this._onTimeout();
} else {
this._timeoutStartTime = Date.now();
this._timeoutId = setInterval(() => {
this._onTimeout();
}, this.currentTimeout);
}
}
} }
reset() { reset() {
super.reset(); super.reset();
clearTimeout(this._timeoutId); clearTimeout(this._timeoutId);
this._timeoutId = undefined; this._timeoutId = undefined;
this._timeoutStartTime = undefined;
} }
end(status) { end(status) {
super.end(status); super.end(status);
this.timerDisplay.stop();
clearTimeout(this._timeoutId); clearTimeout(this._timeoutId);
this._timeoutId = undefined;
}
getRemainingTime() {
return this.currentTimeout - (Date.now() - this._timeoutStartTime);
}
_onTimeout() {
console.log("time end");
this.end(this._win);
} }
} }

View file

@ -96,16 +96,14 @@ function switchToGame(gameContainer, newGame) {
maxHeight: "100%", maxHeight: "100%",
margin: 0, margin: 0,
}; };
const opinator = new Opinator(); const opinator = new Opinator(app.ticker);
const fridge = new Fridge(); const fridge = new Fridge(app.ticker);
const smartMonday = new SmartMonday(); const smartMonday = new SmartMonday(app.ticker);
const cleanupHS = new CleanupHS(root.width, root.height); const cleanupHS = new CleanupHS(root.width, root.height, app.ticker);
//gameContainter.addChild(fridge.gameContainer); let currentGame = opinator;
//gameContainter.addChild(opinator.gameContainer); gameContainter.addChild(currentGame.gameContainer);
gameContainter.addChild(smartMonday.gameContainer);
//gameContainter.addChild(cleanupHS.gameContainer);
root.addChild(gameContainter); root.addChild(gameContainter);
smartMonday.start(); currentGame.start();
})(); })();

1
src/ui/index.js Normal file
View file

@ -0,0 +1 @@
export { TimerDisplay } from "./timerDisplay";

30
src/ui/timerDisplay.js Normal file
View file

@ -0,0 +1,30 @@
import { Text } from "pixi.js";
export class TimerDisplay {
/* variable callback should be a callback giving the value to display */
constructor(variableCallback, ticker) {
this.node = new Text({
text: "default text",
style: {
fontFamily: "Arial",
fontSize: 24,
fill: 0xff1010,
align: "center",
},
});
this.variableCallback = variableCallback;
this.callback = () => {
this.node.text = "" + this.variableCallback();
};
this.ticker = ticker;
}
start() {
this.ticker.add(this.callback);
}
stop() {
this.ticker.remove(this.callback);
}
}

7
todo.md Normal file
View file

@ -0,0 +1,7 @@
- game cycle -> launch a game handle end and launch next
- diplay info
- game result
- game
- game name
- play instruction