From e90c45263e08e2ab4dce038207aea90dfec4a2c3 Mon Sep 17 00:00:00 2001 From: Balaj Marius Date: Fri, 18 Sep 2026 13:24:46 +0300 Subject: [PATCH 1/4] feat: toggle game power and extract movement handlers --- src/context/game.tsx | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/src/context/game.tsx b/src/context/game.tsx index cc56d36..c768c7e 100644 --- a/src/context/game.tsx +++ b/src/context/game.tsx @@ -62,11 +62,16 @@ export const GameProvider = ({ children }: GameProviderProps) => { const [fireLane, setFireLane] = useState(null); const handleStart = () => { + if (gameState === "on") { + setGameState("off"); + } else { + setGameState("on"); + } + reset(); - setLane(OCTO_LANE_FIRST); setFireLane(null); + setLane(OCTO_LANE_FIRST); setLanes(GAME_LANES); - setGameState("on"); play("game/keyPress"); fireEndsAt.current = null; @@ -174,6 +179,18 @@ export const GameProvider = ({ children }: GameProviderProps) => { }); }; + const handleMoveLeft = () => { + setLane((current) => { + return Math.max(OCTO_LANE_FIRST, current - OCTO_LANE_OFFSET); + }); + }; + + const handleMoveRight = () => { + setLane((current) => { + return Math.min(OCTO_LANE_LAST, current + OCTO_LANE_OFFSET); + }); + }; + useEventListener("keydown", (event) => { const isNotControlKey = GAME_CONTROL_KEYS.every((key) => { return key !== event.code; @@ -204,19 +221,11 @@ export const GameProvider = ({ children }: GameProviderProps) => { if (event.code === "Space") { handleFire(); } - // Move the octo one lane to the left. - // Stay on the first lane if there is no further cell. if (event.code === "ArrowLeft") { - setLane((current) => { - return Math.max(OCTO_LANE_FIRST, current - OCTO_LANE_OFFSET); - }); + handleMoveLeft(); } - // Move the octo one lane to the right. - // Stay on the last lane if there is no further cell. if (event.code === "ArrowRight") { - setLane((current) => { - return Math.min(OCTO_LANE_LAST, current + OCTO_LANE_OFFSET); - }); + handleMoveRight(); } }); @@ -272,6 +281,8 @@ export const GameProvider = ({ children }: GameProviderProps) => { lanes, score: count, handleStart, + handleMoveLeft, + handleMoveRight, }} > {children} From bfbcb411d6a834523ee3008d4b6fb49f1e4c737a Mon Sep 17 00:00:00 2001 From: Balaj Marius Date: Fri, 18 Sep 2026 14:14:00 +0300 Subject: [PATCH 2/4] feat: add clickable game controls and button assets --- src/components/octo.tsx | 4 +-- src/context/game.tsx | 46 +++++++++++++++++++----------- src/core/commit4.tsx | 15 ++-------- src/data/copy.json | 5 +++- src/static/css/main.css | 7 +++-- src/static/images/bg-3@2x.png | Bin 6028 -> 66704 bytes src/static/images/bg-4@2x.png | Bin 66704 -> 0 bytes src/static/images/button-1@2x.png | Bin 0 -> 6028 bytes src/static/images/button-2@2x.png | Bin 0 -> 11723 bytes src/static/images/button-3@2x.png | Bin 0 -> 11838 bytes src/static/images/button-4@2x.png | Bin 0 -> 11605 bytes src/ui/controls.tsx | 37 ++++++++++++++++++++++++ 12 files changed, 80 insertions(+), 34 deletions(-) delete mode 100644 src/static/images/bg-4@2x.png create mode 100644 src/static/images/button-1@2x.png create mode 100644 src/static/images/button-2@2x.png create mode 100644 src/static/images/button-3@2x.png create mode 100644 src/static/images/button-4@2x.png create mode 100644 src/ui/controls.tsx diff --git a/src/components/octo.tsx b/src/components/octo.tsx index 51eb1a3..67a3572 100644 --- a/src/components/octo.tsx +++ b/src/components/octo.tsx @@ -44,7 +44,7 @@ const OCTO_LANES = [ export const Octo = () => { const { textures } = useAtlas(); - const { lane, lanes, fireLane, gameState } = useGame(); + const { lane, lanes, firingLane, gameState } = useGame(); const isActive = gameState !== "off"; const blast = lanes.findIndex((laneState) => { @@ -64,7 +64,7 @@ export const Octo = () => { /> void; + handleFire: () => void; + handleMoveLeft: () => void; + handleMoveRight: () => void; }; type GameProviderProps = { @@ -59,7 +62,7 @@ export const GameProvider = ({ children }: GameProviderProps) => { const [lanes, setLanes] = useState(GAME_LANES); const [gameState, setGameState] = useState("off"); const [lane, setLane] = useState(OCTO_LANE_FIRST); - const [fireLane, setFireLane] = useState(null); + const [firingLane, setFiringLane] = useState(null); const handleStart = () => { if (gameState === "on") { @@ -69,7 +72,7 @@ export const GameProvider = ({ children }: GameProviderProps) => { } reset(); - setFireLane(null); + setFiringLane(null); setLane(OCTO_LANE_FIRST); setLanes(GAME_LANES); play("game/keyPress"); @@ -162,11 +165,15 @@ export const GameProvider = ({ children }: GameProviderProps) => { }, []); const handleFire = () => { - if (lanes[lane].bullet !== ACTOR_CELL_IDLE || isNotNil(lanes[lane].collision)) { + const isActive = lanes[lane].bullet !== ACTOR_CELL_IDLE; + + play("game/keyPress"); + + if (gameState !== "on" || isActive || isNotNil(lanes[lane].collision)) { return; } - setFireLane(lane); + setFiringLane(lane); fireEndsAt.current = performance.now() + BUG_TICK_MS; setLanes((current) => { @@ -179,16 +186,24 @@ export const GameProvider = ({ children }: GameProviderProps) => { }); }; - const handleMoveLeft = () => { + const handleMove = (offset: number) => { + play("game/keyPress"); + + if (gameState !== "on") { + return; + } + setLane((current) => { - return Math.max(OCTO_LANE_FIRST, current - OCTO_LANE_OFFSET); + return clamp(current + offset, OCTO_LANE_FIRST, OCTO_LANE_LAST); }); }; + const handleMoveLeft = () => { + handleMove(-OCTO_LANE_OFFSET); + }; + const handleMoveRight = () => { - setLane((current) => { - return Math.min(OCTO_LANE_LAST, current + OCTO_LANE_OFFSET); - }); + handleMove(OCTO_LANE_OFFSET); }; useEventListener("keydown", (event) => { @@ -213,9 +228,7 @@ export const GameProvider = ({ children }: GameProviderProps) => { return; } - setFireLane(null); - play("game/keyPress"); - + setFiringLane(null); fireEndsAt.current = null; if (event.code === "Space") { @@ -239,7 +252,7 @@ export const GameProvider = ({ children }: GameProviderProps) => { const bulletTick = Math.floor((now - startedAt.current) / BULLET_TICK_MS); if (isNotNil(fireEndsAt.current) && now >= fireEndsAt.current) { - setFireLane(null); + setFiringLane(null); fireEndsAt.current = null; } @@ -276,11 +289,12 @@ export const GameProvider = ({ children }: GameProviderProps) => { { - const { t } = useTranslation(); const { ref, entry } = useIntersectionObserver(); - const { handleStart } = useGame(); - const isCaseClip = Boolean(entry?.rootBounds && entry.boundingClientRect.top > entry.rootBounds.bottom); return ( @@ -24,13 +19,7 @@ export const Commit4 = () => { - -