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.minimapRenderer.render(this.world, seen, visible, this.entityAccessor);
|
||||||
|
this.fxRenderer.updateVisibility(seen, visible, this.world.width);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FX Delegations
|
// FX Delegations
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { GAME_CONFIG } from "../core/config/GameConfig";
|
|||||||
|
|
||||||
export class FxRenderer {
|
export class FxRenderer {
|
||||||
private scene: Phaser.Scene;
|
private scene: Phaser.Scene;
|
||||||
private corpseSprites: Phaser.GameObjects.Sprite[] = [];
|
private corpseSprites: { sprite: Phaser.GameObjects.Sprite; x: number; y: number }[] = [];
|
||||||
|
|
||||||
constructor(scene: Phaser.Scene) {
|
constructor(scene: Phaser.Scene) {
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
@@ -34,8 +34,8 @@ export class FxRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
clearCorpses() {
|
clearCorpses() {
|
||||||
for (const sprite of this.corpseSprites) {
|
for (const entry of this.corpseSprites) {
|
||||||
sprite.destroy();
|
entry.sprite.destroy();
|
||||||
}
|
}
|
||||||
this.corpseSprites = [];
|
this.corpseSprites = [];
|
||||||
}
|
}
|
||||||
@@ -142,7 +142,26 @@ export class FxRenderer {
|
|||||||
);
|
);
|
||||||
corpse.setDepth(50);
|
corpse.setDepth(50);
|
||||||
corpse.play(`${textureKey}-die`);
|
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) {
|
showWait(x: number, y: number) {
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
|
import '../../__tests__/test-setup';
|
||||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
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', () => {
|
vi.mock('phaser', () => {
|
||||||
const mockSprite = {
|
const mockSprite = {
|
||||||
setDepth: vi.fn().mockReturnThis(),
|
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', () => {
|
describe('DungeonRenderer', () => {
|
||||||
let mockScene: any;
|
let mockScene: any;
|
||||||
let renderer: DungeonRenderer;
|
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