Another refactor
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
import type { World, EntityId, Action, SimEvent, Actor, CombatantActor, CollectibleActor, ActorType } from "../../core/types";
|
||||
|
||||
import { isBlocked } from "../world/world-logic";
|
||||
import { findPathAStar } from "../world/pathfinding";
|
||||
import { GAME_CONFIG } from "../../core/config/GameConfig";
|
||||
import { type EntityManager } from "../EntityManager";
|
||||
|
||||
|
||||
export function applyAction(w: World, actorId: EntityId, action: Action): SimEvent[] {
|
||||
export function applyAction(w: World, actorId: EntityId, action: Action, em?: EntityManager): SimEvent[] {
|
||||
const actor = w.actors.get(actorId);
|
||||
if (!actor) return [];
|
||||
|
||||
@@ -12,10 +14,10 @@ export function applyAction(w: World, actorId: EntityId, action: Action): SimEve
|
||||
|
||||
switch (action.type) {
|
||||
case "move":
|
||||
events.push(...handleMove(w, actor, action));
|
||||
events.push(...handleMove(w, actor, action, em));
|
||||
break;
|
||||
case "attack":
|
||||
events.push(...handleAttack(w, actor, action));
|
||||
events.push(...handleAttack(w, actor, action, em));
|
||||
break;
|
||||
case "wait":
|
||||
default:
|
||||
@@ -31,7 +33,7 @@ export function applyAction(w: World, actorId: EntityId, action: Action): SimEve
|
||||
return events;
|
||||
}
|
||||
|
||||
function handleExpCollection(w: World, player: Actor, events: SimEvent[]) {
|
||||
function handleExpCollection(w: World, player: Actor, events: SimEvent[], em?: EntityManager) {
|
||||
if (player.category !== "combatant") return;
|
||||
|
||||
const orbs = [...w.actors.values()].filter(a =>
|
||||
@@ -53,7 +55,8 @@ function handleExpCollection(w: World, player: Actor, events: SimEvent[]) {
|
||||
});
|
||||
|
||||
checkLevelUp(player, events);
|
||||
w.actors.delete(orb.id);
|
||||
if (em) em.removeActor(orb.id);
|
||||
else w.actors.delete(orb.id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,19 +89,23 @@ function checkLevelUp(player: CombatantActor, events: SimEvent[]) {
|
||||
}
|
||||
|
||||
|
||||
function handleMove(w: World, actor: Actor, action: { dx: number; dy: number }): SimEvent[] {
|
||||
function handleMove(w: World, actor: Actor, action: { dx: number; dy: number }, em?: EntityManager): SimEvent[] {
|
||||
const from = { ...actor.pos };
|
||||
const nx = actor.pos.x + action.dx;
|
||||
const ny = actor.pos.y + action.dy;
|
||||
|
||||
if (!isBlocked(w, nx, ny)) {
|
||||
actor.pos.x = nx;
|
||||
actor.pos.y = ny;
|
||||
if (!isBlocked(w, nx, ny, em)) {
|
||||
if (em) {
|
||||
em.moveActor(actor.id, from, { x: nx, y: ny });
|
||||
} else {
|
||||
actor.pos.x = nx;
|
||||
actor.pos.y = ny;
|
||||
}
|
||||
const to = { ...actor.pos };
|
||||
const events: SimEvent[] = [{ type: "moved", actorId: actor.id, from, to }];
|
||||
|
||||
if (actor.category === "combatant" && actor.isPlayer) {
|
||||
handleExpCollection(w, actor, events);
|
||||
handleExpCollection(w, actor, events, em);
|
||||
}
|
||||
|
||||
return events;
|
||||
@@ -108,7 +115,8 @@ function handleMove(w: World, actor: Actor, action: { dx: number; dy: number }):
|
||||
}
|
||||
|
||||
|
||||
function handleAttack(w: World, actor: Actor, action: { targetId: EntityId }): SimEvent[] {
|
||||
|
||||
function handleAttack(w: World, actor: Actor, action: { targetId: EntityId }, em?: EntityManager): SimEvent[] {
|
||||
const target = w.actors.get(action.targetId);
|
||||
if (target && target.category === "combatant" && actor.category === "combatant") {
|
||||
const events: SimEvent[] = [{ type: "attacked", attackerId: actor.id, targetId: action.targetId }];
|
||||
@@ -183,19 +191,25 @@ function handleAttack(w: World, actor: Actor, action: { targetId: EntityId }): S
|
||||
y: target.pos.y,
|
||||
victimType: target.type as ActorType
|
||||
});
|
||||
w.actors.delete(target.id);
|
||||
if (em) em.removeActor(target.id);
|
||||
else w.actors.delete(target.id);
|
||||
|
||||
// Spawn EXP Orb
|
||||
const enemyDef = (GAME_CONFIG.enemies as any)[target.type || ""];
|
||||
const expAmount = enemyDef?.expValue || 0;
|
||||
const orbId = Math.max(0, ...w.actors.keys(), target.id) + 1;
|
||||
w.actors.set(orbId, {
|
||||
const orbId = em ? em.getNextId() : Math.max(0, ...w.actors.keys(), target.id) + 1;
|
||||
|
||||
const orb: CollectibleActor = {
|
||||
id: orbId,
|
||||
category: "collectible",
|
||||
type: "exp_orb",
|
||||
pos: { ...target.pos },
|
||||
expAmount // Explicit member in CollectibleActor
|
||||
});
|
||||
expAmount
|
||||
};
|
||||
|
||||
if (em) em.addActor(orb);
|
||||
else w.actors.set(orbId, orb);
|
||||
|
||||
|
||||
events.push({ type: "orb-spawned", orbId, x: target.pos.x, y: target.pos.y });
|
||||
}
|
||||
@@ -210,7 +224,7 @@ function handleAttack(w: World, actor: Actor, action: { targetId: EntityId }): S
|
||||
* - if adjacent to player, attack
|
||||
* - else step toward player using greedy Manhattan
|
||||
*/
|
||||
export function decideEnemyAction(w: World, enemy: CombatantActor, player: CombatantActor): Action {
|
||||
export function decideEnemyAction(w: World, enemy: CombatantActor, player: CombatantActor, em?: EntityManager): Action {
|
||||
const dx = player.pos.x - enemy.pos.x;
|
||||
const dy = player.pos.y - enemy.pos.y;
|
||||
const dist = Math.abs(dx) + Math.abs(dy);
|
||||
@@ -219,7 +233,21 @@ export function decideEnemyAction(w: World, enemy: CombatantActor, player: Comba
|
||||
return { type: "attack", targetId: player.id };
|
||||
}
|
||||
|
||||
// Use A* for smarter pathfinding
|
||||
const dummySeen = new Uint8Array(w.width * w.height).fill(1); // Enemies "know" the map
|
||||
const path = findPathAStar(w, dummySeen, enemy.pos, player.pos, { ignoreBlockedTarget: true, ignoreSeen: true, em });
|
||||
|
||||
|
||||
if (path.length >= 2) {
|
||||
const next = path[1];
|
||||
const adx = next.x - enemy.pos.x;
|
||||
const ady = next.y - enemy.pos.y;
|
||||
return { type: "move", dx: adx, dy: ady };
|
||||
}
|
||||
|
||||
// Fallback to greedy if no path found
|
||||
const options: { dx: number; dy: number }[] = [];
|
||||
|
||||
if (Math.abs(dx) >= Math.abs(dy)) {
|
||||
options.push({ dx: Math.sign(dx), dy: 0 });
|
||||
options.push({ dx: 0, dy: Math.sign(dy) });
|
||||
@@ -243,7 +271,7 @@ export function decideEnemyAction(w: World, enemy: CombatantActor, player: Comba
|
||||
* Energy/speed scheduler: runs until it's the player's turn and the game needs input.
|
||||
* Returns enemy events accumulated along the way.
|
||||
*/
|
||||
export function stepUntilPlayerTurn(w: World, playerId: EntityId): { awaitingPlayerId: EntityId; events: SimEvent[] } {
|
||||
export function stepUntilPlayerTurn(w: World, playerId: EntityId, em?: EntityManager): { awaitingPlayerId: EntityId; events: SimEvent[] } {
|
||||
const player = w.actors.get(playerId) as CombatantActor;
|
||||
if (!player || player.category !== "combatant") throw new Error("Player missing or invalid");
|
||||
|
||||
@@ -269,8 +297,8 @@ export function stepUntilPlayerTurn(w: World, playerId: EntityId): { awaitingPla
|
||||
return { awaitingPlayerId: actor.id, events };
|
||||
}
|
||||
|
||||
const action = decideEnemyAction(w, actor, player);
|
||||
events.push(...applyAction(w, actor.id, action));
|
||||
const action = decideEnemyAction(w, actor, player, em);
|
||||
events.push(...applyAction(w, actor.id, action, em));
|
||||
|
||||
// Check if player was killed by this action
|
||||
if (!w.actors.has(playerId)) {
|
||||
|
||||
Reference in New Issue
Block a user