From bad3d62449d5039035c6c994912cb7068836be55 Mon Sep 17 00:00:00 2001 From: Pabloader Date: Tue, 1 Jul 2025 11:47:38 +0000 Subject: [PATCH] Fix transfer --- src/games/zombies/index.ts | 2 +- src/games/zombies/inventory.ts | 54 ++++++++++++++++++++++---------- src/games/zombies/player.ts | 35 +++++++++++++++++++-- src/games/zombies/tile.ts | 21 ++++++++++++- src/games/zombies/tilemap.ts | 57 ++++++++++++++-------------------- 5 files changed, 113 insertions(+), 56 deletions(-) diff --git a/src/games/zombies/index.ts b/src/games/zombies/index.ts index 4aa2ab0..1359158 100644 --- a/src/games/zombies/index.ts +++ b/src/games/zombies/index.ts @@ -11,7 +11,7 @@ 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 = clamp((window.innerWidth - window.innerHeight) / 2, 300, 600); +const SIDEBAR_SIZE = clamp((window.innerWidth - window.innerHeight) / 2, MAP_PADDING + 5 * TILE_SIZE, window.innerHeight / 2); const canvas = createCanvas(MAP_PIXEL_SIZE + SIDEBAR_SIZE * 2, MAP_PIXEL_SIZE); diff --git a/src/games/zombies/inventory.ts b/src/games/zombies/inventory.ts index 26c9bb5..89179b0 100644 --- a/src/games/zombies/inventory.ts +++ b/src/games/zombies/inventory.ts @@ -74,7 +74,9 @@ export default class Inventory extends Entity { player: this.players[playerIndex], transfer: true, })) - .filter(({ player }) => this.map.canTransferTo(this.player, player)), + .filter(({ player }) => + this.players.some(p => this.map.canTransferTo(p, player)) + ), ...this.healButtons .slice(0, this.players.length) @@ -84,22 +86,21 @@ export default class Inventory extends Entity { heal: true, })) .filter(({ player }) => - this.player.hasItem(ItemType.ITEM_HEALING_KIT) - && this.map.canHeal(this.player, player) + this.players.some(healer => this.map.canHeal(healer, player)) ), ...this.tiles .slice(0, this.players.length) .flatMap((column, playerIndex) => column - .slice(0, this.players[playerIndex].inventory.length) + .slice(0, this.players[playerIndex].numItems) .map((tile, tileIndex) => ({ tile, player: this.players[playerIndex], - item: this.players[playerIndex].inventory[tileIndex], + item: this.players[playerIndex].getItem(tileIndex), })) .filter(({ item, player }) => this.transferTarget - ? this.player === player + ? this.map.canTransferTo(player, this.transferTarget) : item.isConsumable) ) ]; @@ -109,11 +110,11 @@ export default class Inventory extends Entity { for (const { tile, item, player, transfer, heal } of this.activeTiles) { if (tile.isPointInBounds(x - this.left, y - this.top)) { if (heal) { - this.map.handleHeal(player); + this.handleHeal(player); } else if (transfer) { this.handleToggleTransfer(player); } else if (this.transferTarget && item) { - this.handleTransfer(item); + this.handleTransfer(player, item); } else if (item) { this.map.handleItemUse(player, item); } else if (player !== this.player) { @@ -137,16 +138,33 @@ export default class Inventory extends Entity { } } - private handleTransfer(item: Item) { + private handleTransfer(player: Player, item: Item) { if (this.transferTarget) { - const itemExisted = this.player.removeItem(item); + const itemExisted = player.removeItem(item); if (itemExisted) { - this.transferTarget.inventory.push(item); + this.transferTarget.addItem(item); } } this.transferTarget = undefined; } + private handleHeal(target: Player) { + const healers = [ + this.players.find(p => p.type === ItemType.CHAR_NURSE), + target, + ...this.players, + ]; + for (const healer of healers) { + if (!healer || !this.map.canHeal(healer, target)) continue; + + const itemExisted = healer.removeItem(healer.healingKit); + if (itemExisted) { + healer.heal(target); + return; + } + } + } + private drawPlayer(ctx: CanvasRenderingContext2D, idx: number) { const player = this.players[idx]; @@ -167,7 +185,7 @@ export default class Inventory extends Entity { } let y = 2; - for (const item of player.inventory) { + for (const item of player) { ctx.drawImage(item.type, 0.1, 0.1 + y, 0.8, 0.8); y += 1; } @@ -191,9 +209,12 @@ export default class Inventory extends Entity { ctx.scale(1 / this.width, 1 / this.height); ctx.font = `${0.3 * columnWidth}px Arial`; + + const activeTiles = new Set(this.activeTiles.map(({ tile }) => tile)); + this.transferButtons.slice(0, this.players.length) .map((tile, playerIdx) => ({ tile, player: this.players[playerIdx] })) - .filter(({ player }) => this.map.canTransferTo(this.player, player)) + .filter(({ tile }) => activeTiles.has(tile)) .forEach(({ tile, player }) => { if (player === this.transferTarget) { ctx.fillStyle = 'lime'; @@ -205,12 +226,11 @@ export default class Inventory extends Entity { ctx.fillStyle = 'black'; this.healButtons.slice(0, this.players.length) - .map((tile, playerIdx) => ({ tile, player: this.players[playerIdx] })) - .filter(({ player }) => this.map.canHeal(this.player, player)) - .forEach(({ tile }) => { + .filter((tile) => activeTiles.has(tile)) + .forEach((tile) => { ctx.fillText('🩹', tile.centerX, tile.centerY); }); - this.activeTiles.forEach(({ tile }) => tile.render(ctx)); + activeTiles.forEach((tile) => tile.render(ctx)); } } \ No newline at end of file diff --git a/src/games/zombies/player.ts b/src/games/zombies/player.ts index 3a60c82..4097286 100644 --- a/src/games/zombies/player.ts +++ b/src/games/zombies/player.ts @@ -5,10 +5,11 @@ import Tile from "./tile"; export default class Player extends Character { public health: number; - public inventory: Item[] = []; public lastDoor: Tile | undefined; public active = true; + private inventory: Item[] = []; + constructor(type: ItemTypeImage) { super(type); this.health = this.maxHealth; @@ -68,8 +69,18 @@ export default class Player extends Character { return this.inventory.find(i => i.type === ItemType.ITEM_HEALING_KIT); } - public heal(player: Player) { - player.health += this.healingAmount; + public heal(patient: Player) { + patient.health += this.healingAmount; + } + + public addItem(item: Item | null | undefined) { + if (item) { + this.inventory.push(item); + } + } + + public getItem(i: number) { + return this.inventory[i]; } public removeItem(item: Item | null | undefined): boolean { @@ -86,6 +97,12 @@ export default class Player extends Character { return false; } + public removeAllItems(): Item[] { + const inventory = this.inventory.slice(); + this.inventory = []; + return inventory; + } + public hasItem(item: Item | ItemTypeImage | null | undefined): boolean { if (!item) { return false; @@ -95,6 +112,18 @@ export default class Player extends Character { return itemIndex >= 0; } + public get hasAnyItems() { + return this.inventory.length > 0; + } + + public get numItems() { + return this.inventory.length; + } + + public [Symbol.iterator]() { + return this.inventory.slice()[Symbol.iterator](); + } + public toggleActive() { this.active = !this.active; } diff --git a/src/games/zombies/tile.ts b/src/games/zombies/tile.ts index ecaa4eb..7c5ef5c 100644 --- a/src/games/zombies/tile.ts +++ b/src/games/zombies/tile.ts @@ -14,9 +14,10 @@ export enum TileType { export default class Tile extends Entity { public connections: Tile[] = []; - public items: Item[] = []; public isOpen = false; + private items: Item[] = []; + constructor(position: [number, number], size: number, public type: TileType = TileType.NORMAL) { super(position, [size, size]); } @@ -101,6 +102,12 @@ export default class Tile extends Entity { this.isOpen = true; } + public addItem(item: Item | null | undefined) { + if (item) { + this.items.push(item); + } + } + public removeItem(item: Item | null | undefined): boolean { if (!item) { return false; @@ -115,6 +122,18 @@ export default class Tile extends Entity { return false; } + public get hasSingleItem() { + return this.items.length === 1; + } + + public get hasAnyItems() { + return this.items.length > 0; + } + + public [Symbol.iterator]() { + return this.items.slice()[Symbol.iterator](); + } + public killEnemy() { this.removeItem(this.enemy); } diff --git a/src/games/zombies/tilemap.ts b/src/games/zombies/tilemap.ts index 7b2a0d0..9886304 100644 --- a/src/games/zombies/tilemap.ts +++ b/src/games/zombies/tilemap.ts @@ -190,30 +190,28 @@ export default class TileMap extends Entity { } const endTiles = this.tiles.filter(t => t.type === TileType.END); - const endTilesNeighbors = new Set(endTiles.flatMap(t => t.connections)); const doorTiles = this.tiles.filter(t => t.type === TileType.DOOR || t.type === TileType.DOORWAY); const normalTiles = this.tiles.filter(t => t.type === TileType.NORMAL - && !endTilesNeighbors.has(t) && !this.startTile.connections.includes(t) ); const fillableTiles = [ - ...endTilesNeighbors, + ...endTiles, ...this.startTile.connections, ...doorTiles, ...shuffle(normalTiles), ]; for (const [tile, item] of zip(fillableTiles, shuffle(items))) { - tile.items.push(item); + tile.addItem(item); if (item instanceof Character) { item.tile = tile; } } for (const tile of this.tiles) { - if (tile.items.length === 0) { + if (!tile.hasAnyItems) { tile.isOpen = true; } } @@ -225,7 +223,7 @@ export default class TileMap extends Entity { get activeTiles() { return this.tiles.filter(tile => ( - tile.items.length > 0 + tile.hasAnyItems || tile.type === TileType.LOCKED_DOOR || tile.hovered || (this.state === GameState.NORMAL && this.availableTiles.includes(tile)) @@ -237,20 +235,20 @@ export default class TileMap extends Entity { (tile) => tile.isOpen && tile.enemy - && tile.items.length === 1 + && tile.hasSingleItem ) } - public canTransferTo(player: Player, otherPlayer: Player): boolean { - if (otherPlayer === player) return false; + public canTransferTo(player: Player, target: Player): boolean { + if (target === player) return false; - if (player.inventory.length === 0) return false; + if (!player.hasAnyItems) return false; - if (player.tile === otherPlayer.tile) { + if (player.tile === target.tile) { return true; } for (const tile of player.tile.connections) { - if (otherPlayer.tile === tile) { + if (target.tile === tile) { return true; } } @@ -258,19 +256,10 @@ export default class TileMap extends Entity { return false; } - public canHeal(player: Player, otherPlayer: Player): boolean { - if (!player.healingKit) return false; + public canHeal(healer: Player, patient: Player): boolean { + if (!healer.healingKit) return false; - return player === otherPlayer || this.canTransferTo(player, otherPlayer); - } - - public handleHeal(target: Player) { - if (target) { - const itemExisted = this.player.removeItem(this.player.healingKit); - if (itemExisted) { - this.player.heal(target); - } - } + return healer === patient || this.canTransferTo(healer, patient); } public override handleMouseMove(x: number, y: number): void { @@ -289,11 +278,11 @@ export default class TileMap extends Entity { this.player.lastDoor = path.find(t => t.type === TileType.DOOR); this.player.moveTo(tile, path); tile.open(); - for (const item of tile.items.slice()) { // iterate remaining items + for (const item of tile) { if (item.isPickable) { if (!tile.enemy) { tile.removeItem(item); - this.player.inventory.push(item); + this.player.addItem(item); } } else if (item instanceof Player) { tile.removeItem(item); @@ -339,7 +328,7 @@ export default class TileMap extends Entity { switch (action) { case SpinnerAction.BITE: if (this.player.isDead) { - this.player.tile.items.push(...this.player.inventory); + this.player.removeAllItems().forEach(i => this.player.tile.addItem(i)); this.players.splice(this.currentPlayerIdx, 1); this.currentPlayerIdx = this.currentPlayerIdx % this.players.length; this.setNormalState(); @@ -427,7 +416,7 @@ export default class TileMap extends Entity { const moves = Pathfinding.findPossibleMoves(enemyTile, 5); for (const move of shuffle(moves)) { - if (move.items.length > 0) continue; + if (move.hasAnyItems) continue; for (const [playerIndex, player] of enumerate(this.players)) { if (player.tile === move) { @@ -442,7 +431,7 @@ export default class TileMap extends Entity { const targetTile = path[allowedSteps] ?? player.tile; enemyTile.removeItem(enemy); - targetTile.items.push(enemy); + targetTile.addItem(enemy); enemy.moveTo(targetTile, path.slice(0, allowedSteps + 1)); if (targetTile === player.tile) { @@ -460,12 +449,12 @@ export default class TileMap extends Entity { private killEnemy() { this.player.tile.killEnemy(); - this.player.tile.items.slice().forEach((item) => { + for (const item of this.player.tile) { if (item.isPickable) { - this.player.inventory.push(item); + this.player.addItem(item); this.player.tile.removeItem(item); } - }); + }; this.nextPlayer(); this.setNormalState(); } @@ -476,7 +465,7 @@ export default class TileMap extends Entity { } const endTiles = this.tiles.filter(t => t.type === TileType.END); for (const tile of endTiles) { - if (tile.items.length !== 0) return false; + if (tile.hasAnyItems) return false; } for (const player of Object.values(Players)) { if (!this.foundPlayers.has(player)) return false; @@ -539,7 +528,7 @@ export default class TileMap extends Entity { for (const tile of this.tiles) { if (tile.type !== TileType.END) continue; - this.drawItem(ctx, ItemType.ENEMY_ZOMBIE, x, y, w, tile.items.length === 0); + this.drawItem(ctx, ItemType.ENEMY_ZOMBIE, x, y, w, !tile.hasAnyItems); x += this.tileSize; } }