refactor items logic
This commit is contained in:
@@ -1,77 +1,180 @@
|
|||||||
import type { Item } from "../types";
|
import type {
|
||||||
|
ConsumableItem,
|
||||||
|
MeleeWeaponItem,
|
||||||
|
RangedWeaponItem,
|
||||||
|
ArmourItem,
|
||||||
|
AmmoItem
|
||||||
|
} from "../types";
|
||||||
|
|
||||||
export const ITEMS: Record<string, Item> = {
|
// =============================================================================
|
||||||
"health_potion": {
|
// Per-Type Template Lists (Immutable)
|
||||||
id: "health_potion",
|
// =============================================================================
|
||||||
|
|
||||||
|
export const CONSUMABLES = {
|
||||||
|
health_potion: {
|
||||||
name: "Health Potion",
|
name: "Health Potion",
|
||||||
type: "Consumable",
|
|
||||||
textureKey: "items",
|
textureKey: "items",
|
||||||
spriteIndex: 57,
|
spriteIndex: 57,
|
||||||
stats: {
|
healAmount: 5,
|
||||||
hp: 5
|
|
||||||
},
|
|
||||||
stackable: true,
|
stackable: true,
|
||||||
quantity: 1
|
|
||||||
},
|
},
|
||||||
"iron_sword": {
|
throwing_dagger: {
|
||||||
id: "iron_sword",
|
|
||||||
name: "Iron Sword",
|
|
||||||
type: "Weapon",
|
|
||||||
weaponType: "melee",
|
|
||||||
textureKey: "items",
|
|
||||||
spriteIndex: 2,
|
|
||||||
stats: {
|
|
||||||
attack: 2
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"leather_armor": {
|
|
||||||
id: "leather_armor",
|
|
||||||
name: "Leather Armor",
|
|
||||||
type: "BodyArmour",
|
|
||||||
textureKey: "items",
|
|
||||||
spriteIndex: 25,
|
|
||||||
stats: {
|
|
||||||
defense: 2
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"throwing_dagger": {
|
|
||||||
id: "throwing_dagger",
|
|
||||||
name: "Throwing Dagger",
|
name: "Throwing Dagger",
|
||||||
type: "Consumable",
|
|
||||||
textureKey: "items",
|
textureKey: "items",
|
||||||
spriteIndex: 15,
|
spriteIndex: 15,
|
||||||
stats: {
|
attack: 4,
|
||||||
attack: 4
|
|
||||||
},
|
|
||||||
throwable: true,
|
throwable: true,
|
||||||
stackable: true,
|
stackable: true,
|
||||||
quantity: 1
|
|
||||||
},
|
},
|
||||||
"pistol": {
|
} as const;
|
||||||
id: "pistol",
|
|
||||||
|
export const RANGED_WEAPONS = {
|
||||||
|
pistol: {
|
||||||
name: "Pistol",
|
name: "Pistol",
|
||||||
type: "Weapon",
|
|
||||||
weaponType: "ranged",
|
|
||||||
textureKey: "weapons",
|
textureKey: "weapons",
|
||||||
spriteIndex: 1,
|
spriteIndex: 1,
|
||||||
stats: {
|
|
||||||
attack: 10,
|
attack: 10,
|
||||||
range: 8,
|
range: 8,
|
||||||
magazineSize: 6,
|
magazineSize: 6,
|
||||||
currentAmmo: 6,
|
|
||||||
ammoType: "9mm",
|
ammoType: "9mm",
|
||||||
projectileSpeed: 15,
|
projectileSpeed: 15,
|
||||||
fireSound: "shoot"
|
fireSound: "shoot",
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"ammo_9mm": {
|
} as const;
|
||||||
id: "ammo_9mm",
|
|
||||||
|
export const MELEE_WEAPONS = {
|
||||||
|
iron_sword: {
|
||||||
|
name: "Iron Sword",
|
||||||
|
textureKey: "items",
|
||||||
|
spriteIndex: 2,
|
||||||
|
attack: 2,
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const AMMO = {
|
||||||
|
ammo_9mm: {
|
||||||
name: "9mm Ammo",
|
name: "9mm Ammo",
|
||||||
type: "Ammo",
|
|
||||||
ammoType: "9mm",
|
|
||||||
textureKey: "weapons",
|
textureKey: "weapons",
|
||||||
spriteIndex: 23,
|
spriteIndex: 23,
|
||||||
|
ammoType: "9mm",
|
||||||
stackable: true,
|
stackable: true,
|
||||||
quantity: 10 // Finds a pack of 10
|
},
|
||||||
}
|
} as const;
|
||||||
};
|
|
||||||
|
export const ARMOUR = {
|
||||||
|
leather_armor: {
|
||||||
|
name: "Leather Armor",
|
||||||
|
textureKey: "items",
|
||||||
|
spriteIndex: 25,
|
||||||
|
defense: 2,
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// Combined lookup for rendering (e.g., projectile sprites)
|
||||||
|
export const ALL_TEMPLATES = {
|
||||||
|
...CONSUMABLES,
|
||||||
|
...RANGED_WEAPONS,
|
||||||
|
...MELEE_WEAPONS,
|
||||||
|
...AMMO,
|
||||||
|
...ARMOUR,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Type-Safe IDs (derived from templates)
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
export type ConsumableId = keyof typeof CONSUMABLES;
|
||||||
|
export type RangedWeaponId = keyof typeof RANGED_WEAPONS;
|
||||||
|
export type MeleeWeaponId = keyof typeof MELEE_WEAPONS;
|
||||||
|
export type AmmoId = keyof typeof AMMO;
|
||||||
|
export type ArmourId = keyof typeof ARMOUR;
|
||||||
|
export type ItemTemplateId = keyof typeof ALL_TEMPLATES;
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Factory Functions
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
export function createConsumable(id: ConsumableId, quantity = 1): ConsumableItem {
|
||||||
|
const t = CONSUMABLES[id];
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name: t.name,
|
||||||
|
type: "Consumable",
|
||||||
|
textureKey: t.textureKey,
|
||||||
|
spriteIndex: t.spriteIndex,
|
||||||
|
stackable: t.stackable ?? false,
|
||||||
|
quantity,
|
||||||
|
stats: {
|
||||||
|
hp: "healAmount" in t ? t.healAmount : undefined,
|
||||||
|
attack: "attack" in t ? t.attack : undefined,
|
||||||
|
},
|
||||||
|
throwable: "throwable" in t ? t.throwable : undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createRangedWeapon(id: RangedWeaponId): RangedWeaponItem {
|
||||||
|
const t = RANGED_WEAPONS[id];
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name: t.name,
|
||||||
|
type: "Weapon",
|
||||||
|
weaponType: "ranged",
|
||||||
|
textureKey: t.textureKey,
|
||||||
|
spriteIndex: t.spriteIndex,
|
||||||
|
currentAmmo: t.magazineSize,
|
||||||
|
stats: {
|
||||||
|
attack: t.attack,
|
||||||
|
range: t.range,
|
||||||
|
magazineSize: t.magazineSize,
|
||||||
|
ammoType: t.ammoType,
|
||||||
|
projectileSpeed: t.projectileSpeed,
|
||||||
|
fireSound: t.fireSound,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createMeleeWeapon(id: MeleeWeaponId): MeleeWeaponItem {
|
||||||
|
const t = MELEE_WEAPONS[id];
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name: t.name,
|
||||||
|
type: "Weapon",
|
||||||
|
weaponType: "melee",
|
||||||
|
textureKey: t.textureKey,
|
||||||
|
spriteIndex: t.spriteIndex,
|
||||||
|
stats: {
|
||||||
|
attack: t.attack,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createAmmo(id: AmmoId, quantity = 10): AmmoItem {
|
||||||
|
const t = AMMO[id];
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name: t.name,
|
||||||
|
type: "Ammo",
|
||||||
|
textureKey: t.textureKey,
|
||||||
|
spriteIndex: t.spriteIndex,
|
||||||
|
ammoType: t.ammoType,
|
||||||
|
stackable: true,
|
||||||
|
quantity,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createArmour(id: ArmourId): ArmourItem {
|
||||||
|
const t = ARMOUR[id];
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name: t.name,
|
||||||
|
type: "BodyArmour",
|
||||||
|
textureKey: t.textureKey,
|
||||||
|
spriteIndex: t.spriteIndex,
|
||||||
|
stats: {
|
||||||
|
defense: t.defense,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy export for backward compatibility during migration
|
||||||
|
export const ITEMS = ALL_TEMPLATES;
|
||||||
|
|||||||
@@ -98,11 +98,11 @@ export interface MeleeWeaponItem extends BaseItem {
|
|||||||
export interface RangedWeaponItem extends BaseItem {
|
export interface RangedWeaponItem extends BaseItem {
|
||||||
type: "Weapon";
|
type: "Weapon";
|
||||||
weaponType: "ranged";
|
weaponType: "ranged";
|
||||||
|
currentAmmo: number; // Runtime state - moved to top level for easier access
|
||||||
stats: {
|
stats: {
|
||||||
attack: number;
|
attack: number;
|
||||||
range: number;
|
range: number;
|
||||||
magazineSize: number;
|
magazineSize: number;
|
||||||
currentAmmo: number;
|
|
||||||
ammoType: string;
|
ammoType: string;
|
||||||
projectileSpeed: number;
|
projectileSpeed: number;
|
||||||
fireSound?: string;
|
fireSound?: string;
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
import { describe, it, expect, beforeEach } from "vitest";
|
import { describe, it, expect, beforeEach } from "vitest";
|
||||||
import { ItemManager } from "../../../scenes/systems/ItemManager";
|
import { ItemManager } from "../../../scenes/systems/ItemManager";
|
||||||
import { EntityManager } from "../../EntityManager";
|
import { EntityManager } from "../../EntityManager";
|
||||||
import type { World, CombatantActor, RangedWeaponItem, AmmoItem } from "../../../core/types";
|
import type { World, CombatantActor, RangedWeaponItem } from "../../../core/types";
|
||||||
import { ITEMS } from "../../../core/config/Items";
|
import { createRangedWeapon, createAmmo } from "../../../core/config/Items";
|
||||||
|
|
||||||
// Mock World and EntityManager
|
// Mock World and EntityManager
|
||||||
const mockWorld: World = {
|
const mockWorld: World = {
|
||||||
@@ -50,8 +50,7 @@ describe("Fireable Weapons & Ammo System", () => {
|
|||||||
|
|
||||||
it("should stack ammo correctly", () => {
|
it("should stack ammo correctly", () => {
|
||||||
// Spawn Ammo pack 1
|
// Spawn Ammo pack 1
|
||||||
const ammo1 = { ...ITEMS["ammo_9mm"] } as AmmoItem;
|
const ammo1 = createAmmo("ammo_9mm", 10);
|
||||||
ammo1.quantity = 10;
|
|
||||||
itemManager.spawnItem(ammo1, { x: 0, y: 0 });
|
itemManager.spawnItem(ammo1, { x: 0, y: 0 });
|
||||||
|
|
||||||
// Pickup
|
// Pickup
|
||||||
@@ -60,8 +59,7 @@ describe("Fireable Weapons & Ammo System", () => {
|
|||||||
expect(player.inventory!.items[0].quantity).toBe(10);
|
expect(player.inventory!.items[0].quantity).toBe(10);
|
||||||
|
|
||||||
// Spawn Ammo pack 2
|
// Spawn Ammo pack 2
|
||||||
const ammo2 = { ...ITEMS["ammo_9mm"] } as AmmoItem;
|
const ammo2 = createAmmo("ammo_9mm", 5);
|
||||||
ammo2.quantity = 5;
|
|
||||||
itemManager.spawnItem(ammo2, { x: 0, y: 0 });
|
itemManager.spawnItem(ammo2, { x: 0, y: 0 });
|
||||||
|
|
||||||
// Pickup (should merge)
|
// Pickup (should merge)
|
||||||
@@ -71,82 +69,77 @@ describe("Fireable Weapons & Ammo System", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should consume ammo from weapon when fired", () => {
|
it("should consume ammo from weapon when fired", () => {
|
||||||
// Manually Equip Pistol
|
// Create pistol using factory (already has currentAmmo initialized)
|
||||||
const pistol = { ...ITEMS["pistol"] } as RangedWeaponItem;
|
const pistol = createRangedWeapon("pistol");
|
||||||
// Deep clone stats for test isolation
|
|
||||||
pistol.stats = { ...pistol.stats };
|
|
||||||
player.inventory!.items.push(pistol);
|
player.inventory!.items.push(pistol);
|
||||||
|
|
||||||
// Sanity Check
|
// Sanity Check - currentAmmo is now top-level
|
||||||
expect(pistol.stats.currentAmmo).toBe(6);
|
expect(pistol.currentAmmo).toBe(6);
|
||||||
expect(pistol.stats.magazineSize).toBe(6);
|
expect(pistol.stats.magazineSize).toBe(6);
|
||||||
|
|
||||||
// Simulate Firing (logic mimic from GameScene)
|
// Simulate Firing (logic mimic from GameScene)
|
||||||
if (pistol.stats.currentAmmo > 0) {
|
if (pistol.currentAmmo > 0) {
|
||||||
pistol.stats.currentAmmo--;
|
pistol.currentAmmo--;
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(pistol.stats.currentAmmo).toBe(5);
|
expect(pistol.currentAmmo).toBe(5);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should reload weapon using inventory ammo", () => {
|
it("should reload weapon using inventory ammo", () => {
|
||||||
const pistol = { ...ITEMS["pistol"] } as RangedWeaponItem;
|
const pistol = createRangedWeapon("pistol");
|
||||||
pistol.stats = { ...pistol.stats };
|
pistol.currentAmmo = 0; // Empty
|
||||||
pistol.stats.currentAmmo = 0; // Empty
|
|
||||||
player.inventory!.items.push(pistol);
|
player.inventory!.items.push(pistol);
|
||||||
|
|
||||||
const ammo = { ...ITEMS["ammo_9mm"] } as AmmoItem;
|
const ammo = createAmmo("ammo_9mm", 10);
|
||||||
ammo.quantity = 10;
|
|
||||||
player.inventory!.items.push(ammo);
|
player.inventory!.items.push(ammo);
|
||||||
|
|
||||||
// Logic mimic from GameScene
|
// Logic mimic from GameScene
|
||||||
const needed = pistol.stats.magazineSize - pistol.stats.currentAmmo; // 6
|
const needed = pistol.stats.magazineSize - pistol.currentAmmo; // 6
|
||||||
const toTake = Math.min(needed, ammo.quantity); // 6
|
const toTake = Math.min(needed, ammo.quantity!); // 6
|
||||||
|
|
||||||
pistol.stats.currentAmmo += toTake;
|
pistol.currentAmmo += toTake;
|
||||||
ammo.quantity -= toTake;
|
ammo.quantity! -= toTake;
|
||||||
|
|
||||||
expect(pistol.stats.currentAmmo).toBe(6);
|
expect(pistol.currentAmmo).toBe(6);
|
||||||
expect(ammo.quantity).toBe(4);
|
expect(ammo.quantity).toBe(4);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should handle partial reload if not enough ammo", () => {
|
it("should handle partial reload if not enough ammo", () => {
|
||||||
const pistol = { ...ITEMS["pistol"] } as RangedWeaponItem;
|
const pistol = createRangedWeapon("pistol");
|
||||||
pistol.stats = { ...pistol.stats };
|
pistol.currentAmmo = 0;
|
||||||
pistol.stats.currentAmmo = 0;
|
|
||||||
player.inventory!.items.push(pistol);
|
player.inventory!.items.push(pistol);
|
||||||
|
|
||||||
const ammo = { ...ITEMS["ammo_9mm"] } as AmmoItem;
|
const ammo = createAmmo("ammo_9mm", 3); // Only 3 bullets
|
||||||
ammo.quantity = 3; // Only 3 bullets
|
|
||||||
player.inventory!.items.push(ammo);
|
player.inventory!.items.push(ammo);
|
||||||
|
|
||||||
// Logic mimic
|
// Logic mimic
|
||||||
const needed = pistol.stats.magazineSize - pistol.stats.currentAmmo; // 6
|
const needed = pistol.stats.magazineSize - pistol.currentAmmo; // 6
|
||||||
const toTake = Math.min(needed, ammo.quantity); // 3
|
const toTake = Math.min(needed, ammo.quantity!); // 3
|
||||||
|
|
||||||
pistol.stats.currentAmmo += toTake;
|
pistol.currentAmmo += toTake;
|
||||||
ammo.quantity -= toTake;
|
ammo.quantity! -= toTake;
|
||||||
|
|
||||||
expect(pistol.stats.currentAmmo).toBe(3);
|
expect(pistol.currentAmmo).toBe(3);
|
||||||
expect(ammo.quantity).toBe(0);
|
expect(ammo.quantity).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should deep clone stats on spawn so pistols remain independent", () => {
|
it("should deep clone on spawn so pistols remain independent", () => {
|
||||||
const pistolDef = ITEMS["pistol"] as RangedWeaponItem;
|
const pistol1 = createRangedWeapon("pistol");
|
||||||
|
|
||||||
// Spawn 1
|
// Spawn 1
|
||||||
itemManager.spawnItem(pistolDef, {x:0, y:0});
|
itemManager.spawnItem(pistol1, {x:0, y:0});
|
||||||
const picked1 = itemManager.tryPickup(player)! as RangedWeaponItem;
|
const picked1 = itemManager.tryPickup(player)! as RangedWeaponItem;
|
||||||
|
|
||||||
// Spawn 2
|
// 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;
|
const picked2 = itemManager.tryPickup(player)! as RangedWeaponItem;
|
||||||
|
|
||||||
expect(picked1).not.toBe(picked2);
|
expect(picked1).not.toBe(picked2);
|
||||||
expect(picked1.stats).not.toBe(picked2.stats); // Critical!
|
expect(picked1.stats).not.toBe(picked2.stats); // Critical!
|
||||||
|
|
||||||
// Modifying one should not affect other
|
// Modifying one should not affect other
|
||||||
picked1.stats.currentAmmo = 0;
|
picked1.currentAmmo = 0;
|
||||||
expect(picked2.stats.currentAmmo).toBe(6);
|
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 { TileType } from "../../core/terrain";
|
||||||
import { idx } from "./world-logic";
|
import { idx } from "./world-logic";
|
||||||
import { GAME_CONFIG } from "../../core/config/GameConfig";
|
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 { seededRandom } from "../../core/math";
|
||||||
import * as ROT from "rot-js";
|
import * as ROT from "rot-js";
|
||||||
|
|
||||||
@@ -53,11 +58,11 @@ export function generateWorld(floor: number, runState: RunState): { world: World
|
|||||||
...runState.inventory.items,
|
...runState.inventory.items,
|
||||||
// Add starting items for testing if empty
|
// Add starting items for testing if empty
|
||||||
...(runState.inventory.items.length === 0 ? [
|
...(runState.inventory.items.length === 0 ? [
|
||||||
{ ...ITEMS["health_potion"], quantity: 2 },
|
createConsumable("health_potion", 2),
|
||||||
ITEMS["iron_sword"],
|
createMeleeWeapon("iron_sword"),
|
||||||
{ ...ITEMS["throwing_dagger"], quantity: 3 },
|
createConsumable("throwing_dagger", 3),
|
||||||
ITEMS["pistol"],
|
createRangedWeapon("pistol"),
|
||||||
ITEMS["leather_armor"]
|
createArmour("leather_armor")
|
||||||
] : [])
|
] : [])
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { type World, type EntityId, type Vec2, type ActorType } from "../core/ty
|
|||||||
import { TILE_SIZE } from "../core/constants";
|
import { TILE_SIZE } from "../core/constants";
|
||||||
import { idx, isWall } from "../engine/world/world-logic";
|
import { idx, isWall } from "../engine/world/world-logic";
|
||||||
import { GAME_CONFIG } from "../core/config/GameConfig";
|
import { GAME_CONFIG } from "../core/config/GameConfig";
|
||||||
import { ITEMS } from "../core/config/Items";
|
import { ALL_TEMPLATES } from "../core/config/Items";
|
||||||
import { FovManager } from "./FovManager";
|
import { FovManager } from "./FovManager";
|
||||||
import { MinimapRenderer } from "./MinimapRenderer";
|
import { MinimapRenderer } from "./MinimapRenderer";
|
||||||
import { FxRenderer } from "./FxRenderer";
|
import { FxRenderer } from "./FxRenderer";
|
||||||
@@ -328,7 +328,7 @@ export class DungeonRenderer {
|
|||||||
|
|
||||||
// Create sprite
|
// Create sprite
|
||||||
// Look up sprite index from config
|
// Look up sprite index from config
|
||||||
const itemConfig = ITEMS[itemId];
|
const itemConfig = ALL_TEMPLATES[itemId as keyof typeof ALL_TEMPLATES];
|
||||||
const texture = itemConfig?.textureKey ?? "items";
|
const texture = itemConfig?.textureKey ?? "items";
|
||||||
const frame = itemConfig?.spriteIndex ?? 0;
|
const frame = itemConfig?.spriteIndex ?? 0;
|
||||||
|
|
||||||
|
|||||||
@@ -176,16 +176,16 @@ export class GameScene extends Phaser.Scene {
|
|||||||
// Ranged Weapon Logic
|
// Ranged Weapon Logic
|
||||||
if (item.type === "Weapon" && item.weaponType === "ranged") {
|
if (item.type === "Weapon" && item.weaponType === "ranged") {
|
||||||
// Check Ammo
|
// Check Ammo
|
||||||
if (item.stats.currentAmmo <= 0) {
|
if (item.currentAmmo <= 0) {
|
||||||
// Try Reload
|
// Try Reload
|
||||||
const ammoId = `ammo_${item.stats.ammoType}`;
|
const ammoId = `ammo_${item.stats.ammoType}`;
|
||||||
const ammoItem = player.inventory.items.find(it => it.id === ammoId); // Simple check
|
const ammoItem = player.inventory.items.find(it => it.id === ammoId); // Simple check
|
||||||
|
|
||||||
if (ammoItem && ammoItem.quantity && ammoItem.quantity > 0) {
|
if (ammoItem && ammoItem.quantity && ammoItem.quantity > 0) {
|
||||||
const needed = item.stats.magazineSize - item.stats.currentAmmo;
|
const needed = item.stats.magazineSize - item.currentAmmo;
|
||||||
const toTake = Math.min(needed, ammoItem.quantity);
|
const toTake = Math.min(needed, ammoItem.quantity);
|
||||||
|
|
||||||
item.stats.currentAmmo += toTake;
|
item.currentAmmo += toTake;
|
||||||
ammoItem.quantity -= toTake;
|
ammoItem.quantity -= toTake;
|
||||||
|
|
||||||
if (ammoItem.quantity <= 0) {
|
if (ammoItem.quantity <= 0) {
|
||||||
@@ -193,7 +193,7 @@ export class GameScene extends Phaser.Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.dungeonRenderer.showFloatingText(player.pos.x, player.pos.y, "Reloaded!", "#00ff00");
|
this.dungeonRenderer.showFloatingText(player.pos.x, player.pos.y, "Reloaded!", "#00ff00");
|
||||||
console.log("Reloaded. Ammo:", item.stats.currentAmmo);
|
console.log("Reloaded. Ammo:", item.currentAmmo);
|
||||||
this.commitPlayerAction({ type: "wait" });
|
this.commitPlayerAction({ type: "wait" });
|
||||||
this.emitUIUpdate();
|
this.emitUIUpdate();
|
||||||
} else {
|
} else {
|
||||||
@@ -721,8 +721,8 @@ export class GameScene extends Phaser.Scene {
|
|||||||
projectileId = `ammo_${item.stats.ammoType}`; // Show ammo sprite
|
projectileId = `ammo_${item.stats.ammoType}`; // Show ammo sprite
|
||||||
|
|
||||||
// Consume Ammo
|
// Consume Ammo
|
||||||
if (item.stats.currentAmmo > 0) {
|
if (item.currentAmmo > 0) {
|
||||||
item.stats.currentAmmo--;
|
item.currentAmmo--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -735,8 +735,9 @@ export class GameScene extends Phaser.Scene {
|
|||||||
const shouldDrop = item.type !== "Weapon";
|
const shouldDrop = item.type !== "Weapon";
|
||||||
|
|
||||||
if (shouldDrop) {
|
if (shouldDrop) {
|
||||||
// Drop the actual item at the landing spot
|
// Drop a SINGLE item at the landing spot (not the whole stack)
|
||||||
this.itemManager.spawnItem(item, blockedPos);
|
const singleItem = { ...item, quantity: 1 };
|
||||||
|
this.itemManager.spawnItem(singleItem, blockedPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trigger destruction/interaction
|
// Trigger destruction/interaction
|
||||||
|
|||||||
@@ -343,7 +343,7 @@ export class InventoryOverlay extends OverlayComponent {
|
|||||||
if (item.stackable) {
|
if (item.stackable) {
|
||||||
labelText = `x${item.quantity || 1}`;
|
labelText = `x${item.quantity || 1}`;
|
||||||
} else if (item.type === "Weapon" && item.weaponType === "ranged" && item.stats) {
|
} else if (item.type === "Weapon" && item.weaponType === "ranged" && item.stats) {
|
||||||
labelText = `${item.stats.currentAmmo}/${item.stats.magazineSize}`;
|
labelText = `${item.currentAmmo}/${item.stats.magazineSize}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (labelText) {
|
if (labelText) {
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ export class QuickSlotComponent {
|
|||||||
labelText = `x${totalQuantity}`;
|
labelText = `x${totalQuantity}`;
|
||||||
} else if (foundItem.type === "Weapon" && foundItem.weaponType === "ranged" && foundItem.stats) {
|
} else if (foundItem.type === "Weapon" && foundItem.weaponType === "ranged" && foundItem.stats) {
|
||||||
// Show ammo for non-stackable ranged weapons
|
// Show ammo for non-stackable ranged weapons
|
||||||
labelText = `${foundItem.stats.currentAmmo}/${foundItem.stats.magazineSize}`;
|
labelText = `${foundItem.currentAmmo}/${foundItem.stats.magazineSize}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (labelText) {
|
if (labelText) {
|
||||||
|
|||||||
Reference in New Issue
Block a user