Files
rogue/src/engine/gameplay/CombatLogic.ts

97 lines
2.8 KiB
TypeScript

import { type World, type Vec2, type EntityId } from "../../core/types";
import { isBlocked } from "../world/world-logic";
import { raycast } from "../../core/math";
import { EntityManager } from "../EntityManager";
export interface ProjectileResult {
path: Vec2[];
blockedPos: Vec2;
hitActorId?: EntityId;
}
/**
* Calculates the path and impact of a projectile.
*/
export function traceProjectile(
world: World,
start: Vec2,
target: Vec2,
entityManager: EntityManager,
shooterId?: EntityId
): ProjectileResult {
const points = raycast(start.x, start.y, target.x, target.y);
let blockedPos = target;
let hitActorId: EntityId | undefined;
// Iterate points (skip start)
for (let i = 1; i < points.length; i++) {
const p = points[i];
// Check for blocking
if (isBlocked(world, p.x, p.y, entityManager)) {
// Check if we hit a combatant
const actors = entityManager.getActorsAt(p.x, p.y);
const enemy = actors.find(a => a.category === "combatant" && a.id !== shooterId);
if (enemy) {
hitActorId = enemy.id;
blockedPos = p;
} else {
// Hit wall or other obstacle
blockedPos = p;
}
break;
}
blockedPos = p;
}
return {
path: points,
blockedPos,
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;
}