Highlight active item slot and activate when shortcut key is pressed
This commit is contained in:
@@ -155,3 +155,12 @@ export type World = {
|
|||||||
actors: Map<EntityId, Actor>;
|
actors: Map<EntityId, Actor>;
|
||||||
exit: Vec2;
|
exit: Vec2;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface UIUpdatePayload {
|
||||||
|
world: World;
|
||||||
|
playerId: EntityId;
|
||||||
|
floorIndex: number;
|
||||||
|
uiState: {
|
||||||
|
targetingItemId: string | null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
132
src/engine/__tests__/combat_logic.test.ts
Normal file
132
src/engine/__tests__/combat_logic.test.ts
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
|
||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import { getClosestVisibleEnemy } from "../gameplay/CombatLogic";
|
||||||
|
import type { World, CombatantActor } from "../../core/types";
|
||||||
|
|
||||||
|
describe("CombatLogic - getClosestVisibleEnemy", () => {
|
||||||
|
|
||||||
|
// Helper to create valid default stats for testing
|
||||||
|
const createMockStats = () => ({
|
||||||
|
hp: 10, maxHp: 10, attack: 1, defense: 0,
|
||||||
|
accuracy: 100, evasion: 0, critChance: 0, critMultiplier: 0,
|
||||||
|
blockChance: 0, lifesteal: 0, mana: 0, maxMana: 0,
|
||||||
|
level: 1, exp: 0, expToNextLevel: 100, luck: 0,
|
||||||
|
statPoints: 0, skillPoints: 0,
|
||||||
|
strength: 10, dexterity: 10, intelligence: 10,
|
||||||
|
passiveNodes: []
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return null if no enemies are visible", () => {
|
||||||
|
const world: World = {
|
||||||
|
width: 10,
|
||||||
|
height: 10,
|
||||||
|
tiles: new Array(100).fill(0),
|
||||||
|
actors: new Map(),
|
||||||
|
exit: { x: 9, y: 9 }
|
||||||
|
};
|
||||||
|
|
||||||
|
const player: CombatantActor = {
|
||||||
|
id: 0, category: "combatant", type: "player", pos: { x: 5, y: 5 }, isPlayer: true,
|
||||||
|
stats: createMockStats(),
|
||||||
|
inventory: { gold: 0, items: [] }, equipment: {},
|
||||||
|
speed: 1, energy: 0
|
||||||
|
};
|
||||||
|
world.actors.set(0, player);
|
||||||
|
|
||||||
|
const enemy: CombatantActor = {
|
||||||
|
id: 1, category: "combatant", type: "rat", pos: { x: 6, y: 6 }, isPlayer: false,
|
||||||
|
stats: createMockStats(),
|
||||||
|
speed: 1, energy: 0
|
||||||
|
};
|
||||||
|
world.actors.set(1, enemy);
|
||||||
|
|
||||||
|
// Mock seenArray where nothing is seen
|
||||||
|
const seenArray = new Uint8Array(100).fill(0);
|
||||||
|
|
||||||
|
const result = getClosestVisibleEnemy(world, player.pos, seenArray, 10);
|
||||||
|
expect(result).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return the closest visible enemy", () => {
|
||||||
|
const world: World = {
|
||||||
|
width: 10,
|
||||||
|
height: 10,
|
||||||
|
tiles: new Array(100).fill(0),
|
||||||
|
actors: new Map(),
|
||||||
|
exit: { x: 9, y: 9 }
|
||||||
|
};
|
||||||
|
|
||||||
|
const player: CombatantActor = {
|
||||||
|
id: 0, category: "combatant", type: "player", pos: { x: 5, y: 5 }, isPlayer: true,
|
||||||
|
stats: createMockStats(),
|
||||||
|
inventory: { gold: 0, items: [] }, equipment: {},
|
||||||
|
speed: 1, energy: 0
|
||||||
|
};
|
||||||
|
world.actors.set(0, player);
|
||||||
|
|
||||||
|
// Enemy 1: Close (distance sqrt(2) ~= 1.41)
|
||||||
|
const enemy1: CombatantActor = {
|
||||||
|
id: 1, category: "combatant", type: "rat", pos: { x: 6, y: 6 }, isPlayer: false,
|
||||||
|
stats: createMockStats(),
|
||||||
|
speed: 1, energy: 0
|
||||||
|
};
|
||||||
|
world.actors.set(1, enemy1);
|
||||||
|
|
||||||
|
// Enemy 2: Farther (distance sqrt(8) ~= 2.82)
|
||||||
|
const enemy2: CombatantActor = {
|
||||||
|
id: 2, category: "combatant", type: "rat", pos: { x: 7, y: 7 }, isPlayer: false,
|
||||||
|
stats: createMockStats(),
|
||||||
|
speed: 1, energy: 0
|
||||||
|
};
|
||||||
|
world.actors.set(2, enemy2);
|
||||||
|
|
||||||
|
// Mock seenArray where both are seen
|
||||||
|
const seenArray = new Uint8Array(100).fill(0);
|
||||||
|
seenArray[6 * 10 + 6] = 1; // Enemy 1 visible
|
||||||
|
seenArray[7 * 10 + 7] = 1; // Enemy 2 visible
|
||||||
|
|
||||||
|
const result = getClosestVisibleEnemy(world, player.pos, seenArray, 10);
|
||||||
|
expect(result).toEqual({ x: 6, y: 6 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should ignore invisible closer enemies and select visible farther ones", () => {
|
||||||
|
const world: World = {
|
||||||
|
width: 10,
|
||||||
|
height: 10,
|
||||||
|
tiles: new Array(100).fill(0),
|
||||||
|
actors: new Map(),
|
||||||
|
exit: { x: 9, y: 9 }
|
||||||
|
};
|
||||||
|
|
||||||
|
const player: CombatantActor = {
|
||||||
|
id: 0, category: "combatant", type: "player", pos: { x: 5, y: 5 }, isPlayer: true,
|
||||||
|
stats: createMockStats(),
|
||||||
|
inventory: { gold: 0, items: [] }, equipment: {},
|
||||||
|
speed: 1, energy: 0
|
||||||
|
};
|
||||||
|
world.actors.set(0, player);
|
||||||
|
|
||||||
|
// Enemy 1: Close but invisible
|
||||||
|
const enemy1: CombatantActor = {
|
||||||
|
id: 1, category: "combatant", type: "rat", pos: { x: 6, y: 6 }, isPlayer: false,
|
||||||
|
stats: createMockStats(),
|
||||||
|
speed: 1, energy: 0
|
||||||
|
};
|
||||||
|
world.actors.set(1, enemy1);
|
||||||
|
|
||||||
|
// Enemy 2: Farther but visible
|
||||||
|
const enemy2: CombatantActor = {
|
||||||
|
id: 2, category: "combatant", type: "rat", pos: { x: 8, y: 5 }, isPlayer: false,
|
||||||
|
stats: createMockStats(),
|
||||||
|
speed: 1, energy: 0
|
||||||
|
};
|
||||||
|
world.actors.set(2, enemy2);
|
||||||
|
|
||||||
|
// Mock seenArray where only Enemy 2 is seen
|
||||||
|
const seenArray = new Uint8Array(100).fill(0);
|
||||||
|
seenArray[5 * 10 + 8] = 1; // Enemy 2 visible at (8,5)
|
||||||
|
|
||||||
|
const result = getClosestVisibleEnemy(world, player.pos, seenArray, 10);
|
||||||
|
expect(result).toEqual({ x: 8, y: 5 });
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -51,3 +51,46 @@ export function traceProjectile(
|
|||||||
hitActorId
|
hitActorId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the closest visible enemy to a given position.
|
||||||
|
*/
|
||||||
|
export function getClosestVisibleEnemy(
|
||||||
|
world: World,
|
||||||
|
origin: Vec2,
|
||||||
|
seenTiles: Set<string> | boolean[] | Uint8Array, // Support various visibility structures
|
||||||
|
width?: number // Required if seenTiles is a flat array
|
||||||
|
): Vec2 | null {
|
||||||
|
let closestDistSq = Infinity;
|
||||||
|
let closestPos: Vec2 | null = null;
|
||||||
|
|
||||||
|
// Helper to check visibility
|
||||||
|
const isVisible = (x: number, y: number) => {
|
||||||
|
if (Array.isArray(seenTiles) || seenTiles instanceof Uint8Array || seenTiles instanceof Int8Array) {
|
||||||
|
// Flat array
|
||||||
|
if (!width) return false;
|
||||||
|
return (seenTiles as any)[y * width + x];
|
||||||
|
} else {
|
||||||
|
// Set<string>
|
||||||
|
return (seenTiles as Set<string>).has(`${x},${y}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const actor of world.actors.values()) {
|
||||||
|
if (actor.category !== "combatant" || actor.isPlayer) continue;
|
||||||
|
|
||||||
|
// Check visibility
|
||||||
|
if (!isVisible(actor.pos.x, actor.pos.y)) continue;
|
||||||
|
|
||||||
|
const dx = actor.pos.x - origin.x;
|
||||||
|
const dy = actor.pos.y - origin.y;
|
||||||
|
const distSq = dx*dx + dy*dy;
|
||||||
|
|
||||||
|
if (distSq < closestDistSq) {
|
||||||
|
closestDistSq = distSq;
|
||||||
|
closestPos = { x: actor.pos.x, y: actor.pos.y };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return closestPos;
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,14 +8,15 @@ import {
|
|||||||
type World,
|
type World,
|
||||||
type CombatantActor,
|
type CombatantActor,
|
||||||
type Item,
|
type Item,
|
||||||
type ItemDropActor
|
type ItemDropActor,
|
||||||
|
type UIUpdatePayload
|
||||||
} from "../core/types";
|
} from "../core/types";
|
||||||
import { TILE_SIZE } from "../core/constants";
|
import { TILE_SIZE } from "../core/constants";
|
||||||
import { inBounds, isBlocked, isPlayerOnExit, tryDestructTile } from "../engine/world/world-logic";
|
import { inBounds, isBlocked, isPlayerOnExit, tryDestructTile } from "../engine/world/world-logic";
|
||||||
import { findPathAStar } from "../engine/world/pathfinding";
|
import { findPathAStar } from "../engine/world/pathfinding";
|
||||||
import { applyAction, stepUntilPlayerTurn } from "../engine/simulation/simulation";
|
import { applyAction, stepUntilPlayerTurn } from "../engine/simulation/simulation";
|
||||||
import { generateWorld } from "../engine/world/generator";
|
import { generateWorld } from "../engine/world/generator";
|
||||||
import { traceProjectile } from "../engine/gameplay/CombatLogic";
|
import { traceProjectile, getClosestVisibleEnemy } from "../engine/gameplay/CombatLogic";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -53,6 +54,7 @@ export class GameScene extends Phaser.Scene {
|
|||||||
// Targeting Mode
|
// Targeting Mode
|
||||||
private isTargeting = false;
|
private isTargeting = false;
|
||||||
private targetingItem: string | null = null;
|
private targetingItem: string | null = null;
|
||||||
|
private targetCursor: { x: number, y: number } | null = null;
|
||||||
private targetingGraphics!: Phaser.GameObjects.Graphics;
|
private targetingGraphics!: Phaser.GameObjects.Graphics;
|
||||||
|
|
||||||
private turnCount = 0; // Track turns for mana regen
|
private turnCount = 0; // Track turns for mana regen
|
||||||
@@ -188,9 +190,38 @@ export class GameScene extends Phaser.Scene {
|
|||||||
this.emitUIUpdate();
|
this.emitUIUpdate();
|
||||||
}
|
}
|
||||||
} else if (item.throwable) {
|
} else if (item.throwable) {
|
||||||
|
// Check if already targeting this item -> verify intent to throw
|
||||||
|
if (this.isTargeting && this.targetingItem === item.id) {
|
||||||
|
if (this.targetCursor) {
|
||||||
|
this.executeThrow(this.targetCursor.x, this.targetCursor.y);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.targetingItem = item.id;
|
this.targetingItem = item.id;
|
||||||
this.isTargeting = true;
|
this.isTargeting = true;
|
||||||
|
|
||||||
|
// Auto-target closest visible enemy
|
||||||
|
const closest = getClosestVisibleEnemy(
|
||||||
|
this.world,
|
||||||
|
player.pos,
|
||||||
|
this.dungeonRenderer.seenArray,
|
||||||
|
this.world.width
|
||||||
|
);
|
||||||
|
|
||||||
|
if (closest) {
|
||||||
|
this.targetCursor = closest;
|
||||||
|
} else {
|
||||||
|
// Default to player pos or null?
|
||||||
|
// If we default to mouse pos, we need current mouse pos.
|
||||||
|
// Let's default to null and wait for mouse move, OR default to player pos forward?
|
||||||
|
// Let's just default to null until mouse moves.
|
||||||
|
this.targetCursor = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.drawTargetingLine();
|
||||||
console.log("Targeting Mode: ON");
|
console.log("Targeting Mode: ON");
|
||||||
|
this.emitUIUpdate();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -230,7 +261,11 @@ export class GameScene extends Phaser.Scene {
|
|||||||
this.input.on("pointermove", (p: Phaser.Input.Pointer) => {
|
this.input.on("pointermove", (p: Phaser.Input.Pointer) => {
|
||||||
if (!p.isDown) { // Even if not down, we might need to update targeting line
|
if (!p.isDown) { // Even if not down, we might need to update targeting line
|
||||||
if (this.isTargeting) {
|
if (this.isTargeting) {
|
||||||
this.updateTargetingLine(p);
|
const tx = Math.floor(p.worldX / TILE_SIZE);
|
||||||
|
const ty = Math.floor(p.worldY / TILE_SIZE);
|
||||||
|
// Only update if changed to avoid jitter if needed, but simple assignment is fine
|
||||||
|
this.targetCursor = { x: tx, y: ty };
|
||||||
|
this.drawTargetingLine();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -255,7 +290,10 @@ export class GameScene extends Phaser.Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.isTargeting) {
|
if (this.isTargeting) {
|
||||||
this.updateTargetingLine(p);
|
const tx = Math.floor(p.worldX / TILE_SIZE);
|
||||||
|
const ty = Math.floor(p.worldY / TILE_SIZE);
|
||||||
|
this.targetCursor = { x: tx, y: ty };
|
||||||
|
this.drawTargetingLine();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -265,9 +303,9 @@ export class GameScene extends Phaser.Scene {
|
|||||||
if (this.isTargeting) {
|
if (this.isTargeting) {
|
||||||
// Only Left Click throws
|
// Only Left Click throws
|
||||||
if (p.button === 0) {
|
if (p.button === 0) {
|
||||||
const tx = Math.floor(p.worldX / TILE_SIZE);
|
if (this.targetCursor) {
|
||||||
const ty = Math.floor(p.worldY / TILE_SIZE);
|
this.executeThrow(this.targetCursor.x, this.targetCursor.y);
|
||||||
this.executeThrow(tx, ty);
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -397,11 +435,15 @@ export class GameScene extends Phaser.Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private emitUIUpdate() {
|
private emitUIUpdate() {
|
||||||
this.events.emit("update-ui", {
|
const payload: UIUpdatePayload = {
|
||||||
world: this.world,
|
world: this.world,
|
||||||
playerId: this.playerId,
|
playerId: this.playerId,
|
||||||
floorIndex: this.floorIndex
|
floorIndex: this.floorIndex,
|
||||||
});
|
uiState: {
|
||||||
|
targetingItemId: this.targetingItem
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.events.emit("update-ui", payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
private commitPlayerAction(action: Action) {
|
private commitPlayerAction(action: Action) {
|
||||||
@@ -547,8 +589,12 @@ export class GameScene extends Phaser.Scene {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateTargetingLine(p: Phaser.Input.Pointer) {
|
private drawTargetingLine() {
|
||||||
if (!this.world) return;
|
if (!this.world || !this.targetCursor) {
|
||||||
|
this.targetingGraphics.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.targetingGraphics.clear();
|
this.targetingGraphics.clear();
|
||||||
|
|
||||||
const player = this.world.actors.get(this.playerId) as CombatantActor;
|
const player = this.world.actors.get(this.playerId) as CombatantActor;
|
||||||
@@ -557,22 +603,22 @@ export class GameScene extends Phaser.Scene {
|
|||||||
const startX = player.pos.x * TILE_SIZE + TILE_SIZE / 2;
|
const startX = player.pos.x * TILE_SIZE + TILE_SIZE / 2;
|
||||||
const startY = player.pos.y * TILE_SIZE + TILE_SIZE / 2;
|
const startY = player.pos.y * TILE_SIZE + TILE_SIZE / 2;
|
||||||
|
|
||||||
const endX = p.worldX;
|
const endX = this.targetCursor.x * TILE_SIZE + TILE_SIZE / 2;
|
||||||
const endY = p.worldY;
|
const endY = this.targetCursor.y * TILE_SIZE + TILE_SIZE / 2;
|
||||||
|
|
||||||
this.targetingGraphics.lineStyle(2, 0xff0000, 0.7);
|
this.targetingGraphics.lineStyle(2, 0xff0000, 0.7);
|
||||||
this.targetingGraphics.lineBetween(startX, startY, endX, endY);
|
this.targetingGraphics.lineBetween(startX, startY, endX, endY);
|
||||||
|
|
||||||
const tx = Math.floor(endX / TILE_SIZE);
|
this.targetingGraphics.strokeRect(this.targetCursor.x * TILE_SIZE, this.targetCursor.y * TILE_SIZE, TILE_SIZE, TILE_SIZE);
|
||||||
const ty = Math.floor(endY / TILE_SIZE);
|
|
||||||
this.targetingGraphics.strokeRect(tx * TILE_SIZE, ty * TILE_SIZE, TILE_SIZE, TILE_SIZE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private cancelTargeting() {
|
private cancelTargeting() {
|
||||||
this.isTargeting = false;
|
this.isTargeting = false;
|
||||||
this.targetingItem = null;
|
this.targetingItem = null;
|
||||||
|
this.targetCursor = null;
|
||||||
this.targetingGraphics.clear();
|
this.targetingGraphics.clear();
|
||||||
console.log("Targeting cancelled");
|
console.log("Targeting cancelled");
|
||||||
|
this.emitUIUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
private executeThrow(targetX: number, targetY: number) {
|
private executeThrow(targetX: number, targetY: number) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import { type World, type EntityId, type CombatantActor, type Stats } from "../core/types";
|
import { type CombatantActor, type Stats, type UIUpdatePayload } from "../core/types";
|
||||||
import { HudComponent } from "./components/HudComponent";
|
import { HudComponent } from "./components/HudComponent";
|
||||||
import { MenuComponent } from "./components/MenuComponent";
|
import { MenuComponent } from "./components/MenuComponent";
|
||||||
import { InventoryOverlay } from "./components/InventoryOverlay";
|
import { InventoryOverlay } from "./components/InventoryOverlay";
|
||||||
@@ -42,8 +42,8 @@ export default class GameUI extends Phaser.Scene {
|
|||||||
|
|
||||||
|
|
||||||
// Listen for updates from GameScene
|
// Listen for updates from GameScene
|
||||||
gameScene.events.on("update-ui", (data: { world: World; playerId: EntityId; floorIndex: number }) => {
|
gameScene.events.on("update-ui", (payload: UIUpdatePayload) => {
|
||||||
this.updateUI(data.world, data.playerId, data.floorIndex);
|
this.updateUI(payload);
|
||||||
});
|
});
|
||||||
|
|
||||||
gameScene.events.on("toggle-menu", () => {
|
gameScene.events.on("toggle-menu", () => {
|
||||||
@@ -96,13 +96,14 @@ export default class GameUI extends Phaser.Scene {
|
|||||||
this.death.show(data);
|
this.death.show(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateUI(world: World, playerId: EntityId, floorIndex: number) {
|
private updateUI(payload: UIUpdatePayload) {
|
||||||
|
const { world, playerId, floorIndex, uiState } = payload;
|
||||||
const player = world.actors.get(playerId) as CombatantActor;
|
const player = world.actors.get(playerId) as CombatantActor;
|
||||||
if (!player) return;
|
if (!player) return;
|
||||||
|
|
||||||
this.hud.update(player.stats, floorIndex);
|
this.hud.update(player.stats, floorIndex);
|
||||||
this.inventory.update(player);
|
this.inventory.update(player);
|
||||||
this.character.update(player);
|
this.character.update(player);
|
||||||
this.quickSlots.update(player);
|
this.quickSlots.update(player, uiState.targetingItemId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,13 +53,14 @@ export class QuickSlotComponent {
|
|||||||
this.scene.input.keyboard?.on("keydown-FOUR", () => this.activateSlot(3));
|
this.scene.input.keyboard?.on("keydown-FOUR", () => this.activateSlot(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
update(player: CombatantActor) {
|
update(player: CombatantActor, activeItemId?: string | null) {
|
||||||
if (!player.inventory) return;
|
if (!player.inventory) return;
|
||||||
|
|
||||||
// Update slots based on inventory availability
|
// Update slots based on inventory availability
|
||||||
for (let i = 0; i < 4; i++) {
|
for (let i = 0; i < 4; i++) {
|
||||||
const desiredId = this.assignedIds[i];
|
const desiredId = this.assignedIds[i];
|
||||||
const slot = this.slots[i];
|
const slot = this.slots[i];
|
||||||
|
const bgGraphics = slot.list[0] as Phaser.GameObjects.Graphics;
|
||||||
|
|
||||||
// Clear previous item icon if any (children > 2, since 0=bg, 1=text)
|
// Clear previous item icon if any (children > 2, since 0=bg, 1=text)
|
||||||
if (slot.list.length > 2) {
|
if (slot.list.length > 2) {
|
||||||
@@ -70,6 +71,20 @@ export class QuickSlotComponent {
|
|||||||
const foundItem = player.inventory.items.find(it => it.id === desiredId);
|
const foundItem = player.inventory.items.find(it => it.id === desiredId);
|
||||||
this.itemMap[i] = foundItem || null;
|
this.itemMap[i] = foundItem || null;
|
||||||
|
|
||||||
|
const isActive = foundItem && foundItem.id === activeItemId;
|
||||||
|
|
||||||
|
// Redraw background based on active state
|
||||||
|
bgGraphics.clear();
|
||||||
|
bgGraphics.fillStyle(0x1a1a1a, 0.8);
|
||||||
|
bgGraphics.fillRect(0, 0, 40, 40);
|
||||||
|
|
||||||
|
if (isActive) {
|
||||||
|
bgGraphics.lineStyle(2, 0xffff00); // Gold highlight
|
||||||
|
} else {
|
||||||
|
bgGraphics.lineStyle(1, 0x555555); // Default gray
|
||||||
|
}
|
||||||
|
bgGraphics.strokeRect(0, 0, 40, 40);
|
||||||
|
|
||||||
if (foundItem) {
|
if (foundItem) {
|
||||||
const texture = foundItem.textureKey ?? "items";
|
const texture = foundItem.textureKey ?? "items";
|
||||||
const sprite = this.scene.add.sprite(20, 20, texture, foundItem.spriteIndex);
|
const sprite = this.scene.add.sprite(20, 20, texture, foundItem.spriteIndex);
|
||||||
@@ -87,6 +102,12 @@ export class QuickSlotComponent {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.itemMap[i] = null;
|
this.itemMap[i] = null;
|
||||||
|
// Reset bg
|
||||||
|
bgGraphics.clear();
|
||||||
|
bgGraphics.fillStyle(0x1a1a1a, 0.8);
|
||||||
|
bgGraphics.fillRect(0, 0, 40, 40);
|
||||||
|
bgGraphics.lineStyle(1, 0x555555);
|
||||||
|
bgGraphics.strokeRect(0, 0, 40, 40);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user