refactor items logic
This commit is contained in:
@@ -2,8 +2,8 @@
|
||||
import { describe, it, expect, beforeEach } from "vitest";
|
||||
import { ItemManager } from "../../../scenes/systems/ItemManager";
|
||||
import { EntityManager } from "../../EntityManager";
|
||||
import type { World, CombatantActor, RangedWeaponItem, AmmoItem } from "../../../core/types";
|
||||
import { ITEMS } from "../../../core/config/Items";
|
||||
import type { World, CombatantActor, RangedWeaponItem } from "../../../core/types";
|
||||
import { createRangedWeapon, createAmmo } from "../../../core/config/Items";
|
||||
|
||||
// Mock World and EntityManager
|
||||
const mockWorld: World = {
|
||||
@@ -50,8 +50,7 @@ describe("Fireable Weapons & Ammo System", () => {
|
||||
|
||||
it("should stack ammo correctly", () => {
|
||||
// Spawn Ammo pack 1
|
||||
const ammo1 = { ...ITEMS["ammo_9mm"] } as AmmoItem;
|
||||
ammo1.quantity = 10;
|
||||
const ammo1 = createAmmo("ammo_9mm", 10);
|
||||
itemManager.spawnItem(ammo1, { x: 0, y: 0 });
|
||||
|
||||
// Pickup
|
||||
@@ -60,8 +59,7 @@ describe("Fireable Weapons & Ammo System", () => {
|
||||
expect(player.inventory!.items[0].quantity).toBe(10);
|
||||
|
||||
// Spawn Ammo pack 2
|
||||
const ammo2 = { ...ITEMS["ammo_9mm"] } as AmmoItem;
|
||||
ammo2.quantity = 5;
|
||||
const ammo2 = createAmmo("ammo_9mm", 5);
|
||||
itemManager.spawnItem(ammo2, { x: 0, y: 0 });
|
||||
|
||||
// Pickup (should merge)
|
||||
@@ -71,82 +69,77 @@ describe("Fireable Weapons & Ammo System", () => {
|
||||
});
|
||||
|
||||
it("should consume ammo from weapon when fired", () => {
|
||||
// Manually Equip Pistol
|
||||
const pistol = { ...ITEMS["pistol"] } as RangedWeaponItem;
|
||||
// Deep clone stats for test isolation
|
||||
pistol.stats = { ...pistol.stats };
|
||||
// Create pistol using factory (already has currentAmmo initialized)
|
||||
const pistol = createRangedWeapon("pistol");
|
||||
player.inventory!.items.push(pistol);
|
||||
|
||||
// Sanity Check
|
||||
expect(pistol.stats.currentAmmo).toBe(6);
|
||||
// Sanity Check - currentAmmo is now top-level
|
||||
expect(pistol.currentAmmo).toBe(6);
|
||||
expect(pistol.stats.magazineSize).toBe(6);
|
||||
|
||||
// Simulate Firing (logic mimic from GameScene)
|
||||
if (pistol.stats.currentAmmo > 0) {
|
||||
pistol.stats.currentAmmo--;
|
||||
if (pistol.currentAmmo > 0) {
|
||||
pistol.currentAmmo--;
|
||||
}
|
||||
|
||||
expect(pistol.stats.currentAmmo).toBe(5);
|
||||
expect(pistol.currentAmmo).toBe(5);
|
||||
});
|
||||
|
||||
it("should reload weapon using inventory ammo", () => {
|
||||
const pistol = { ...ITEMS["pistol"] } as RangedWeaponItem;
|
||||
pistol.stats = { ...pistol.stats };
|
||||
pistol.stats.currentAmmo = 0; // Empty
|
||||
const pistol = createRangedWeapon("pistol");
|
||||
pistol.currentAmmo = 0; // Empty
|
||||
player.inventory!.items.push(pistol);
|
||||
|
||||
const ammo = { ...ITEMS["ammo_9mm"] } as AmmoItem;
|
||||
ammo.quantity = 10;
|
||||
const ammo = createAmmo("ammo_9mm", 10);
|
||||
player.inventory!.items.push(ammo);
|
||||
|
||||
// Logic mimic from GameScene
|
||||
const needed = pistol.stats.magazineSize - pistol.stats.currentAmmo; // 6
|
||||
const toTake = Math.min(needed, ammo.quantity); // 6
|
||||
const needed = pistol.stats.magazineSize - pistol.currentAmmo; // 6
|
||||
const toTake = Math.min(needed, ammo.quantity!); // 6
|
||||
|
||||
pistol.stats.currentAmmo += toTake;
|
||||
ammo.quantity -= toTake;
|
||||
pistol.currentAmmo += toTake;
|
||||
ammo.quantity! -= toTake;
|
||||
|
||||
expect(pistol.stats.currentAmmo).toBe(6);
|
||||
expect(pistol.currentAmmo).toBe(6);
|
||||
expect(ammo.quantity).toBe(4);
|
||||
});
|
||||
|
||||
it("should handle partial reload if not enough ammo", () => {
|
||||
const pistol = { ...ITEMS["pistol"] } as RangedWeaponItem;
|
||||
pistol.stats = { ...pistol.stats };
|
||||
pistol.stats.currentAmmo = 0;
|
||||
const pistol = createRangedWeapon("pistol");
|
||||
pistol.currentAmmo = 0;
|
||||
player.inventory!.items.push(pistol);
|
||||
|
||||
const ammo = { ...ITEMS["ammo_9mm"] } as AmmoItem;
|
||||
ammo.quantity = 3; // Only 3 bullets
|
||||
const ammo = createAmmo("ammo_9mm", 3); // Only 3 bullets
|
||||
player.inventory!.items.push(ammo);
|
||||
|
||||
// Logic mimic
|
||||
const needed = pistol.stats.magazineSize - pistol.stats.currentAmmo; // 6
|
||||
const toTake = Math.min(needed, ammo.quantity); // 3
|
||||
const needed = pistol.stats.magazineSize - pistol.currentAmmo; // 6
|
||||
const toTake = Math.min(needed, ammo.quantity!); // 3
|
||||
|
||||
pistol.stats.currentAmmo += toTake;
|
||||
ammo.quantity -= toTake;
|
||||
pistol.currentAmmo += toTake;
|
||||
ammo.quantity! -= toTake;
|
||||
|
||||
expect(pistol.stats.currentAmmo).toBe(3);
|
||||
expect(pistol.currentAmmo).toBe(3);
|
||||
expect(ammo.quantity).toBe(0);
|
||||
});
|
||||
|
||||
it("should deep clone stats on spawn so pistols remain independent", () => {
|
||||
const pistolDef = ITEMS["pistol"] as RangedWeaponItem;
|
||||
it("should deep clone on spawn so pistols remain independent", () => {
|
||||
const pistol1 = createRangedWeapon("pistol");
|
||||
|
||||
// Spawn 1
|
||||
itemManager.spawnItem(pistolDef, {x:0, y:0});
|
||||
itemManager.spawnItem(pistol1, {x:0, y:0});
|
||||
const picked1 = itemManager.tryPickup(player)! as RangedWeaponItem;
|
||||
|
||||
// Spawn 2
|
||||
itemManager.spawnItem(pistolDef, {x:0, y:0});
|
||||
const pistol2 = createRangedWeapon("pistol");
|
||||
itemManager.spawnItem(pistol2, {x:0, y:0});
|
||||
const picked2 = itemManager.tryPickup(player)! as RangedWeaponItem;
|
||||
|
||||
expect(picked1).not.toBe(picked2);
|
||||
expect(picked1.stats).not.toBe(picked2.stats); // Critical!
|
||||
|
||||
// Modifying one should not affect other
|
||||
picked1.stats.currentAmmo = 0;
|
||||
expect(picked2.stats.currentAmmo).toBe(6);
|
||||
picked1.currentAmmo = 0;
|
||||
expect(picked2.currentAmmo).toBe(6);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,7 +2,12 @@ import { type World, type EntityId, type RunState, type Tile, type Actor, type V
|
||||
import { TileType } from "../../core/terrain";
|
||||
import { idx } from "./world-logic";
|
||||
import { GAME_CONFIG } from "../../core/config/GameConfig";
|
||||
import { ITEMS } from "../../core/config/Items";
|
||||
import {
|
||||
createConsumable,
|
||||
createMeleeWeapon,
|
||||
createRangedWeapon,
|
||||
createArmour
|
||||
} from "../../core/config/Items";
|
||||
import { seededRandom } from "../../core/math";
|
||||
import * as ROT from "rot-js";
|
||||
|
||||
@@ -53,11 +58,11 @@ export function generateWorld(floor: number, runState: RunState): { world: World
|
||||
...runState.inventory.items,
|
||||
// Add starting items for testing if empty
|
||||
...(runState.inventory.items.length === 0 ? [
|
||||
{ ...ITEMS["health_potion"], quantity: 2 },
|
||||
ITEMS["iron_sword"],
|
||||
{ ...ITEMS["throwing_dagger"], quantity: 3 },
|
||||
ITEMS["pistol"],
|
||||
ITEMS["leather_armor"]
|
||||
createConsumable("health_potion", 2),
|
||||
createMeleeWeapon("iron_sword"),
|
||||
createConsumable("throwing_dagger", 3),
|
||||
createRangedWeapon("pistol"),
|
||||
createArmour("leather_armor")
|
||||
] : [])
|
||||
]
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user