Fixed bug with vision when standing in doorway
This commit is contained in:
@@ -2,7 +2,7 @@ import type { World, EntityId, Action, SimEvent, Actor, CombatantActor, Collecti
|
||||
import { calculateDamage } from "../gameplay/CombatLogic";
|
||||
|
||||
import { isBlocked, tryDestructTile } from "../world/world-logic";
|
||||
import { isDestructibleByWalk } from "../../core/terrain";
|
||||
import { isDestructibleByWalk, TileType } from "../../core/terrain";
|
||||
import { GAME_CONFIG } from "../../core/config/GameConfig";
|
||||
import { type EntityAccessor } from "../EntityAccessor";
|
||||
import { AISystem } from "../ecs/AISystem";
|
||||
@@ -102,8 +102,25 @@ function handleMove(w: World, actor: Actor, action: { dx: number; dy: number },
|
||||
const events: SimEvent[] = [{ type: "moved", actorId: actor.id, from, to }];
|
||||
|
||||
const tileIdx = ny * w.width + nx;
|
||||
if (isDestructibleByWalk(w.tiles[tileIdx])) {
|
||||
tryDestructTile(w, nx, ny);
|
||||
const tile = w.tiles[tileIdx];
|
||||
if (isDestructibleByWalk(tile)) {
|
||||
// Only open if it's currently closed.
|
||||
// tryDestructTile toggles, so we must be specific for doors.
|
||||
if (tile === TileType.DOOR_CLOSED) {
|
||||
tryDestructTile(w, nx, ny);
|
||||
} else if (tile !== TileType.DOOR_OPEN) {
|
||||
// For other destructibles like grass
|
||||
tryDestructTile(w, nx, ny);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle "from" tile - Close door if we just left it and no one else is there
|
||||
const fromIdx = from.y * w.width + from.x;
|
||||
if (w.tiles[fromIdx] === TileType.DOOR_OPEN) {
|
||||
const actorsLeft = accessor.getActorsAt(from.x, from.y);
|
||||
if (actorsLeft.length === 0) {
|
||||
w.tiles[fromIdx] = TileType.DOOR_CLOSED;
|
||||
}
|
||||
}
|
||||
|
||||
if (actor.category === "combatant" && actor.isPlayer) {
|
||||
|
||||
@@ -13,6 +13,7 @@ export class FovManager {
|
||||
private visibleStrength!: Float32Array;
|
||||
private worldWidth: number = 0;
|
||||
private worldHeight: number = 0;
|
||||
private currentOrigin: { x: number; y: number } = { x: 0, y: 0 };
|
||||
|
||||
initialize(world: World) {
|
||||
this.worldWidth = world.width;
|
||||
@@ -22,6 +23,10 @@ export class FovManager {
|
||||
this.visibleStrength = new Float32Array(world.width * world.height);
|
||||
|
||||
this.fov = new FOV.PreciseShadowcasting((x: number, y: number) => {
|
||||
// Best practice: Origin is always transparent to itself,
|
||||
// otherwise vision is blocked if standing on an opaque tile (like a doorway).
|
||||
if (x === this.currentOrigin.x && y === this.currentOrigin.y) return true;
|
||||
|
||||
if (!inBounds(world, x, y)) return false;
|
||||
const idx = y * world.width + x;
|
||||
return !blocksSight(world.tiles[idx]);
|
||||
@@ -29,6 +34,7 @@ export class FovManager {
|
||||
}
|
||||
|
||||
compute(world: World, origin: { x: number; y: number }) {
|
||||
this.currentOrigin = origin;
|
||||
this.visible.fill(0);
|
||||
this.visibleStrength.fill(0);
|
||||
|
||||
|
||||
65
src/rendering/__tests__/FovManager.repro.test.ts
Normal file
65
src/rendering/__tests__/FovManager.repro.test.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
|
||||
// Mock Phaser
|
||||
vi.mock('phaser', () => ({
|
||||
default: {
|
||||
Math: {
|
||||
Clamp: (v: number, min: number, max: number) => Math.min(Math.max(v, min), max)
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
import { FovManager } from '../FovManager';
|
||||
import { TileType } from '../../core/terrain';
|
||||
import { type World } from '../../core/types';
|
||||
|
||||
describe('FovManager Repro', () => {
|
||||
let fovManager: FovManager;
|
||||
let world: World;
|
||||
|
||||
beforeEach(() => {
|
||||
world = {
|
||||
width: 11,
|
||||
height: 11,
|
||||
tiles: new Array(11 * 11).fill(TileType.EMPTY),
|
||||
exit: { x: 10, y: 10 },
|
||||
trackPath: []
|
||||
};
|
||||
fovManager = new FovManager();
|
||||
});
|
||||
|
||||
it('should see through a doorway when standing in it (open door)', () => {
|
||||
// Create a vertical wall at x=5 with a door at (5,5)
|
||||
for (let y = 0; y < 11; y++) {
|
||||
if (y === 5) {
|
||||
world.tiles[y * 11 + 5] = TileType.DOOR_OPEN;
|
||||
} else {
|
||||
world.tiles[y * 11 + 5] = TileType.WALL;
|
||||
}
|
||||
}
|
||||
|
||||
fovManager.initialize(world);
|
||||
fovManager.compute(world, { x: 5, y: 5 });
|
||||
|
||||
expect(fovManager.isVisible(4, 5)).toBe(true);
|
||||
expect(fovManager.isVisible(6, 5)).toBe(true);
|
||||
});
|
||||
|
||||
it('should NOT be blind when standing on an opaque tile (like a closed door) AFTER FIX', () => {
|
||||
// Create a horizontal wall with a closed door at (5,5)
|
||||
for (let x = 0; x < 11; x++) {
|
||||
if (x === 5) {
|
||||
world.tiles[5 * 11 + x] = TileType.DOOR_CLOSED;
|
||||
} else {
|
||||
world.tiles[5 * 11 + x] = TileType.WALL;
|
||||
}
|
||||
}
|
||||
|
||||
fovManager.initialize(world);
|
||||
fovManager.compute(world, { x: 5, y: 5 });
|
||||
|
||||
// AFTER FIX: should see tiles on both sides of the door
|
||||
expect(fovManager.isVisible(5, 4)).toBe(true);
|
||||
expect(fovManager.isVisible(5, 6)).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user