Hide sprites of corpses when in fog of war
This commit is contained in:
@@ -360,6 +360,7 @@ export class DungeonRenderer {
|
||||
}
|
||||
|
||||
this.minimapRenderer.render(this.world, seen, visible, this.entityAccessor);
|
||||
this.fxRenderer.updateVisibility(seen, visible, this.world.width);
|
||||
}
|
||||
|
||||
// FX Delegations
|
||||
|
||||
@@ -5,7 +5,7 @@ import { GAME_CONFIG } from "../core/config/GameConfig";
|
||||
|
||||
export class FxRenderer {
|
||||
private scene: Phaser.Scene;
|
||||
private corpseSprites: Phaser.GameObjects.Sprite[] = [];
|
||||
private corpseSprites: { sprite: Phaser.GameObjects.Sprite; x: number; y: number }[] = [];
|
||||
|
||||
constructor(scene: Phaser.Scene) {
|
||||
this.scene = scene;
|
||||
@@ -34,8 +34,8 @@ export class FxRenderer {
|
||||
}
|
||||
|
||||
clearCorpses() {
|
||||
for (const sprite of this.corpseSprites) {
|
||||
sprite.destroy();
|
||||
for (const entry of this.corpseSprites) {
|
||||
entry.sprite.destroy();
|
||||
}
|
||||
this.corpseSprites = [];
|
||||
}
|
||||
@@ -142,7 +142,26 @@ export class FxRenderer {
|
||||
);
|
||||
corpse.setDepth(50);
|
||||
corpse.play(`${textureKey}-die`);
|
||||
this.corpseSprites.push(corpse);
|
||||
this.corpseSprites.push({ sprite: corpse, x, y });
|
||||
}
|
||||
|
||||
updateVisibility(seen: Uint8Array, visible: Uint8Array, worldWidth: number) {
|
||||
for (const entry of this.corpseSprites) {
|
||||
const idx = entry.y * worldWidth + entry.x;
|
||||
const isSeen = seen[idx] === 1;
|
||||
const isVisible = visible[idx] === 1;
|
||||
|
||||
entry.sprite.setVisible(isSeen);
|
||||
if (isSeen) {
|
||||
if (isVisible) {
|
||||
entry.sprite.setAlpha(1);
|
||||
entry.sprite.clearTint();
|
||||
} else {
|
||||
entry.sprite.setAlpha(0.4);
|
||||
entry.sprite.setTint(0x888888);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showWait(x: number, y: number) {
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
|
||||
import '../../__tests__/test-setup';
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { DungeonRenderer } from '../DungeonRenderer';
|
||||
import type { World, EntityId } from '../../core/types';
|
||||
import { ECSWorld } from '../../engine/ecs/World';
|
||||
import { EntityAccessor } from '../../engine/EntityAccessor';
|
||||
|
||||
// Mock Phaser
|
||||
// Mock Phaser - must be before imports that use it
|
||||
vi.mock('phaser', () => {
|
||||
const mockSprite = {
|
||||
setDepth: vi.fn().mockReturnThis(),
|
||||
@@ -65,6 +61,11 @@ vi.mock('phaser', () => {
|
||||
};
|
||||
});
|
||||
|
||||
import { DungeonRenderer } from '../DungeonRenderer';
|
||||
import type { World, EntityId } from '../../core/types';
|
||||
import { ECSWorld } from '../../engine/ecs/World';
|
||||
import { EntityAccessor } from '../../engine/EntityAccessor';
|
||||
|
||||
describe('DungeonRenderer', () => {
|
||||
let mockScene: any;
|
||||
let renderer: DungeonRenderer;
|
||||
|
||||
89
src/rendering/__tests__/FxRenderer.test.ts
Normal file
89
src/rendering/__tests__/FxRenderer.test.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
|
||||
import '../../__tests__/test-setup';
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
|
||||
// Mock Phaser - must be before imports that use it
|
||||
vi.mock('phaser', () => {
|
||||
const mockSprite = {
|
||||
setDepth: vi.fn().mockReturnThis(),
|
||||
play: vi.fn().mockReturnThis(),
|
||||
setVisible: vi.fn().mockReturnThis(),
|
||||
setAlpha: vi.fn().mockReturnThis(),
|
||||
setTint: vi.fn().mockReturnThis(),
|
||||
clearTint: vi.fn().mockReturnThis(),
|
||||
destroy: vi.fn(),
|
||||
};
|
||||
|
||||
return {
|
||||
default: {
|
||||
GameObjects: {
|
||||
Sprite: vi.fn(() => mockSprite),
|
||||
},
|
||||
Scene: vi.fn(),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
import { FxRenderer } from '../FxRenderer';
|
||||
|
||||
describe('FxRenderer', () => {
|
||||
let mockScene: any;
|
||||
let fxRenderer: FxRenderer;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
mockScene = {
|
||||
add: {
|
||||
sprite: vi.fn(() => ({
|
||||
setDepth: vi.fn().mockReturnThis(),
|
||||
play: vi.fn().mockReturnThis(),
|
||||
setVisible: vi.fn().mockReturnThis(),
|
||||
setAlpha: vi.fn().mockReturnThis(),
|
||||
setTint: vi.fn().mockReturnThis(),
|
||||
clearTint: vi.fn().mockReturnThis(),
|
||||
destroy: vi.fn(),
|
||||
})),
|
||||
text: vi.fn(() => ({
|
||||
setOrigin: vi.fn().mockReturnThis(),
|
||||
setDepth: vi.fn().mockReturnThis(),
|
||||
destroy: vi.fn(),
|
||||
})),
|
||||
},
|
||||
tweens: {
|
||||
add: vi.fn(),
|
||||
},
|
||||
};
|
||||
|
||||
fxRenderer = new FxRenderer(mockScene);
|
||||
});
|
||||
|
||||
it('should update corpse visibility and appearance based on FOV', () => {
|
||||
// Spawn a corpse at (5, 5)
|
||||
fxRenderer.spawnCorpse(5, 5, 'rat');
|
||||
const corpseSprite = mockScene.add.sprite.mock.results[0].value;
|
||||
|
||||
const seen = new Uint8Array(100).fill(0);
|
||||
const visible = new Uint8Array(100).fill(0);
|
||||
const worldWidth = 10;
|
||||
const idx = 5 * worldWidth + 5;
|
||||
|
||||
// Case 1: Unseen tile
|
||||
fxRenderer.updateVisibility(seen, visible, worldWidth);
|
||||
expect(corpseSprite.setVisible).toHaveBeenCalledWith(false);
|
||||
|
||||
// Case 2: Seen but not currently visible (dimmed)
|
||||
seen[idx] = 1;
|
||||
fxRenderer.updateVisibility(seen, visible, worldWidth);
|
||||
expect(corpseSprite.setVisible).toHaveBeenCalledWith(true);
|
||||
expect(corpseSprite.setAlpha).toHaveBeenCalledWith(0.4);
|
||||
expect(corpseSprite.setTint).toHaveBeenCalledWith(0x888888);
|
||||
|
||||
// Case 3: Currently visible (full brightness)
|
||||
visible[idx] = 1;
|
||||
fxRenderer.updateVisibility(seen, visible, worldWidth);
|
||||
expect(corpseSprite.setVisible).toHaveBeenCalledWith(true);
|
||||
expect(corpseSprite.setAlpha).toHaveBeenCalledWith(1);
|
||||
expect(corpseSprite.clearTint).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user