Add ammo counter for ranged items in quickslot
This commit is contained in:
@@ -315,6 +315,10 @@ export class DungeonRenderer {
|
||||
this.fxRenderer.showAlert(x, y);
|
||||
}
|
||||
|
||||
showFloatingText(x: number, y: number, message: string, color: string) {
|
||||
this.fxRenderer.showFloatingText(x, y, message, color);
|
||||
}
|
||||
|
||||
showProjectile(from: Vec2, to: Vec2, itemId: string, onComplete: () => void) {
|
||||
// World coords
|
||||
const startX = from.x * TILE_SIZE + TILE_SIZE / 2;
|
||||
|
||||
@@ -174,6 +174,56 @@ export class GameScene extends Phaser.Scene {
|
||||
if (itemIdx === -1) return;
|
||||
const item = player.inventory.items[itemIdx];
|
||||
|
||||
// Ranged Weapon Logic
|
||||
if (item.type === "Weapon" && item.weaponType === "ranged") {
|
||||
// Check Ammo
|
||||
if (item.stats.currentAmmo <= 0) {
|
||||
// Try Reload
|
||||
const ammoId = `ammo_${item.stats.ammoType}`;
|
||||
const ammoItem = player.inventory.items.find(it => it.id === ammoId); // Simple check
|
||||
|
||||
if (ammoItem && ammoItem.quantity && ammoItem.quantity > 0) {
|
||||
const needed = item.stats.magazineSize - item.stats.currentAmmo;
|
||||
const toTake = Math.min(needed, ammoItem.quantity);
|
||||
|
||||
item.stats.currentAmmo += toTake;
|
||||
ammoItem.quantity -= toTake;
|
||||
|
||||
if (ammoItem.quantity <= 0) {
|
||||
player.inventory.items = player.inventory.items.filter(it => it !== ammoItem);
|
||||
}
|
||||
|
||||
this.dungeonRenderer.showFloatingText(player.pos.x, player.pos.y, "Reloaded!", "#00ff00");
|
||||
console.log("Reloaded. Ammo:", item.stats.currentAmmo);
|
||||
this.commitPlayerAction({ type: "wait" });
|
||||
this.emitUIUpdate();
|
||||
} else {
|
||||
this.dungeonRenderer.showFloatingText(player.pos.x, player.pos.y, "No Ammo!", "#ff0000");
|
||||
console.log("No ammo found for", item.name);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Has ammo, start targeting
|
||||
if (this.targetingSystem.isActive && this.targetingSystem.itemId === item.id) {
|
||||
// Already targeting - execute shoot
|
||||
if (this.targetingSystem.cursorPos) {
|
||||
this.executeThrow(this.targetingSystem.cursorPos.x, this.targetingSystem.cursorPos.y);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.targetingSystem.startTargeting(
|
||||
item.id,
|
||||
player.pos,
|
||||
this.world,
|
||||
this.dungeonRenderer.seenArray,
|
||||
this.world.width
|
||||
);
|
||||
this.emitUIUpdate();
|
||||
return;
|
||||
}
|
||||
|
||||
const result = this.itemManager.handleUse(data.itemId, player);
|
||||
|
||||
if (result.success && result.consumed) {
|
||||
@@ -565,10 +615,12 @@ export class GameScene extends Phaser.Scene {
|
||||
this.playerId,
|
||||
this.entityManager,
|
||||
(blockedPos, hitActorId, item) => {
|
||||
// Damage Logic
|
||||
if (hitActorId !== undefined) {
|
||||
const victim = this.world.actors.get(hitActorId) as CombatantActor;
|
||||
if (victim) {
|
||||
const dmg = item.stats?.attack ?? 1;
|
||||
const stats = 'stats' in item ? item.stats : undefined;
|
||||
const dmg = (stats && 'attack' in stats) ? (stats.attack ?? 1) : 1;
|
||||
victim.stats.hp -= dmg;
|
||||
this.dungeonRenderer.showDamage(victim.pos.x, victim.pos.y, dmg);
|
||||
this.dungeonRenderer.shakeCamera();
|
||||
@@ -576,13 +628,30 @@ export class GameScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
const player = this.world.actors.get(this.playerId) as CombatantActor;
|
||||
|
||||
// Projectile Visuals
|
||||
let projectileId = item.id;
|
||||
if (item.type === "Weapon" && item.weaponType === "ranged") {
|
||||
projectileId = `ammo_${item.stats.ammoType}`; // Show ammo sprite
|
||||
|
||||
// Consume Ammo
|
||||
if (item.stats.currentAmmo > 0) {
|
||||
item.stats.currentAmmo--;
|
||||
}
|
||||
}
|
||||
|
||||
this.dungeonRenderer.showProjectile(
|
||||
player.pos,
|
||||
blockedPos,
|
||||
item.id,
|
||||
projectileId,
|
||||
() => {
|
||||
// Drop the actual item at the landing spot
|
||||
this.itemManager.spawnItem(item, blockedPos);
|
||||
// Only drop item if it acts as a thrown item (Consumable/Misc), NOT Weapon
|
||||
const shouldDrop = item.type !== "Weapon";
|
||||
|
||||
if (shouldDrop) {
|
||||
// Drop the actual item at the landing spot
|
||||
this.itemManager.spawnItem(item, blockedPos);
|
||||
}
|
||||
|
||||
// Trigger destruction/interaction
|
||||
if (tryDestructTile(this.world, blockedPos.x, blockedPos.y)) {
|
||||
@@ -590,7 +659,7 @@ export class GameScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
this.targetingSystem.cancel();
|
||||
this.commitPlayerAction({ type: "throw" });
|
||||
this.commitPlayerAction({ type: "throw" }); // Or 'attack' if shooting? 'throw' is fine for now
|
||||
this.emitUIUpdate();
|
||||
}
|
||||
);
|
||||
|
||||
@@ -37,12 +37,18 @@ export class ItemManager {
|
||||
spawnItem(item: Item, pos: Vec2): void {
|
||||
if (!this.world || !this.entityManager) return;
|
||||
|
||||
// Deep clone item (crucial for items with mutable stats like ammo)
|
||||
const clonedItem = { ...item } as Item;
|
||||
if ('stats' in clonedItem && clonedItem.stats) {
|
||||
(clonedItem as any).stats = { ...clonedItem.stats };
|
||||
}
|
||||
|
||||
const id = this.entityManager.getNextId();
|
||||
const drop: ItemDropActor = {
|
||||
id,
|
||||
pos: { x: pos.x, y: pos.y },
|
||||
category: "item_drop",
|
||||
item: { ...item } // Clone item
|
||||
item: clonedItem
|
||||
};
|
||||
|
||||
this.entityManager.addActor(drop);
|
||||
@@ -61,7 +67,20 @@ export class ItemManager {
|
||||
if (itemActor) {
|
||||
const item = itemActor.item;
|
||||
|
||||
// Stacking Logic
|
||||
if (item.stackable) {
|
||||
const existingItem = player.inventory.items.find(it => it.id === item.id);
|
||||
if (existingItem) {
|
||||
existingItem.quantity = (existingItem.quantity || 1) + (item.quantity || 1);
|
||||
console.log(`Stacked ${item.name}. New quantity: ${existingItem.quantity}`);
|
||||
|
||||
this.entityManager.removeActor(itemActor.id);
|
||||
return existingItem;
|
||||
}
|
||||
}
|
||||
|
||||
// Add to inventory
|
||||
item.quantity = item.quantity || 1;
|
||||
player.inventory.items.push(item);
|
||||
|
||||
// Remove from world
|
||||
@@ -91,7 +110,7 @@ export class ItemManager {
|
||||
const item = player.inventory.items[itemIdx];
|
||||
|
||||
// Check if item is a healing consumable
|
||||
if (item.stats && item.stats.hp && item.stats.hp > 0) {
|
||||
if (item.type === "Consumable" && item.stats?.hp) {
|
||||
const healAmount = item.stats.hp;
|
||||
|
||||
if (player.stats.hp >= player.stats.maxHp) {
|
||||
@@ -100,8 +119,12 @@ export class ItemManager {
|
||||
|
||||
player.stats.hp = Math.min(player.stats.hp + healAmount, player.stats.maxHp);
|
||||
|
||||
// Remove item after use
|
||||
player.inventory.items.splice(itemIdx, 1);
|
||||
// Consume item (check stack)
|
||||
if (item.quantity && item.quantity > 1) {
|
||||
item.quantity--;
|
||||
} else {
|
||||
player.inventory.items.splice(itemIdx, 1);
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
@@ -110,8 +133,8 @@ export class ItemManager {
|
||||
};
|
||||
}
|
||||
|
||||
// Throwable items are handled by TargetingSystem, not here
|
||||
if (item.throwable) {
|
||||
// Throwable items
|
||||
if (item.type === "Consumable" && item.throwable) {
|
||||
return {
|
||||
success: true,
|
||||
consumed: false,
|
||||
|
||||
@@ -79,8 +79,16 @@ export class TargetingSystem {
|
||||
}
|
||||
|
||||
const item = player.inventory.items[itemIdx];
|
||||
// Remove item from inventory before throw
|
||||
player.inventory.items.splice(itemIdx, 1);
|
||||
|
||||
// Only remove if it's a consumable throwable
|
||||
if (item.type === "Consumable" && item.throwable) {
|
||||
// Handle stack decrement if applicable, or remove
|
||||
if (item.quantity && item.quantity > 1) {
|
||||
item.quantity--;
|
||||
} else {
|
||||
player.inventory.items.splice(itemIdx, 1);
|
||||
}
|
||||
}
|
||||
|
||||
const start = player.pos;
|
||||
const end = { x: this.cursor.x, y: this.cursor.y };
|
||||
|
||||
@@ -6,7 +6,7 @@ export class QuickSlotComponent {
|
||||
private container!: Phaser.GameObjects.Container;
|
||||
private slots: Phaser.GameObjects.Container[] = [];
|
||||
private itemMap: (Item | null)[] = new Array(10).fill(null);
|
||||
private assignedIds: string[] = ["health_potion", "throwing_dagger", ...new Array(8).fill("")];
|
||||
private assignedIds: string[] = ["health_potion", "pistol", "throwing_dagger", ...new Array(7).fill("")];
|
||||
|
||||
constructor(scene: Phaser.Scene) {
|
||||
this.scene = scene;
|
||||
@@ -125,6 +125,19 @@ export class QuickSlotComponent {
|
||||
}).setOrigin(1, 1);
|
||||
slot.add(countText);
|
||||
}
|
||||
|
||||
// Add Ammo Counter for Ranged Weapons (Top-Right)
|
||||
if (foundItem.type === "Weapon" && foundItem.weaponType === "ranged" && foundItem.stats) {
|
||||
const ammoText = `${foundItem.stats.currentAmmo}/${foundItem.stats.magazineSize}`;
|
||||
const ammoDisplay = this.scene.add.text(slotSize - 2, 2, ammoText, {
|
||||
fontSize: "10px",
|
||||
color: "#00FF00", // Green text
|
||||
fontStyle: "bold",
|
||||
stroke: "#000000",
|
||||
strokeThickness: 2
|
||||
}).setOrigin(1, 0); // Top-right anchor
|
||||
slot.add(ammoDisplay);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.itemMap[i] = null;
|
||||
|
||||
Reference in New Issue
Block a user