From 8146782fec88c7f5747930b3cf1ca76a4b7a7bdb Mon Sep 17 00:00:00 2001 From: Pabloader Date: Fri, 27 Jun 2025 09:35:57 +0000 Subject: [PATCH] Door locking --- src/games/zombies/character.ts | 10 ++++++++++ src/games/zombies/index.ts | 12 +++++------ src/games/zombies/pathfinding.ts | 2 +- src/games/zombies/tile.ts | 34 ++++++++++++++++++++++++-------- src/games/zombies/tilemap.ts | 11 ++++++++++- 5 files changed, 53 insertions(+), 16 deletions(-) diff --git a/src/games/zombies/character.ts b/src/games/zombies/character.ts index 919c43e..8d14eb0 100644 --- a/src/games/zombies/character.ts +++ b/src/games/zombies/character.ts @@ -6,6 +6,7 @@ export default class Character extends Item { public health: number; public inventory: Item[] = []; public tile: Tile = new Tile([0, 0], 1); + public lastDoor: Tile | undefined; constructor(type: ItemTypeImage) { super(type); @@ -76,6 +77,15 @@ export default class Character extends Item { return false; } + public hasItem(item: Item | null | undefined): boolean { + if (!item) { + return false; + } + const itemIndex = this.inventory.findIndex(i => i === item); + + return itemIndex >= 0; + } + /** @returns true, if action was performed */ public handleSpin(action: SpinnerAction): boolean { switch (action) { diff --git a/src/games/zombies/index.ts b/src/games/zombies/index.ts index 86f8483..102f5b6 100644 --- a/src/games/zombies/index.ts +++ b/src/games/zombies/index.ts @@ -2,7 +2,7 @@ import { createCanvas } from "@common/display/canvas"; import Spinner from "./spinner"; import type Entity from "./entity"; import { getRealPoint } from "@common/dom"; -import { nextFrame } from "@common/utils"; +import { clamp, nextFrame } from "@common/utils"; import bgImg from './assets/bg.jpg'; import TileMap from "./tilemap"; import Inventory from "./inventory"; @@ -11,18 +11,18 @@ const MAP_SIZE = 12; const MAP_PIXEL_SIZE = window.innerHeight; const MAP_PADDING = 10; const TILE_SIZE = (MAP_PIXEL_SIZE - MAP_PADDING * 2) / MAP_SIZE; -const SIDEBAR_SIZE = Math.min((window.innerWidth - window.innerHeight) / 2, 400); +const SIDEBAR_SIZE = clamp((window.innerWidth - window.innerHeight) / 2, 300, 600); const canvas = createCanvas(MAP_PIXEL_SIZE + SIDEBAR_SIZE * 2, MAP_PIXEL_SIZE); const map = new TileMap( - [MAP_PADDING, MAP_PADDING], + [MAP_PADDING + SIDEBAR_SIZE, MAP_PADDING], MAP_SIZE, TILE_SIZE, ); -const spinner = new Spinner([MAP_PIXEL_SIZE, 0], [SIDEBAR_SIZE, SIDEBAR_SIZE]); +const spinner = new Spinner([MAP_PIXEL_SIZE + SIDEBAR_SIZE, 0], [SIDEBAR_SIZE, SIDEBAR_SIZE]); const inventory = new Inventory( map, - [MAP_PIXEL_SIZE + SIDEBAR_SIZE, 0], + [0, 0], [SIDEBAR_SIZE, MAP_PIXEL_SIZE], ); @@ -43,7 +43,7 @@ async function render(ctx: CanvasRenderingContext2D) { ctx.fillStyle = 'white'; ctx.fillRect(0, 0, canvas.width, canvas.height); - ctx.drawImage(bgImg, 0, 0, MAP_PIXEL_SIZE, MAP_PIXEL_SIZE); + ctx.drawImage(bgImg, SIDEBAR_SIZE, 0, MAP_PIXEL_SIZE, MAP_PIXEL_SIZE); entities.forEach(entity => entity.render(ctx)); } diff --git a/src/games/zombies/pathfinding.ts b/src/games/zombies/pathfinding.ts index 9e5b1de..4cdbbe8 100644 --- a/src/games/zombies/pathfinding.ts +++ b/src/games/zombies/pathfinding.ts @@ -43,7 +43,7 @@ namespace Pathfinding { if (current !== start && current.items.length > 0) continue; - for (const neighbor of current.connections) { + for (const neighbor of current.activeConnections) { // tentative gScore is current’s gScore plus cost to move to neighbor const tentativeG = (gScore.get(current) ?? Infinity) + distance(current, neighbor); diff --git a/src/games/zombies/tile.ts b/src/games/zombies/tile.ts index 728e5bb..468346b 100644 --- a/src/games/zombies/tile.ts +++ b/src/games/zombies/tile.ts @@ -20,6 +20,8 @@ export default class Tile extends Entity { } protected draw(ctx: CanvasRenderingContext2D) { + ctx.font = '0.2px Arial'; + if (this.hovered) { ctx.fillStyle = `rgba(255, 255, 255, 0.2)`; ctx.fillRect(0, 0, 1, 1); @@ -41,30 +43,46 @@ export default class Tile extends Entity { } if (this.items.length > 0) { if (this.isOpen) { - const item = this.items[0]; + const item = this.enemy ?? this.items[0]; ctx.drawImage(item.type, 0.1, 0.1, 0.8, 0.8); if (this.items.length > 1) { - ctx.strokeStyle = 'red'; - ctx.lineWidth = 2 / this.width; - - ctx.strokeRect(0.1, 0.1, 0.8, 0.8); + ctx.fillText('💀', 0.85, 0.15); } } else { ctx.fillStyle = 'white'; ctx.fillRect(0.1, 0.1, 0.8, 0.8); - - ctx.fillStyle = 'black'; - ctx.font = '0.2px Arial'; ctx.fillText('❓', 0.5, 0.5); } } + if (this.type === TileType.LOCKED_DOOR) { + const doorway = this.connections.find(t => t.type === TileType.DOORWAY); + let x = 0.5; + let y = 0.5; + + if (doorway) { + x += (Number(this.left < doorway.left) - Number(doorway.left < this.left)) * 0.5; + y += (Number(this.top < doorway.top) - Number(doorway.top < this.top)) * 0.5; + } + ctx.font = `0.4px Arial`; + ctx.fillText('⛔', x, y); + } } get enemy() { return this.items.find(i => i.isEnemy); } + get activeConnections() { + if (this.type === TileType.LOCKED_DOOR) { + return this.connections.filter(t => t.type !== TileType.DOORWAY); + } + if (this.type === TileType.DOORWAY) { + return this.connections.filter(t => t.type !== TileType.LOCKED_DOOR); + } + return this.connections; + } + public open() { this.isOpen = true; } diff --git a/src/games/zombies/tilemap.ts b/src/games/zombies/tilemap.ts index 3191768..da2e973 100644 --- a/src/games/zombies/tilemap.ts +++ b/src/games/zombies/tilemap.ts @@ -212,6 +212,7 @@ export default class TileMap extends Entity { if (tile.isPointInBounds(x - this.left, y - this.top)) { const path = Pathfinding.findPath(this.character.tile, tile); if (path.length > 1) { + this.character.lastDoor = path.find(t => t.type === TileType.DOOR); this.character.tile = tile; tile.open(); for (const item of tile.items.slice()) { // iterate remaining items @@ -274,14 +275,22 @@ export default class TileMap extends Entity { } public handleItemUse(character: Character, item: Item) { - const success = character.removeItem(item); + let success = character.hasItem(item); if (success) { if (item.type === ItemType.ITEM_HEAL) { character.heal(character); } else if (item.type === ItemType.WEAPON_GRENADE && this.state === GameState.FIGHT) { this.killEnemy(); + } else if (item.type === ItemType.ITEM_PLANKS && character.lastDoor?.items.length === 0) { + character.lastDoor.type = TileType.LOCKED_DOOR; + } else { + success = false; + console.warn(`Dont know how to use ${item}`); } } + if (success) { + character.removeItem(item); + } } private findAvailableTiles(moveDistance: number = 1) {