Added pregress bar for reloading
This commit is contained in:
@@ -1,224 +1,233 @@
|
|||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import type { CombatantActor, Item } from "../../core/types";
|
import type { CombatantActor, Item } from "../../core/types";
|
||||||
import { ItemSpriteFactory } from "../../rendering/ItemSpriteFactory";
|
import { ItemSpriteFactory } from "../../rendering/ItemSpriteFactory";
|
||||||
|
import { GAME_CONFIG } from "../../core/config/GameConfig";
|
||||||
|
|
||||||
export class QuickSlotComponent {
|
export class QuickSlotComponent {
|
||||||
private scene: Phaser.Scene;
|
private scene: Phaser.Scene;
|
||||||
private container!: Phaser.GameObjects.Container;
|
private container!: Phaser.GameObjects.Container;
|
||||||
private slots: Phaser.GameObjects.Container[] = [];
|
private slots: Phaser.GameObjects.Container[] = [];
|
||||||
private itemMap: (Item | null)[] = new Array(10).fill(null);
|
private itemMap: (Item | null)[] = new Array(10).fill(null);
|
||||||
private assignedIds: string[] = ["health_potion", "pistol", "throwing_dagger", ...new Array(7).fill("")];
|
private assignedIds: string[] = ["health_potion", "pistol", "throwing_dagger", ...new Array(7).fill("")];
|
||||||
private draggedSlotIndex: number | null = null;
|
private draggedSlotIndex: number | null = null;
|
||||||
private dragIcon: Phaser.GameObjects.Sprite | null = null;
|
private dragIcon: Phaser.GameObjects.Sprite | null = null;
|
||||||
|
private reloadSliderContainer!: Phaser.GameObjects.Container;
|
||||||
|
|
||||||
constructor(scene: Phaser.Scene) {
|
constructor(scene: Phaser.Scene) {
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
}
|
|
||||||
|
|
||||||
create() {
|
|
||||||
const { width, height } = this.scene.scale;
|
|
||||||
const slotSize = 48;
|
|
||||||
const slotSpacing = 4;
|
|
||||||
const totalWidth = (slotSize + slotSpacing) * 10 - slotSpacing;
|
|
||||||
const actionButtonHeight = 40 + 10; // Button height + spacing
|
|
||||||
|
|
||||||
// Position above action buttons
|
|
||||||
this.container = this.scene.add.container(
|
|
||||||
width / 2 - totalWidth / 2,
|
|
||||||
height - slotSize - actionButtonHeight - 20
|
|
||||||
);
|
|
||||||
this.container.setScrollFactor(0).setDepth(1500);
|
|
||||||
|
|
||||||
for (let i = 0; i < 10; i++) {
|
|
||||||
const x = i * (slotSize + slotSpacing);
|
|
||||||
const g = this.scene.add.graphics();
|
|
||||||
|
|
||||||
// Draw slot background (dark purple/brown)
|
|
||||||
g.fillStyle(0x2a1f3d, 0.95);
|
|
||||||
g.fillRect(0, 0, slotSize, slotSize);
|
|
||||||
|
|
||||||
// Draw gold border (default state)
|
|
||||||
g.lineStyle(2, 0xD4AF37, 1);
|
|
||||||
g.strokeRect(0, 0, slotSize, slotSize);
|
|
||||||
|
|
||||||
// Hotkey label (bottom-left, gold color)
|
|
||||||
const label = i === 9 ? "0" : `${i + 1}`;
|
|
||||||
const key = this.scene.add.text(3, slotSize - 3, label, {
|
|
||||||
fontSize: "12px",
|
|
||||||
color: "#D4AF37",
|
|
||||||
fontStyle: "bold"
|
|
||||||
}).setOrigin(0, 1);
|
|
||||||
|
|
||||||
const slotContainer = this.scene.add.container(x, 0, [g, key]);
|
|
||||||
slotContainer.setData("index", i);
|
|
||||||
this.slots.push(slotContainer);
|
|
||||||
this.container.add(slotContainer);
|
|
||||||
|
|
||||||
// Input
|
|
||||||
const hitArea = new Phaser.Geom.Rectangle(0, 0, slotSize, slotSize);
|
|
||||||
slotContainer.setInteractive(hitArea, Phaser.Geom.Rectangle.Contains);
|
|
||||||
this.scene.input.setDraggable(slotContainer);
|
|
||||||
|
|
||||||
slotContainer.on("pointerdown", () => {
|
|
||||||
});
|
|
||||||
|
|
||||||
slotContainer.on("pointerup", (pointer: Phaser.Input.Pointer) => {
|
|
||||||
// If we didn't drag, then activate
|
|
||||||
if (this.draggedSlotIndex === null && pointer.getDistance() < 10) {
|
|
||||||
this.activateSlot(i);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drag and Drop Events
|
create() {
|
||||||
this.scene.input.on("dragstart", (pointer: Phaser.Input.Pointer, gameObject: Phaser.GameObjects.Container) => {
|
const { width, height } = this.scene.scale;
|
||||||
// Only handle if it's one of our slots
|
|
||||||
const index = gameObject.getData("index");
|
|
||||||
if (index === undefined || !this.slots.includes(gameObject)) return;
|
|
||||||
|
|
||||||
const item = this.itemMap[index];
|
|
||||||
if (!item) return;
|
|
||||||
|
|
||||||
this.draggedSlotIndex = index;
|
|
||||||
|
|
||||||
// Setup drag icon
|
|
||||||
if (!this.dragIcon) {
|
|
||||||
this.dragIcon = this.scene.add.sprite(0, 0, item.textureKey ?? "items", item.spriteIndex);
|
|
||||||
this.dragIcon.setDepth(2000).setScale(2.5).setAlpha(0.7);
|
|
||||||
} else {
|
|
||||||
this.dragIcon.setTexture(item.textureKey ?? "items", item.spriteIndex);
|
|
||||||
this.dragIcon.setVisible(true);
|
|
||||||
}
|
|
||||||
this.dragIcon.setPosition(pointer.x, pointer.y);
|
|
||||||
|
|
||||||
// Ghost the original slot's item
|
|
||||||
const sprite = gameObject.list.find(child => child instanceof Phaser.GameObjects.Sprite) as Phaser.GameObjects.Sprite;
|
|
||||||
if (sprite) sprite.setAlpha(0.3);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.scene.input.on("drag", (pointer: Phaser.Input.Pointer) => {
|
|
||||||
if (this.dragIcon) {
|
|
||||||
this.dragIcon.setPosition(pointer.x, pointer.y);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.scene.input.on("dragend", (pointer: Phaser.Input.Pointer, gameObject: Phaser.GameObjects.Container) => {
|
|
||||||
if (this.draggedSlotIndex === null || !this.slots.includes(gameObject)) return;
|
|
||||||
|
|
||||||
const startIndex = this.draggedSlotIndex;
|
|
||||||
this.draggedSlotIndex = null;
|
|
||||||
if (this.dragIcon) this.dragIcon.setVisible(false);
|
|
||||||
|
|
||||||
// Reset alpha of original sprite
|
|
||||||
const sprite = gameObject.list.find(child => child instanceof Phaser.GameObjects.Sprite) as Phaser.GameObjects.Sprite;
|
|
||||||
if (sprite) sprite.setAlpha(1.0);
|
|
||||||
|
|
||||||
// Determine if we dropped on another slot
|
|
||||||
let targetIndex: number | null = null;
|
|
||||||
const slotSize = 48;
|
const slotSize = 48;
|
||||||
const slotSpacing = 4;
|
const slotSpacing = 4;
|
||||||
|
const totalWidth = (slotSize + slotSpacing) * 10 - slotSpacing;
|
||||||
// Calculate pointer position relative to the quick-slot container
|
const actionButtonHeight = 40 + 10; // Button height + spacing
|
||||||
// Since container has scrollFactor(0), its screen position is fixed
|
|
||||||
const localX = pointer.x - this.container.x;
|
|
||||||
const localY = pointer.y - this.container.y;
|
|
||||||
|
|
||||||
// Check if pointer is within the vertical bounds of the slots
|
// Position above action buttons
|
||||||
if (localY >= 0 && localY <= slotSize) {
|
this.container = this.scene.add.container(
|
||||||
// Calculate which slot index the pointer is over
|
width / 2 - totalWidth / 2,
|
||||||
const index = Math.floor(localX / (slotSize + slotSpacing));
|
height - slotSize - actionButtonHeight - 20
|
||||||
const remainder = localX % (slotSize + slotSpacing);
|
);
|
||||||
|
this.container.setScrollFactor(0).setDepth(1500);
|
||||||
// Ensure index is valid and pointer is within the slot's actual area (not spacing)
|
|
||||||
if (index >= 0 && index < 10 && remainder <= slotSize) {
|
for (let i = 0; i < 10; i++) {
|
||||||
targetIndex = index;
|
const x = i * (slotSize + slotSpacing);
|
||||||
}
|
const g = this.scene.add.graphics();
|
||||||
|
|
||||||
|
// Draw slot background (dark purple/brown)
|
||||||
|
g.fillStyle(0x2a1f3d, 0.95);
|
||||||
|
g.fillRect(0, 0, slotSize, slotSize);
|
||||||
|
|
||||||
|
// Draw gold border (default state)
|
||||||
|
g.lineStyle(2, 0xD4AF37, 1);
|
||||||
|
g.strokeRect(0, 0, slotSize, slotSize);
|
||||||
|
|
||||||
|
// Hotkey label (bottom-left, gold color)
|
||||||
|
const label = i === 9 ? "0" : `${i + 1}`;
|
||||||
|
const key = this.scene.add.text(3, slotSize - 3, label, {
|
||||||
|
fontSize: "12px",
|
||||||
|
color: "#D4AF37",
|
||||||
|
fontStyle: "bold"
|
||||||
|
}).setOrigin(0, 1);
|
||||||
|
|
||||||
|
const slotContainer = this.scene.add.container(x, 0, [g, key]);
|
||||||
|
slotContainer.setData("index", i);
|
||||||
|
this.slots.push(slotContainer);
|
||||||
|
this.container.add(slotContainer);
|
||||||
|
|
||||||
|
// Input
|
||||||
|
const hitArea = new Phaser.Geom.Rectangle(0, 0, slotSize, slotSize);
|
||||||
|
slotContainer.setInteractive(hitArea, Phaser.Geom.Rectangle.Contains);
|
||||||
|
this.scene.input.setDraggable(slotContainer);
|
||||||
|
|
||||||
|
slotContainer.on("pointerdown", () => {
|
||||||
|
});
|
||||||
|
|
||||||
|
slotContainer.on("pointerup", (pointer: Phaser.Input.Pointer) => {
|
||||||
|
// If we didn't drag, then activate
|
||||||
|
if (this.draggedSlotIndex === null && pointer.getDistance() < 10) {
|
||||||
|
this.activateSlot(i);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targetIndex !== null && targetIndex !== startIndex) {
|
// Drag and Drop Events
|
||||||
// Swap or Move
|
this.scene.input.on("dragstart", (pointer: Phaser.Input.Pointer, gameObject: Phaser.GameObjects.Container) => {
|
||||||
const temp = this.assignedIds[startIndex];
|
// Only handle if it's one of our slots
|
||||||
this.assignedIds[startIndex] = this.assignedIds[targetIndex];
|
const index = gameObject.getData("index");
|
||||||
this.assignedIds[targetIndex] = temp;
|
if (index === undefined || !this.slots.includes(gameObject)) return;
|
||||||
console.log(`Moved/Swapped slot ${startIndex} to ${targetIndex}`);
|
|
||||||
} else if (targetIndex === null) {
|
const item = this.itemMap[index];
|
||||||
// Check if dropped over inventory backpack
|
if (!item) return;
|
||||||
const gameUI = this.scene as any;
|
|
||||||
if (gameUI.inventory && gameUI.inventory.isPointerOver(pointer.x, pointer.y)) {
|
this.draggedSlotIndex = index;
|
||||||
// Clear the quick slot (returning to backpack)
|
|
||||||
this.assignedIds[startIndex] = "";
|
// Setup drag icon
|
||||||
console.log(`Cleared quick slot ${startIndex} (returned to backpack)`);
|
if (!this.dragIcon) {
|
||||||
|
this.dragIcon = this.scene.add.sprite(0, 0, item.textureKey ?? "items", item.spriteIndex);
|
||||||
|
this.dragIcon.setDepth(2000).setScale(2.5).setAlpha(0.7);
|
||||||
} else {
|
} else {
|
||||||
// Dropped outside - drop on ground
|
this.dragIcon.setTexture(item.textureKey ?? "items", item.spriteIndex);
|
||||||
const item = this.itemMap[startIndex];
|
this.dragIcon.setVisible(true);
|
||||||
if (item) {
|
}
|
||||||
const gameScene = this.scene.scene.get("GameScene") as any;
|
this.dragIcon.setPosition(pointer.x, pointer.y);
|
||||||
gameScene.events.emit("drop-item", {
|
|
||||||
itemId: item.id,
|
// Ghost the original slot's item
|
||||||
pointerX: pointer.x,
|
const sprite = gameObject.list.find(child => child instanceof Phaser.GameObjects.Sprite) as Phaser.GameObjects.Sprite;
|
||||||
pointerY: pointer.y
|
if (sprite) sprite.setAlpha(0.3);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Clear the slot
|
this.scene.input.on("drag", (pointer: Phaser.Input.Pointer) => {
|
||||||
this.assignedIds[startIndex] = "";
|
if (this.dragIcon) {
|
||||||
|
this.dragIcon.setPosition(pointer.x, pointer.y);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.scene.input.on("dragend", (pointer: Phaser.Input.Pointer, gameObject: Phaser.GameObjects.Container) => {
|
||||||
|
if (this.draggedSlotIndex === null || !this.slots.includes(gameObject)) return;
|
||||||
|
|
||||||
|
const startIndex = this.draggedSlotIndex;
|
||||||
|
this.draggedSlotIndex = null;
|
||||||
|
if (this.dragIcon) this.dragIcon.setVisible(false);
|
||||||
|
|
||||||
|
// Reset alpha of original sprite
|
||||||
|
const sprite = gameObject.list.find(child => child instanceof Phaser.GameObjects.Sprite) as Phaser.GameObjects.Sprite;
|
||||||
|
if (sprite) sprite.setAlpha(1.0);
|
||||||
|
|
||||||
|
// Determine if we dropped on another slot
|
||||||
|
let targetIndex: number | null = null;
|
||||||
|
const slotSize = 48;
|
||||||
|
const slotSpacing = 4;
|
||||||
|
|
||||||
|
// Calculate pointer position relative to the quick-slot container
|
||||||
|
// Since container has scrollFactor(0), its screen position is fixed
|
||||||
|
const localX = pointer.x - this.container.x;
|
||||||
|
const localY = pointer.y - this.container.y;
|
||||||
|
|
||||||
|
// Check if pointer is within the vertical bounds of the slots
|
||||||
|
if (localY >= 0 && localY <= slotSize) {
|
||||||
|
// Calculate which slot index the pointer is over
|
||||||
|
const index = Math.floor(localX / (slotSize + slotSpacing));
|
||||||
|
const remainder = localX % (slotSize + slotSpacing);
|
||||||
|
|
||||||
|
// Ensure index is valid and pointer is within the slot's actual area (not spacing)
|
||||||
|
if (index >= 0 && index < 10 && remainder <= slotSize) {
|
||||||
|
targetIndex = index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Trigger UI refresh to reflect changes on the correct event bus
|
if (targetIndex !== null && targetIndex !== startIndex) {
|
||||||
const gameScene = this.scene.scene.get("GameScene");
|
// Swap or Move
|
||||||
gameScene.events.emit("request-ui-update");
|
const temp = this.assignedIds[startIndex];
|
||||||
});
|
this.assignedIds[startIndex] = this.assignedIds[targetIndex];
|
||||||
|
this.assignedIds[targetIndex] = temp;
|
||||||
|
console.log(`Moved/Swapped slot ${startIndex} to ${targetIndex}`);
|
||||||
|
} else if (targetIndex === null) {
|
||||||
|
// Check if dropped over inventory backpack
|
||||||
|
const gameUI = this.scene as any;
|
||||||
|
if (gameUI.inventory && gameUI.inventory.isPointerOver(pointer.x, pointer.y)) {
|
||||||
|
// Clear the quick slot (returning to backpack)
|
||||||
|
this.assignedIds[startIndex] = "";
|
||||||
|
console.log(`Cleared quick slot ${startIndex} (returned to backpack)`);
|
||||||
|
} else {
|
||||||
|
// Dropped outside - drop on ground
|
||||||
|
const item = this.itemMap[startIndex];
|
||||||
|
if (item) {
|
||||||
|
const gameScene = this.scene.scene.get("GameScene") as any;
|
||||||
|
gameScene.events.emit("drop-item", {
|
||||||
|
itemId: item.id,
|
||||||
|
pointerX: pointer.x,
|
||||||
|
pointerY: pointer.y
|
||||||
|
});
|
||||||
|
|
||||||
// Keyboard inputs
|
// Clear the slot
|
||||||
this.scene.input.keyboard?.on("keydown-ONE", () => this.activateSlot(0));
|
this.assignedIds[startIndex] = "";
|
||||||
this.scene.input.keyboard?.on("keydown-TWO", () => this.activateSlot(1));
|
}
|
||||||
this.scene.input.keyboard?.on("keydown-THREE", () => this.activateSlot(2));
|
}
|
||||||
this.scene.input.keyboard?.on("keydown-FOUR", () => this.activateSlot(3));
|
}
|
||||||
this.scene.input.keyboard?.on("keydown-FIVE", () => this.activateSlot(4));
|
|
||||||
this.scene.input.keyboard?.on("keydown-SIX", () => this.activateSlot(5));
|
|
||||||
this.scene.input.keyboard?.on("keydown-SEVEN", () => this.activateSlot(6));
|
|
||||||
this.scene.input.keyboard?.on("keydown-EIGHT", () => this.activateSlot(7));
|
|
||||||
this.scene.input.keyboard?.on("keydown-NINE", () => this.activateSlot(8));
|
|
||||||
this.scene.input.keyboard?.on("keydown-ZERO", () => this.activateSlot(9));
|
|
||||||
}
|
|
||||||
|
|
||||||
update(player: CombatantActor, activeItemId?: string | null) {
|
// Trigger UI refresh to reflect changes on the correct event bus
|
||||||
if (!player.inventory) return;
|
const gameScene = this.scene.scene.get("GameScene");
|
||||||
|
gameScene.events.emit("request-ui-update");
|
||||||
|
});
|
||||||
|
|
||||||
const slotSize = 48;
|
// Keyboard inputs
|
||||||
|
this.scene.input.keyboard?.on("keydown-ONE", () => this.activateSlot(0));
|
||||||
|
this.scene.input.keyboard?.on("keydown-TWO", () => this.activateSlot(1));
|
||||||
|
this.scene.input.keyboard?.on("keydown-THREE", () => this.activateSlot(2));
|
||||||
|
this.scene.input.keyboard?.on("keydown-FOUR", () => this.activateSlot(3));
|
||||||
|
this.scene.input.keyboard?.on("keydown-FIVE", () => this.activateSlot(4));
|
||||||
|
this.scene.input.keyboard?.on("keydown-SIX", () => this.activateSlot(5));
|
||||||
|
this.scene.input.keyboard?.on("keydown-SEVEN", () => this.activateSlot(6));
|
||||||
|
this.scene.input.keyboard?.on("keydown-EIGHT", () => this.activateSlot(7));
|
||||||
|
this.scene.input.keyboard?.on("keydown-NINE", () => this.activateSlot(8));
|
||||||
|
this.scene.input.keyboard?.on("keydown-ZERO", () => this.activateSlot(9));
|
||||||
|
|
||||||
// Update slots based on inventory availability
|
// Global Slider Container
|
||||||
for (let i = 0; i < 10; i++) {
|
this.reloadSliderContainer = this.scene.add.container(
|
||||||
const desiredId = this.assignedIds[i];
|
totalWidth / 2,
|
||||||
const slot = this.slots[i];
|
-40
|
||||||
const bgGraphics = slot.list[0] as Phaser.GameObjects.Graphics;
|
);
|
||||||
|
this.container.add(this.reloadSliderContainer);
|
||||||
// Clear previous item icon if any (children > 2, since 0=bg, 1=text)
|
}
|
||||||
if (slot.list.length > 2) {
|
|
||||||
slot.removeBetween(2, undefined, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (desiredId) {
|
update(player: CombatantActor, activeItemId?: string | null) {
|
||||||
const foundItem = player.inventory.items.find(it => it.id === desiredId);
|
if (!player.inventory) return;
|
||||||
this.itemMap[i] = foundItem || null;
|
|
||||||
|
|
||||||
const isActive = foundItem && foundItem.id === activeItemId;
|
|
||||||
|
|
||||||
// Redraw background based on active state
|
const slotSize = 48;
|
||||||
bgGraphics.clear();
|
|
||||||
|
// Update slots based on inventory availability
|
||||||
// Dark background
|
for (let i = 0; i < 10; i++) {
|
||||||
bgGraphics.fillStyle(0x2a1f3d, 0.95);
|
const desiredId = this.assignedIds[i];
|
||||||
bgGraphics.fillRect(0, 0, slotSize, slotSize);
|
const slot = this.slots[i];
|
||||||
|
const bgGraphics = slot.list[0] as Phaser.GameObjects.Graphics;
|
||||||
// Border - subtle cyan for active, gold for normal
|
|
||||||
if (isActive) {
|
// Clear previous item icon if any (children > 2, since 0=bg, 1=text)
|
||||||
bgGraphics.lineStyle(2, 0x00E5FF, 1); // Cyan highlight
|
if (slot.list.length > 2) {
|
||||||
} else {
|
slot.removeBetween(2, undefined, true);
|
||||||
bgGraphics.lineStyle(2, 0xD4AF37, 1); // Gold border
|
}
|
||||||
}
|
|
||||||
bgGraphics.strokeRect(0, 0, slotSize, slotSize);
|
if (desiredId) {
|
||||||
|
const foundItem = player.inventory.items.find(it => it.id === desiredId);
|
||||||
|
this.itemMap[i] = foundItem || null;
|
||||||
|
|
||||||
|
const isActive = foundItem && foundItem.id === activeItemId;
|
||||||
|
|
||||||
|
// Redraw background based on active state
|
||||||
|
bgGraphics.clear();
|
||||||
|
|
||||||
|
// Dark background
|
||||||
|
bgGraphics.fillStyle(0x2a1f3d, 0.95);
|
||||||
|
bgGraphics.fillRect(0, 0, slotSize, slotSize);
|
||||||
|
|
||||||
|
// Border - subtle cyan for active, gold for normal
|
||||||
|
if (isActive) {
|
||||||
|
bgGraphics.lineStyle(2, 0x00E5FF, 1); // Cyan highlight
|
||||||
|
} else {
|
||||||
|
bgGraphics.lineStyle(2, 0xD4AF37, 1); // Gold border
|
||||||
|
}
|
||||||
|
bgGraphics.strokeRect(0, 0, slotSize, slotSize);
|
||||||
|
|
||||||
if (foundItem) {
|
if (foundItem) {
|
||||||
// Use ItemSpriteFactory for glow effect on variants
|
// Use ItemSpriteFactory for glow effect on variants
|
||||||
@@ -251,85 +260,196 @@ export class QuickSlotComponent {
|
|||||||
slot.add(display);
|
slot.add(display);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reloading overlay
|
// Reloading overlay logic removed from individual slots -> Replacing with active lock symbol
|
||||||
if (foundItem.type === "Weapon" && foundItem.weaponType === "ranged" && foundItem.reloadingTurnsLeft > 0) {
|
if (foundItem.type === "Weapon" && foundItem.weaponType === "ranged" && foundItem.reloadingTurnsLeft > 0) {
|
||||||
const reloadText = this.scene.add.text(slotSize / 2, slotSize / 2, "RELOADING", {
|
// Transparent grey overlay
|
||||||
fontSize: "8px",
|
const overlay = this.scene.add.graphics();
|
||||||
color: "#ff0000",
|
overlay.fillStyle(0x000000, 0.5);
|
||||||
fontStyle: "bold",
|
overlay.fillRect(0, 0, slotSize, slotSize);
|
||||||
backgroundColor: "#000000aa",
|
slot.add(overlay);
|
||||||
padding: { x: 2, y: 1 }
|
|
||||||
}).setOrigin(0.5, 0.5);
|
|
||||||
slot.add(reloadText);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.itemMap[i] = null;
|
this.itemMap[i] = null;
|
||||||
// Reset bg
|
// Reset bg
|
||||||
bgGraphics.clear();
|
bgGraphics.clear();
|
||||||
bgGraphics.fillStyle(0x2a1f3d, 0.95);
|
bgGraphics.fillStyle(0x2a1f3d, 0.95);
|
||||||
bgGraphics.fillRect(0, 0, slotSize, slotSize);
|
bgGraphics.fillRect(0, 0, slotSize, slotSize);
|
||||||
bgGraphics.lineStyle(2, 0xD4AF37, 1);
|
bgGraphics.lineStyle(2, 0xD4AF37, 1);
|
||||||
bgGraphics.strokeRect(0, 0, slotSize, slotSize);
|
bgGraphics.strokeRect(0, 0, slotSize, slotSize);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private activateSlot(index: number) {
|
|
||||||
const item = this.itemMap[index];
|
|
||||||
if (item) {
|
|
||||||
console.log(`Activating slot ${index + 1}: ${item.name}`);
|
|
||||||
// Emit event to GameScene to handle item usage
|
|
||||||
const gameScene = this.scene.scene.get("GameScene");
|
|
||||||
gameScene.events.emit("use-item", { itemId: item.id });
|
|
||||||
} else {
|
|
||||||
console.log(`Slot ${index + 1} is empty`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public isPointerOver(x: number, y: number): boolean {
|
|
||||||
const slotSize = 48;
|
|
||||||
const slotSpacing = 4;
|
|
||||||
const totalWidth = (slotSize + slotSpacing) * 10 - slotSpacing;
|
|
||||||
|
|
||||||
const localX = x - this.container.x;
|
|
||||||
const localY = y - this.container.y;
|
|
||||||
|
|
||||||
return localX >= 0 && localX <= totalWidth && localY >= 0 && localY <= slotSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getSlotIndexAt(x: number, y: number): number | null {
|
|
||||||
const slotSize = 48;
|
|
||||||
const slotSpacing = 4;
|
|
||||||
|
|
||||||
const localX = x - this.container.x;
|
|
||||||
const localY = y - this.container.y;
|
|
||||||
|
|
||||||
if (localY >= 0 && localY <= slotSize) {
|
|
||||||
const index = Math.floor(localX / (slotSize + slotSpacing));
|
|
||||||
const remainder = localX % (slotSize + slotSpacing);
|
|
||||||
|
|
||||||
if (index >= 0 && index < 10 && remainder <= slotSize) {
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public assignItem(index: number, itemId: string) {
|
|
||||||
if (index >= 0 && index < 10) {
|
|
||||||
// Prevent duplicate assignments
|
|
||||||
const existingIndex = this.assignedIds.indexOf(itemId);
|
|
||||||
if (existingIndex !== -1 && existingIndex !== index) {
|
|
||||||
this.assignedIds[existingIndex] = "";
|
|
||||||
console.log(`Cleared duplicate assignment of ${itemId} from slot ${existingIndex}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.assignedIds[index] = itemId;
|
// -----------------------------------------------------------------------
|
||||||
|
// Global Reload Slider Logic
|
||||||
// Refresh UI
|
// -----------------------------------------------------------------------
|
||||||
const gameScene = this.scene.scene.get("GameScene");
|
this.reloadSliderContainer.removeAll(true);
|
||||||
gameScene.events.emit("request-ui-update");
|
|
||||||
|
// Find ANY reloading item in the inventory (that needs the UI)
|
||||||
|
// Usually the active one, or just the first one found since turn-based RL doesn't do parallel reloading much
|
||||||
|
const reloadingItem = player.inventory.items.find(
|
||||||
|
it => it.type === "Weapon" && it.weaponType === "ranged" && it.reloadingTurnsLeft > 0
|
||||||
|
) as any; // Cast for easier access to RangedWeaponItem props
|
||||||
|
|
||||||
|
if (reloadingItem) {
|
||||||
|
const maxTurns = GAME_CONFIG.player.reloadDuration;
|
||||||
|
const progress = 1 - (reloadingItem.reloadingTurnsLeft / maxTurns);
|
||||||
|
|
||||||
|
const sliderWidth = 260; // Half of ~520px
|
||||||
|
const sliderHeight = 14;
|
||||||
|
const grooveColor = 0x1a1a1a;
|
||||||
|
const trackColor = 0x4a4a4a; // Stone Grey
|
||||||
|
const handleColor = 0x888888; // Lighter Stone
|
||||||
|
|
||||||
|
// 1. Draw Track Base (Stone Slab)
|
||||||
|
const g = this.scene.add.graphics();
|
||||||
|
|
||||||
|
// Stone Border / Bevel
|
||||||
|
g.lineStyle(4, 0x666666); // Light edge (Top/Left)
|
||||||
|
g.beginPath();
|
||||||
|
g.moveTo(-sliderWidth / 2, sliderHeight / 2);
|
||||||
|
g.lineTo(-sliderWidth / 2, -sliderHeight / 2);
|
||||||
|
g.lineTo(sliderWidth / 2, -sliderHeight / 2);
|
||||||
|
g.strokePath();
|
||||||
|
|
||||||
|
g.lineStyle(4, 0x222222); // Dark edge (Bottom/Right)
|
||||||
|
g.beginPath();
|
||||||
|
g.moveTo(sliderWidth / 2, -sliderHeight / 2);
|
||||||
|
g.lineTo(sliderWidth / 2, sliderHeight / 2);
|
||||||
|
g.lineTo(-sliderWidth / 2, sliderHeight / 2);
|
||||||
|
g.strokePath();
|
||||||
|
|
||||||
|
// Main Track Body
|
||||||
|
g.fillStyle(trackColor);
|
||||||
|
g.fillRect(-sliderWidth / 2, -sliderHeight / 2, sliderWidth, sliderHeight);
|
||||||
|
|
||||||
|
// Groove (Chiseled out)
|
||||||
|
g.fillStyle(grooveColor);
|
||||||
|
g.fillRect(-sliderWidth / 2 + 4, -2, sliderWidth - 8, 4);
|
||||||
|
|
||||||
|
// Tick marks (Etched into stone)
|
||||||
|
g.lineStyle(2, 0x222222, 0.5);
|
||||||
|
// Draw ticks based on actual turns
|
||||||
|
for (let k = 0; k <= maxTurns; k++) {
|
||||||
|
const tx = (-sliderWidth / 2 + 4) + (k * ((sliderWidth - 8) / maxTurns));
|
||||||
|
g.moveTo(tx, -sliderHeight / 2);
|
||||||
|
g.lineTo(tx, -4); // Stop at groove
|
||||||
|
|
||||||
|
g.moveTo(tx, 4); // Start after groove
|
||||||
|
g.lineTo(tx, sliderHeight / 2);
|
||||||
|
}
|
||||||
|
g.strokePath();
|
||||||
|
|
||||||
|
this.reloadSliderContainer.add(g);
|
||||||
|
|
||||||
|
// 2. Draw Handle / Knob (Stone Block)
|
||||||
|
const safeProgress = Math.max(0, Math.min(1, progress));
|
||||||
|
const knobWidth = 20;
|
||||||
|
const knobHeight = 28;
|
||||||
|
const travelLength = (sliderWidth - 8) - knobWidth; // Subtract padding/groove end
|
||||||
|
const startX = (-sliderWidth / 2 + 4) + knobWidth / 2;
|
||||||
|
const knobX = startX + (safeProgress * travelLength);
|
||||||
|
|
||||||
|
const knob = this.scene.add.graphics();
|
||||||
|
|
||||||
|
// Knob Body
|
||||||
|
knob.fillStyle(handleColor);
|
||||||
|
knob.fillRect(knobX - knobWidth / 2, -knobHeight / 2, knobWidth, knobHeight);
|
||||||
|
|
||||||
|
// Stone texture (noise/dots) - simplified as darker specks
|
||||||
|
knob.fillStyle(0x555555, 0.4);
|
||||||
|
knob.fillRect(knobX - knobWidth / 2 + 2, -knobHeight / 2 + 2, 4, 4);
|
||||||
|
knob.fillRect(knobX + knobWidth / 2 - 6, knobHeight / 2 - 6, 4, 4);
|
||||||
|
|
||||||
|
// 3D Bevel for Block
|
||||||
|
knob.lineStyle(2, 0xaaaaaa); // Highlight
|
||||||
|
knob.beginPath();
|
||||||
|
knob.moveTo(knobX - knobWidth / 2, knobHeight / 2);
|
||||||
|
knob.lineTo(knobX - knobWidth / 2, -knobHeight / 2);
|
||||||
|
knob.lineTo(knobX + knobWidth / 2, -knobHeight / 2);
|
||||||
|
knob.strokePath();
|
||||||
|
|
||||||
|
knob.lineStyle(2, 0x333333); // Shadow
|
||||||
|
knob.beginPath();
|
||||||
|
knob.moveTo(knobX + knobWidth / 2, -knobHeight / 2);
|
||||||
|
knob.lineTo(knobX + knobWidth / 2, knobHeight / 2);
|
||||||
|
knob.lineTo(knobX - knobWidth / 2, knobHeight / 2);
|
||||||
|
knob.strokePath();
|
||||||
|
|
||||||
|
// Center indent line
|
||||||
|
knob.lineStyle(2, 0x444444);
|
||||||
|
knob.moveTo(knobX, -knobHeight / 2 + 6);
|
||||||
|
knob.lineTo(knobX, knobHeight / 2 - 6);
|
||||||
|
knob.strokePath();
|
||||||
|
|
||||||
|
this.reloadSliderContainer.add(knob);
|
||||||
|
|
||||||
|
const label = this.scene.add.text(0, sliderHeight + 4, "RELOADING", {
|
||||||
|
fontSize: "12px",
|
||||||
|
color: "#888888",
|
||||||
|
fontFamily: "monospace",
|
||||||
|
fontStyle: "bold",
|
||||||
|
shadow: { offsetX: 1, offsetY: 1, color: "#000000", blur: 0, stroke: true, fill: true }
|
||||||
|
}).setOrigin(0.5, 0);
|
||||||
|
this.reloadSliderContainer.add(label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private activateSlot(index: number) {
|
||||||
|
const item = this.itemMap[index];
|
||||||
|
if (item) {
|
||||||
|
console.log(`Activating slot ${index + 1}: ${item.name}`);
|
||||||
|
// Emit event to GameScene to handle item usage
|
||||||
|
const gameScene = this.scene.scene.get("GameScene");
|
||||||
|
gameScene.events.emit("use-item", { itemId: item.id });
|
||||||
|
} else {
|
||||||
|
console.log(`Slot ${index + 1} is empty`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public isPointerOver(x: number, y: number): boolean {
|
||||||
|
const slotSize = 48;
|
||||||
|
const slotSpacing = 4;
|
||||||
|
const totalWidth = (slotSize + slotSpacing) * 10 - slotSpacing;
|
||||||
|
|
||||||
|
const localX = x - this.container.x;
|
||||||
|
const localY = y - this.container.y;
|
||||||
|
|
||||||
|
return localX >= 0 && localX <= totalWidth && localY >= 0 && localY <= slotSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getSlotIndexAt(x: number, y: number): number | null {
|
||||||
|
const slotSize = 48;
|
||||||
|
const slotSpacing = 4;
|
||||||
|
|
||||||
|
const localX = x - this.container.x;
|
||||||
|
const localY = y - this.container.y;
|
||||||
|
|
||||||
|
if (localY >= 0 && localY <= slotSize) {
|
||||||
|
const index = Math.floor(localX / (slotSize + slotSpacing));
|
||||||
|
const remainder = localX % (slotSize + slotSpacing);
|
||||||
|
|
||||||
|
if (index >= 0 && index < 10 && remainder <= slotSize) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public assignItem(index: number, itemId: string) {
|
||||||
|
if (index >= 0 && index < 10) {
|
||||||
|
// Prevent duplicate assignments
|
||||||
|
const existingIndex = this.assignedIds.indexOf(itemId);
|
||||||
|
if (existingIndex !== -1 && existingIndex !== index) {
|
||||||
|
this.assignedIds[existingIndex] = "";
|
||||||
|
console.log(`Cleared duplicate assignment of ${itemId} from slot ${existingIndex}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.assignedIds[index] = itemId;
|
||||||
|
|
||||||
|
// Refresh UI
|
||||||
|
const gameScene = this.scene.scene.get("GameScene");
|
||||||
|
gameScene.events.emit("request-ui-update");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user