diff --git a/bun.lock b/bun.lock index 6985f85..eb12839 100644 --- a/bun.lock +++ b/bun.lock @@ -5,9 +5,11 @@ "": { "name": "scrumFun", "dependencies": { + "@kaplayjs/crew": "^2.1.0", "kaplay": "^4000.0.0-alpha.26", }, "devDependencies": { + "@tsconfig/bun": "^1.0.10", "@types/node": "^25.3.0", "vite": "^7.3.1", }, @@ -66,6 +68,8 @@ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.3", "", { "os": "win32", "cpu": "x64" }, "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA=="], + "@kaplayjs/crew": ["@kaplayjs/crew@2.1.0", "", { "peerDependencies": { "typescript": "^5.0.0" } }, "sha512-kkRQTppRCfg7Lr3eclMluENopqHEde67zyWATYoMYNyw0FF1ReGpDBcXPH4zBQcOQ+qhM+pPRPyoT2FBtIZucw=="], + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.57.1", "", { "os": "android", "cpu": "arm" }, "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg=="], "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.57.1", "", { "os": "android", "cpu": "arm64" }, "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w=="], @@ -116,6 +120,8 @@ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.57.1", "", { "os": "win32", "cpu": "x64" }, "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA=="], + "@tsconfig/bun": ["@tsconfig/bun@1.0.10", "", {}, "sha512-5AV5YknQjNyoYzZ/8NG0dawqew/wH+x7ANiCfCIn29qo0cdbd1EryvFD1k5NSZWLBMOI/fGqMIaxi58GPIP9Cg=="], + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], "@types/node": ["@types/node@25.3.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A=="], @@ -142,6 +148,8 @@ "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + "undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="], "vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="], diff --git a/index.html b/index.html index 5290a94..12d000c 100644 --- a/index.html +++ b/index.html @@ -1,9 +1,16 @@ - + - -scrumFun - - - - - \ No newline at end of file + + Scrum FUN (TM) + + + + + + + + diff --git a/package.json b/package.json index 07a26c8..efc393b 100644 --- a/package.json +++ b/package.json @@ -8,9 +8,11 @@ "zip": "bun run build && mkdir -p dist && zip -r dist/game.zip dist -x \"**/.DS_Store\"" }, "dependencies": { + "@kaplayjs/crew": "^2.1.0", "kaplay": "^4000.0.0-alpha.26" }, "devDependencies": { + "@tsconfig/bun": "^1.0.10", "@types/node": "^25.3.0", "vite": "^7.3.1" } diff --git a/public/sprites/bubble.aseprite b/public/sprites/bubble.aseprite new file mode 100644 index 0000000..1c73d00 Binary files /dev/null and b/public/sprites/bubble.aseprite differ diff --git a/public/sprites/bubble.png b/public/sprites/bubble.png new file mode 100644 index 0000000..87db173 Binary files /dev/null and b/public/sprites/bubble.png differ diff --git a/public/sprites/card.aseprite b/public/sprites/card.aseprite new file mode 100644 index 0000000..072315b Binary files /dev/null and b/public/sprites/card.aseprite differ diff --git a/public/sprites/card.png b/public/sprites/card.png new file mode 100644 index 0000000..4aaa641 Binary files /dev/null and b/public/sprites/card.png differ diff --git a/public/sprites/dead.aseprite b/public/sprites/dead.aseprite new file mode 100644 index 0000000..d3b5617 Binary files /dev/null and b/public/sprites/dead.aseprite differ diff --git a/public/sprites/dead.png b/public/sprites/dead.png new file mode 100644 index 0000000..5702efe Binary files /dev/null and b/public/sprites/dead.png differ diff --git a/public/sprites/octo.aseprite b/public/sprites/octo.aseprite new file mode 100644 index 0000000..a74d7a7 Binary files /dev/null and b/public/sprites/octo.aseprite differ diff --git a/public/sprites/octo.png b/public/sprites/octo.png new file mode 100644 index 0000000..d9f6040 Binary files /dev/null and b/public/sprites/octo.png differ diff --git a/public/sprites/triboi.aseprite b/public/sprites/triboi.aseprite new file mode 100644 index 0000000..1ce3c4f Binary files /dev/null and b/public/sprites/triboi.aseprite differ diff --git a/public/sprites/triboi.png b/public/sprites/triboi.png new file mode 100644 index 0000000..0dc3051 Binary files /dev/null and b/public/sprites/triboi.png differ diff --git a/public/sprites/wolfi.aseprite b/public/sprites/wolfi.aseprite new file mode 100644 index 0000000..f360a46 Binary files /dev/null and b/public/sprites/wolfi.aseprite differ diff --git a/public/sprites/wolfi.png b/public/sprites/wolfi.png new file mode 100644 index 0000000..f0c0beb Binary files /dev/null and b/public/sprites/wolfi.png differ diff --git a/src/card.ts b/src/card.ts new file mode 100644 index 0000000..87961ce --- /dev/null +++ b/src/card.ts @@ -0,0 +1,101 @@ +import "kaplay/global"; + +type ScrumCard = { + name: string; + title?: string; + sprite?: string; +}; + +const scrumCards = [ + { + name: "?", + sprite: "been", + }, + { + name: "0", + title: "0", + }, + { + name: "1", + title: "1", + }, + { + name: "2", + title: "2", + }, + { + name: "3", + title: "3", + }, + { + name: "5", + title: "5", + }, + { + name: "8", + title: "8", + }, + { + name: "13", + title: "13", + }, + { + name: "20", + title: "20", + }, + { + name: "40", + title: "40", + }, + { + name: "100", + title: "100", + }, + { + name: "pineapple", + sprite: "pineapple", + }, +]; + +const cards = []; + +const BOARD_SLOT = 150; + +export const createCards = () => { + const board = add([pos(100, 100), "board"]); + + scrumCards.forEach((scrumCard, i) => { + const newCard = board.add([ + pos((BOARD_SLOT * i) % (4 * BOARD_SLOT), BOARD_SLOT * Math.floor(i / 4)), + sprite("card"), + rotate(), + animate(), + anchor("center"), + area({ + isSensor: true, + }), + scale(1), + "card", + ]); + + if (scrumCard.title) { + newCard.add([ + text(scrumCard.title, { font: "happy", size: 32 }), + anchor("center"), + "title", + ]); + } else { + newCard.add([sprite(scrumCard.sprite), anchor("center"), "title"]); + } + + newCard.onCollide("player", (player) => { + newCard.scaleTo(1.2); + }); + + newCard.onCollideEnd("player", () => { + newCard.scaleTo(1); + }); + + cards.push(newCard); + }); +}; diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000..4adb4a9 --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,2 @@ +export const WOBBLE_ANGLE = 5; +export const SPEED = 3; diff --git a/src/main.ts b/src/main.ts index 15da322..a745110 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,12 +1,155 @@ -import kaplay, { Color } from "kaplay"; -// import "kaplay/global"; // uncomment if you want to use without the k. prefix +import { crew } from "@kaplayjs/crew"; +import kaplay, { Comp, EmptyComp } from "kaplay"; +import { createCards } from "./card"; +import { WOBBLE_ANGLE, SPEED } from "./constants"; +import "kaplay/global"; // uncomment if you want to use without the prefix -const k = kaplay(); +export const k = kaplay({ + plugins: [crew], + background: "#4e495f", + scale: 2, + debugKey: "r", +}); -k.loadRoot("./"); // A good idea for Itch.io publishing later -k.loadSprite("bean", "sprites/bean.png"); +loadRoot("./"); // A good idea for Itch.io publishing later +loadSprite("card", "sprites/card.png"); +loadSprite("octo", "sprites/octo.png"); +loadSprite("dead", "sprites/dead.png"); +loadSprite("triboi", "sprites/triboi.png"); +loadSprite("wolfi", "sprites/wolfi.png"); +loadSprite("bubble", "sprites/bubble.png"); -k.add([k.pos(120, 80), k.sprite("bean")]); +k.loadCrew("sprite", "cursor"); +k.loadCrew("sprite", "pointer"); +k.loadCrew("sprite", "kat"); +k.loadCrew("sprite", "pineapple"); +k.loadCrew("sprite", "been"); +k.loadCrew("font", "happy"); -k.onClick(() => k.addKaboom(k.mousePos())); +setLayers(["game", "hover", "ui"], "game"); +const createPlayer = (scravatar: string) => { + const player = add([ + sprite(scravatar), + pos(outerWidth / 4, outerWidth / 4), + anchor("center"), + area({ + isSensor: true, + }), + rotate(), + animate(), + named("player"), + "player", + ]); + + player.animate("angle", [WOBBLE_ANGLE, -WOBBLE_ANGLE], { + easing: easings.easeInOutCubic, + direction: "ping-pong", + duration: 1, + }); + + const bubble = player.add([sprite("bubble"), anchor("center"), pos(30, -50)]); + + const bubbleText = bubble.add([ + text("9", { font: "happy", size: 24 }), + pos(-5, -24), + color(Color.BLACK), + timer(), + ]); + + bubble.hidden = true; + + player.on("countdown", () => { + bubble.hidden = false; + let time = 9; + bubbleText.loop( + 1, + () => { + if (time === -1) { + bubble.hidden = true; + } + + bubbleText.text = time.toString(); + time--; + }, + 11, + ); + }); + + return player; +}; + +scene("menu", () => { + add([text("Choose your Scravatar", { font: "happy" })]); + ["wolfi", "octo", "dead", "triboi"].forEach((image, i) => { + const selection = add([ + pos(88 * i + 100, 100), + anchor("center"), + circle(32, { fill: true }), + opacity(0.1), + z(-1), + area(), + ]); + + selection.add([ + text(image.toUpperCase(), { font: "happy", size: 14 }), + anchor("center"), + pos(0, 40), + ]); + + const avatar = selection.add([ + sprite(image), + rotate(), + animate(), + anchor("center"), + ]); + + selection.onHover(() => { + selection.opacity = 1; + setCursor("pointer"); + }); + + selection.onHoverEnd(() => { + selection.opacity = 0.1; + setCursor("default"); + }); + + selection.onClick(() => { + pushScene("game", image); + }, "left"); + + avatar.animate("angle", [WOBBLE_ANGLE, -WOBBLE_ANGLE], { + easing: easings.easeInOutCubic, + direction: "ping-pong", + duration: 1, + }); + }); +}); + +scene("game", (scravatar: string) => { + createCards(); + const player = createPlayer(scravatar); + + onKeyDown(["w", "up"], () => { + player.moveBy(0, -SPEED); + }); + + onKeyDown(["a", "left"], () => { + player.moveBy(-SPEED, 0); + }); + + onKeyDown(["s", "down"], () => { + player.moveBy(0, SPEED); + }); + + onKeyDown(["d", "right"], () => { + player.moveBy(SPEED, 0); + }); + + onKeyDown("u", () => { + player.trigger("countdown"); + }); +}); + +// pushScene("menu"); +pushScene("game", "wolfi"); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1 @@ +{}