161 lines
4.2 KiB
TypeScript
161 lines
4.2 KiB
TypeScript
import Phaser from "phaser";
|
|
import type { World, CombatantActor, Item, Vec2, EntityId } from "../../core/types";
|
|
import { TILE_SIZE } from "../../core/constants";
|
|
import { GAME_CONFIG } from "../../core/config/GameConfig";
|
|
import { traceProjectile, getClosestVisibleEnemy } from "../../engine/gameplay/CombatLogic";
|
|
import type { EntityManager } from "../../engine/EntityManager";
|
|
|
|
/**
|
|
* Manages targeting mode for thrown items.
|
|
* Extracted from GameScene to isolate targeting logic and reduce complexity.
|
|
*/
|
|
export class TargetingSystem {
|
|
private graphics: Phaser.GameObjects.Graphics;
|
|
private active: boolean = false;
|
|
private targetingItemId: string | null = null;
|
|
private cursor: Vec2 | null = null;
|
|
|
|
constructor(graphics: Phaser.GameObjects.Graphics) {
|
|
this.graphics = graphics;
|
|
}
|
|
|
|
/**
|
|
* Start targeting mode for a throwable item
|
|
*/
|
|
startTargeting(
|
|
itemId: string,
|
|
playerPos: Vec2,
|
|
world: World,
|
|
seenArray: Uint8Array,
|
|
worldWidth: number
|
|
): void {
|
|
this.targetingItemId = itemId;
|
|
this.active = true;
|
|
|
|
// Auto-target closest visible enemy
|
|
const closest = getClosestVisibleEnemy(world, playerPos, seenArray, worldWidth);
|
|
|
|
if (closest) {
|
|
this.cursor = closest;
|
|
} else {
|
|
this.cursor = null;
|
|
}
|
|
|
|
this.drawLine(playerPos);
|
|
console.log("Targeting Mode: ON");
|
|
}
|
|
|
|
/**
|
|
* Update the targeting cursor position
|
|
*/
|
|
updateCursor(worldPos: Vec2, playerPos: Vec2): void {
|
|
if (!this.active) return;
|
|
|
|
this.cursor = { x: worldPos.x, y: worldPos.y };
|
|
this.drawLine(playerPos);
|
|
}
|
|
|
|
/**
|
|
* Execute the throw action
|
|
*/
|
|
executeThrow(
|
|
world: World,
|
|
playerId: EntityId,
|
|
entityManager: EntityManager,
|
|
onProjectileComplete: (blockedPos: Vec2, hitActorId: EntityId | undefined, item: Item) => void
|
|
): boolean {
|
|
if (!this.active || !this.targetingItemId || !this.cursor) {
|
|
return false;
|
|
}
|
|
|
|
const player = world.actors.get(playerId) as CombatantActor;
|
|
if (!player || !player.inventory) return false;
|
|
|
|
const itemIdx = player.inventory.items.findIndex(it => it.id === this.targetingItemId);
|
|
if (itemIdx === -1) {
|
|
console.log("Item not found!");
|
|
this.cancel();
|
|
return false;
|
|
}
|
|
|
|
const item = player.inventory.items[itemIdx];
|
|
// Remove item from inventory before throw
|
|
player.inventory.items.splice(itemIdx, 1);
|
|
|
|
const start = player.pos;
|
|
const end = { x: this.cursor.x, y: this.cursor.y };
|
|
|
|
const result = traceProjectile(world, start, end, entityManager, playerId);
|
|
const { blockedPos, hitActorId } = result;
|
|
|
|
// Call the callback with throw results
|
|
onProjectileComplete(blockedPos, hitActorId, item);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Cancel targeting mode
|
|
*/
|
|
cancel(): void {
|
|
this.active = false;
|
|
this.targetingItemId = null;
|
|
this.cursor = null;
|
|
this.graphics.clear();
|
|
console.log("Targeting cancelled");
|
|
}
|
|
|
|
/**
|
|
* Check if targeting is currently active
|
|
*/
|
|
get isActive(): boolean {
|
|
return this.active;
|
|
}
|
|
|
|
/**
|
|
* Get the ID of the item being targeted
|
|
*/
|
|
get itemId(): string | null {
|
|
return this.targetingItemId;
|
|
}
|
|
|
|
/**
|
|
* Get the current cursor position
|
|
*/
|
|
get cursorPos(): Vec2 | null {
|
|
return this.cursor;
|
|
}
|
|
|
|
/**
|
|
* Draw targeting line from player to cursor
|
|
*/
|
|
private drawLine(playerPos: Vec2): void {
|
|
if (!this.cursor) {
|
|
this.graphics.clear();
|
|
return;
|
|
}
|
|
|
|
this.graphics.clear();
|
|
|
|
const startX = playerPos.x * TILE_SIZE + TILE_SIZE / 2;
|
|
const startY = playerPos.y * TILE_SIZE + TILE_SIZE / 2;
|
|
|
|
const endX = this.cursor.x * TILE_SIZE + TILE_SIZE / 2;
|
|
const endY = this.cursor.y * TILE_SIZE + TILE_SIZE / 2;
|
|
|
|
this.graphics.lineStyle(
|
|
GAME_CONFIG.ui.targetingLineWidth,
|
|
GAME_CONFIG.ui.targetingLineColor,
|
|
GAME_CONFIG.ui.targetingLineAlpha
|
|
);
|
|
this.graphics.lineBetween(startX, startY, endX, endY);
|
|
|
|
this.graphics.strokeRect(
|
|
this.cursor.x * TILE_SIZE,
|
|
this.cursor.y * TILE_SIZE,
|
|
TILE_SIZE,
|
|
TILE_SIZE
|
|
);
|
|
}
|
|
}
|