Add more stats, crit/block/accuracy/dodge/lifesteal
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { applyAction } from '../simulation/simulation';
|
||||
import { type World, type Actor, type EntityId } from '../../core/types';
|
||||
import { type World, type Actor, type EntityId, type CombatantActor } from '../../core/types';
|
||||
|
||||
describe('Combat Simulation', () => {
|
||||
const createTestWorld = (actors: Map<EntityId, Actor>): World => ({
|
||||
@@ -14,6 +14,14 @@ describe('Combat Simulation', () => {
|
||||
const createTestStats = (overrides: Partial<any> = {}) => ({
|
||||
maxHp: 20, hp: 20, attack: 5, defense: 2, level: 1, exp: 0, expToNextLevel: 10,
|
||||
statPoints: 0, skillPoints: 0, strength: 10, dexterity: 10, intelligence: 10, passiveNodes: [],
|
||||
// New stats (defaults for tests to keep them deterministic)
|
||||
critChance: 0,
|
||||
critMultiplier: 100,
|
||||
accuracy: 100, // Always hit in tests unless specified
|
||||
lifesteal: 0,
|
||||
evasion: 0,
|
||||
blockChance: 0,
|
||||
luck: 0,
|
||||
...overrides
|
||||
});
|
||||
|
||||
@@ -22,7 +30,9 @@ describe('Combat Simulation', () => {
|
||||
const actors = new Map<EntityId, Actor>();
|
||||
actors.set(1, {
|
||||
id: 1,
|
||||
category: "combatant",
|
||||
isPlayer: true,
|
||||
type: "player",
|
||||
pos: { x: 3, y: 3 },
|
||||
speed: 100,
|
||||
energy: 0,
|
||||
@@ -30,7 +40,9 @@ describe('Combat Simulation', () => {
|
||||
});
|
||||
actors.set(2, {
|
||||
id: 2,
|
||||
category: "combatant",
|
||||
isPlayer: false,
|
||||
type: "rat",
|
||||
pos: { x: 4, y: 3 },
|
||||
speed: 100,
|
||||
energy: 0,
|
||||
@@ -38,20 +50,22 @@ describe('Combat Simulation', () => {
|
||||
});
|
||||
|
||||
const world = createTestWorld(actors);
|
||||
const events = applyAction(world, 1, { type: 'attack', targetId: 2 });
|
||||
const events = applyAction(world, 1, { type: "attack", targetId: 2 });
|
||||
|
||||
const enemy = world.actors.get(2)!;
|
||||
expect(enemy.stats!.hp).toBeLessThan(10);
|
||||
const enemy = world.actors.get(2) as CombatantActor;
|
||||
expect(enemy.stats.hp).toBeLessThan(10);
|
||||
|
||||
// Should have attack event
|
||||
expect(events.some(e => e.type === 'attacked')).toBe(true);
|
||||
expect(events.some(e => e.type === "attacked")).toBe(true);
|
||||
});
|
||||
|
||||
it('should kill enemy when damage exceeds hp', () => {
|
||||
it("should kill enemy when damage exceeds hp", () => {
|
||||
const actors = new Map<EntityId, Actor>();
|
||||
actors.set(1, {
|
||||
id: 1,
|
||||
category: "combatant",
|
||||
isPlayer: true,
|
||||
type: "player",
|
||||
pos: { x: 3, y: 3 },
|
||||
speed: 100,
|
||||
energy: 0,
|
||||
@@ -59,7 +73,9 @@ describe('Combat Simulation', () => {
|
||||
});
|
||||
actors.set(2, {
|
||||
id: 2,
|
||||
category: "combatant",
|
||||
isPlayer: false,
|
||||
type: "rat",
|
||||
pos: { x: 4, y: 3 },
|
||||
speed: 100,
|
||||
energy: 0,
|
||||
@@ -67,20 +83,22 @@ describe('Combat Simulation', () => {
|
||||
});
|
||||
|
||||
const world = createTestWorld(actors);
|
||||
const events = applyAction(world, 1, { type: 'attack', targetId: 2 });
|
||||
const events = applyAction(world, 1, { type: "attack", targetId: 2 });
|
||||
|
||||
// Enemy should be removed from world
|
||||
expect(world.actors.has(2)).toBe(false);
|
||||
|
||||
// Should have killed event
|
||||
expect(events.some(e => e.type === 'killed')).toBe(true);
|
||||
expect(events.some(e => e.type === "killed")).toBe(true);
|
||||
});
|
||||
|
||||
it('should apply defense to reduce damage', () => {
|
||||
it("should apply defense to reduce damage", () => {
|
||||
const actors = new Map<EntityId, Actor>();
|
||||
actors.set(1, {
|
||||
id: 1,
|
||||
category: "combatant",
|
||||
isPlayer: true,
|
||||
type: "player",
|
||||
pos: { x: 3, y: 3 },
|
||||
speed: 100,
|
||||
energy: 0,
|
||||
@@ -88,7 +106,9 @@ describe('Combat Simulation', () => {
|
||||
});
|
||||
actors.set(2, {
|
||||
id: 2,
|
||||
category: "combatant",
|
||||
isPlayer: false,
|
||||
type: "rat",
|
||||
pos: { x: 4, y: 3 },
|
||||
speed: 100,
|
||||
energy: 0,
|
||||
@@ -96,22 +116,98 @@ describe('Combat Simulation', () => {
|
||||
});
|
||||
|
||||
const world = createTestWorld(actors);
|
||||
applyAction(world, 1, { type: 'attack', targetId: 2 });
|
||||
applyAction(world, 1, { type: "attack", targetId: 2 });
|
||||
|
||||
const enemy = world.actors.get(2)!;
|
||||
const damage = 10 - enemy.stats!. hp;
|
||||
const enemy = world.actors.get(2) as CombatantActor;
|
||||
const damage = 10 - enemy.stats.hp;
|
||||
|
||||
// Damage should be reduced by defense (5 attack - 3 defense = 2 damage)
|
||||
expect(damage).toBe(2);
|
||||
});
|
||||
|
||||
it("should emit dodged event when attack misses", () => {
|
||||
const actors = new Map<EntityId, Actor>();
|
||||
actors.set(1, {
|
||||
id: 1, category: "combatant", isPlayer: true, type: "player", pos: { x: 3, y: 3 }, speed: 100, energy: 0,
|
||||
stats: createTestStats({ accuracy: 0 }) // Force miss
|
||||
});
|
||||
actors.set(2, {
|
||||
id: 2, category: "combatant", isPlayer: false, type: "rat", pos: { x: 4, y: 3 }, speed: 100, energy: 0,
|
||||
stats: createTestStats({ evasion: 0 })
|
||||
});
|
||||
|
||||
const world = createTestWorld(actors);
|
||||
const events = applyAction(world, 1, { type: "attack", targetId: 2 });
|
||||
|
||||
expect(events.some(e => e.type === "dodged")).toBe(true);
|
||||
expect((world.actors.get(2) as CombatantActor).stats.hp).toBe(20);
|
||||
});
|
||||
|
||||
it("should emit crit flag when critical strike occurs", () => {
|
||||
const actors = new Map<EntityId, Actor>();
|
||||
actors.set(1, {
|
||||
id: 1, category: "combatant", isPlayer: true, type: "player", pos: { x: 3, y: 3 }, speed: 100, energy: 0,
|
||||
stats: createTestStats({ critChance: 100, critMultiplier: 200, attack: 10 })
|
||||
});
|
||||
actors.set(2, {
|
||||
id: 2, category: "combatant", isPlayer: false, type: "rat", pos: { x: 4, y: 3 }, speed: 100, energy: 0,
|
||||
stats: createTestStats({ defense: 0, hp: 100 })
|
||||
});
|
||||
|
||||
const world = createTestWorld(actors);
|
||||
const events = applyAction(world, 1, { type: "attack", targetId: 2 });
|
||||
|
||||
const damagedEvent = events.find(e => e.type === "damaged") as any;
|
||||
expect(damagedEvent.isCrit).toBe(true);
|
||||
expect(damagedEvent.amount).toBe(20); // 10 * 2.0
|
||||
});
|
||||
|
||||
it("should emit block flag and reduce damage", () => {
|
||||
const actors = new Map<EntityId, Actor>();
|
||||
actors.set(1, {
|
||||
id: 1, category: "combatant", isPlayer: true, type: "player", pos: { x: 3, y: 3 }, speed: 100, energy: 0,
|
||||
stats: createTestStats({ attack: 10 })
|
||||
});
|
||||
actors.set(2, {
|
||||
id: 2, category: "combatant", isPlayer: false, type: "rat", pos: { x: 4, y: 3 }, speed: 100, energy: 0,
|
||||
stats: createTestStats({ defense: 0, blockChance: 100, hp: 100 }) // Force block
|
||||
});
|
||||
|
||||
const world = createTestWorld(actors);
|
||||
const events = applyAction(world, 1, { type: "attack", targetId: 2 });
|
||||
|
||||
const damagedEvent = events.find(e => e.type === "damaged") as any;
|
||||
expect(damagedEvent.isBlock).toBe(true);
|
||||
expect(damagedEvent.amount).toBe(5); // 10 * 0.5
|
||||
});
|
||||
|
||||
it("should heal attacker via lifesteal", () => {
|
||||
const actors = new Map<EntityId, Actor>();
|
||||
actors.set(1, {
|
||||
id: 1, category: "combatant", isPlayer: true, type: "player", pos: { x: 3, y: 3 }, speed: 100, energy: 0,
|
||||
stats: createTestStats({ attack: 10, lifesteal: 50, hp: 5, maxHp: 20 })
|
||||
});
|
||||
actors.set(2, {
|
||||
id: 2, category: "combatant", isPlayer: false, type: "rat", pos: { x: 4, y: 3 }, speed: 100, energy: 0,
|
||||
stats: createTestStats({ defense: 0 })
|
||||
});
|
||||
|
||||
const world = createTestWorld(actors);
|
||||
const events = applyAction(world, 1, { type: "attack", targetId: 2 });
|
||||
|
||||
expect(events.some(e => e.type === "healed")).toBe(true);
|
||||
expect((world.actors.get(1) as CombatantActor).stats.hp).toBe(10); // 5 + (10 * 0.5)
|
||||
});
|
||||
});
|
||||
|
||||
describe('applyAction - move', () => {
|
||||
it('should move actor to new position', () => {
|
||||
describe("applyAction - move", () => {
|
||||
it("should move actor to new position", () => {
|
||||
const actors = new Map<EntityId, Actor>();
|
||||
actors.set(1, {
|
||||
id: 1,
|
||||
category: "combatant",
|
||||
isPlayer: true,
|
||||
type: "player",
|
||||
pos: { x: 3, y: 3 },
|
||||
speed: 100,
|
||||
energy: 0,
|
||||
@@ -119,13 +215,13 @@ describe('Combat Simulation', () => {
|
||||
});
|
||||
|
||||
const world = createTestWorld(actors);
|
||||
const events = applyAction(world, 1, { type: 'move', dx: 1, dy: 0 });
|
||||
const events = applyAction(world, 1, { type: "move", dx: 1, dy: 0 });
|
||||
|
||||
const player = world.actors.get(1)!;
|
||||
const player = world.actors.get(1) as CombatantActor;
|
||||
expect(player.pos).toEqual({ x: 4, y: 3 });
|
||||
|
||||
// Should have moved event
|
||||
expect(events.some(e => e.type === 'moved')).toBe(true);
|
||||
expect(events.some(e => e.type === "moved")).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user