Begin refactoring GameScene
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { DungeonRenderer } from '../DungeonRenderer';
|
||||
import { type World } from '../../core/types';
|
||||
import type { World, EntityId } from '../../core/types';
|
||||
import { ECSWorld } from '../../engine/ecs/World';
|
||||
import { EntityAccessor } from '../../engine/EntityAccessor';
|
||||
|
||||
// Mock Phaser
|
||||
vi.mock('phaser', () => {
|
||||
@@ -11,6 +14,10 @@ vi.mock('phaser', () => {
|
||||
setPosition: vi.fn().mockReturnThis(),
|
||||
setVisible: vi.fn().mockReturnThis(),
|
||||
destroy: vi.fn(),
|
||||
frame: { name: '0' },
|
||||
setFrame: vi.fn(),
|
||||
setAlpha: vi.fn(),
|
||||
clearTint: vi.fn(),
|
||||
};
|
||||
|
||||
const mockGraphics = {
|
||||
@@ -27,6 +34,7 @@ vi.mock('phaser', () => {
|
||||
setVisible: vi.fn().mockReturnThis(),
|
||||
setScrollFactor: vi.fn().mockReturnThis(),
|
||||
setDepth: vi.fn().mockReturnThis(),
|
||||
y: 0
|
||||
};
|
||||
|
||||
const mockRectangle = {
|
||||
@@ -41,6 +49,13 @@ vi.mock('phaser', () => {
|
||||
Graphics: vi.fn(() => mockGraphics),
|
||||
Container: vi.fn(() => mockContainer),
|
||||
Rectangle: vi.fn(() => mockRectangle),
|
||||
Arc: vi.fn(() => ({
|
||||
setStrokeStyle: vi.fn().mockReturnThis(),
|
||||
setDepth: vi.fn().mockReturnThis(),
|
||||
setPosition: vi.fn().mockReturnThis(),
|
||||
setVisible: vi.fn().mockReturnThis(),
|
||||
destroy: vi.fn(),
|
||||
})),
|
||||
},
|
||||
Scene: vi.fn(),
|
||||
Math: {
|
||||
@@ -54,6 +69,8 @@ describe('DungeonRenderer', () => {
|
||||
let mockScene: any;
|
||||
let renderer: DungeonRenderer;
|
||||
let mockWorld: World;
|
||||
let ecsWorld: ECSWorld;
|
||||
let accessor: EntityAccessor;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
@@ -72,13 +89,25 @@ describe('DungeonRenderer', () => {
|
||||
setPosition: vi.fn().mockReturnThis(),
|
||||
setVisible: vi.fn().mockReturnThis(),
|
||||
destroy: vi.fn(),
|
||||
frame: { name: '0' },
|
||||
setFrame: vi.fn(),
|
||||
setAlpha: vi.fn(),
|
||||
clearTint: vi.fn(),
|
||||
})),
|
||||
circle: vi.fn().mockReturnValue({
|
||||
setStrokeStyle: vi.fn().mockReturnThis(),
|
||||
setDepth: vi.fn().mockReturnThis(),
|
||||
setPosition: vi.fn().mockReturnThis(),
|
||||
setVisible: vi.fn().mockReturnThis(),
|
||||
destroy: vi.fn(),
|
||||
}),
|
||||
container: vi.fn().mockReturnValue({
|
||||
add: vi.fn(),
|
||||
setPosition: vi.fn(),
|
||||
setVisible: vi.fn(),
|
||||
setScrollFactor: vi.fn(),
|
||||
setDepth: vi.fn(),
|
||||
y: 0
|
||||
}),
|
||||
rectangle: vi.fn().mockReturnValue({
|
||||
setStrokeStyle: vi.fn().mockReturnThis(),
|
||||
@@ -89,6 +118,7 @@ describe('DungeonRenderer', () => {
|
||||
main: {
|
||||
width: 800,
|
||||
height: 600,
|
||||
shake: vi.fn(),
|
||||
},
|
||||
},
|
||||
anims: {
|
||||
@@ -110,6 +140,9 @@ describe('DungeonRenderer', () => {
|
||||
add: vi.fn(),
|
||||
killTweensOf: vi.fn(),
|
||||
},
|
||||
time: {
|
||||
now: 0
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -117,15 +150,16 @@ describe('DungeonRenderer', () => {
|
||||
width: 10,
|
||||
height: 10,
|
||||
tiles: new Array(100).fill(0),
|
||||
actors: new Map(),
|
||||
exit: { x: 9, y: 9 },
|
||||
};
|
||||
ecsWorld = new ECSWorld();
|
||||
accessor = new EntityAccessor(mockWorld, 1 as EntityId, ecsWorld);
|
||||
|
||||
renderer = new DungeonRenderer(mockScene);
|
||||
});
|
||||
|
||||
it('should track and clear corpse sprites on floor initialization', () => {
|
||||
renderer.initializeFloor(mockWorld, 1);
|
||||
renderer.initializeFloor(mockWorld, ecsWorld, accessor);
|
||||
|
||||
|
||||
// Spawn a couple of corpses
|
||||
@@ -133,31 +167,29 @@ describe('DungeonRenderer', () => {
|
||||
renderer.spawnCorpse(2, 2, 'bat');
|
||||
|
||||
// Get the mock sprites that were returned by scene.add.sprite
|
||||
// The player sprite is created first in initializeFloor if it doesn't exist
|
||||
// Then the two corpses
|
||||
const corpse1 = mockScene.add.sprite.mock.results[1].value;
|
||||
const corpse2 = mockScene.add.sprite.mock.results[2].value;
|
||||
|
||||
expect(mockScene.add.sprite).toHaveBeenCalledTimes(3);
|
||||
expect(mockScene.add.sprite).toHaveBeenCalledTimes(3); // Player + 2 corpses
|
||||
|
||||
// Initialize floor again (changing level)
|
||||
renderer.initializeFloor(mockWorld, 1);
|
||||
renderer.initializeFloor(mockWorld, ecsWorld, accessor);
|
||||
|
||||
|
||||
// Verify destroy was called on both corpse sprites
|
||||
// Verify destroy was called on both corpse sprites (via fxRenderer.clearCorpses)
|
||||
expect(corpse1.destroy).toHaveBeenCalledTimes(1);
|
||||
expect(corpse2.destroy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should render exp_orb as a circle and not as an enemy sprite', () => {
|
||||
renderer.initializeFloor(mockWorld, 1);
|
||||
it('should render exp_orb correctly', () => {
|
||||
renderer.initializeFloor(mockWorld, ecsWorld, accessor);
|
||||
|
||||
// Add an exp_orb to the world
|
||||
mockWorld.actors.set(2, {
|
||||
id: 2,
|
||||
category: "collectible",
|
||||
type: "exp_orb",
|
||||
pos: { x: 2, y: 1 },
|
||||
expAmount: 10
|
||||
});
|
||||
// Add an exp_orb to the ECS world
|
||||
ecsWorld.addComponent(2 as EntityId, "position", { x: 2, y: 1 });
|
||||
ecsWorld.addComponent(2 as EntityId, "collectible", { type: "exp_orb", amount: 10 });
|
||||
ecsWorld.addComponent(2 as EntityId, "actorType", { type: "exp_orb" as any });
|
||||
|
||||
// Make the tile visible for it to render
|
||||
(renderer as any).fovManager.visibleArray[1 * mockWorld.width + 2] = 1;
|
||||
@@ -165,40 +197,19 @@ describe('DungeonRenderer', () => {
|
||||
// Reset mocks
|
||||
mockScene.add.sprite.mockClear();
|
||||
|
||||
// Mock scene.add.circle
|
||||
mockScene.add.circle = vi.fn().mockReturnValue({
|
||||
setStrokeStyle: vi.fn().mockReturnThis(),
|
||||
setDepth: vi.fn().mockReturnThis(),
|
||||
setPosition: vi.fn().mockReturnThis(),
|
||||
setVisible: vi.fn().mockReturnThis(),
|
||||
});
|
||||
|
||||
renderer.render([]);
|
||||
|
||||
// Should NOT have added an enemy sprite for the orb
|
||||
const spriteCalls = mockScene.add.sprite.mock.calls;
|
||||
// Any sprite added that isn't the player (which isn't in mockWorld.actors here except if we added it)
|
||||
// The current loop skips a.isPlayer and then checks if type is in GAME_CONFIG.enemies
|
||||
expect(spriteCalls.length).toBe(0);
|
||||
|
||||
// Should HAVE added a circle for the orb
|
||||
expect(mockScene.add.circle).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should render any enemy type defined in config as a sprite', () => {
|
||||
renderer.initializeFloor(mockWorld, 1);
|
||||
it('should render any enemy type as a sprite', () => {
|
||||
renderer.initializeFloor(mockWorld, ecsWorld, accessor);
|
||||
|
||||
// Add a rat (defined in config)
|
||||
mockWorld.actors.set(3, {
|
||||
id: 3,
|
||||
category: "combatant",
|
||||
isPlayer: false,
|
||||
type: "rat",
|
||||
pos: { x: 3, y: 1 },
|
||||
speed: 10,
|
||||
stats: { hp: 10, maxHp: 10, attack: 2, defense: 0 } as any,
|
||||
energy: 10
|
||||
});
|
||||
// Add a rat
|
||||
ecsWorld.addComponent(3 as EntityId, "position", { x: 3, y: 1 });
|
||||
ecsWorld.addComponent(3 as EntityId, "actorType", { type: "rat" });
|
||||
ecsWorld.addComponent(3 as EntityId, "stats", { hp: 10, maxHp: 10 } as any);
|
||||
|
||||
(renderer as any).fovManager.visibleArray[1 * mockWorld.width + 3] = 1;
|
||||
mockScene.add.sprite.mockClear();
|
||||
@@ -211,21 +222,16 @@ describe('DungeonRenderer', () => {
|
||||
});
|
||||
|
||||
it('should initialize new enemy sprites at target position and not tween them', () => {
|
||||
renderer.initializeFloor(mockWorld, 1);
|
||||
renderer.initializeFloor(mockWorld, ecsWorld, accessor);
|
||||
|
||||
// Position 5,5 -> 5*16 + 8 = 88
|
||||
const TILE_SIZE = 16;
|
||||
const targetX = 5 * TILE_SIZE + TILE_SIZE / 2;
|
||||
const targetY = 5 * TILE_SIZE + TILE_SIZE / 2;
|
||||
|
||||
mockWorld.actors.set(999, {
|
||||
id: 999,
|
||||
category: "combatant",
|
||||
isPlayer: false,
|
||||
type: "rat",
|
||||
pos: { x: 5, y: 5 },
|
||||
stats: { hp: 10, maxHp: 10 } as any,
|
||||
} as any);
|
||||
ecsWorld.addComponent(999 as EntityId, "position", { x: 5, y: 5 });
|
||||
ecsWorld.addComponent(999 as EntityId, "actorType", { type: "rat" });
|
||||
ecsWorld.addComponent(999 as EntityId, "stats", { hp: 10, maxHp: 10 } as any);
|
||||
|
||||
(renderer as any).fovManager.visibleArray[5 * mockWorld.width + 5] = 1;
|
||||
mockScene.add.sprite.mockClear();
|
||||
|
||||
Reference in New Issue
Block a user