Added flamethrower with buring effects
This commit is contained in:
@@ -36,22 +36,22 @@ function handleExpCollection(player: Actor, events: SimEvent[], accessor: Entity
|
||||
if (player.category !== "combatant") return;
|
||||
|
||||
const actorsAtPos = accessor.getActorsAt(player.pos.x, player.pos.y);
|
||||
const orbs = actorsAtPos.filter(a =>
|
||||
a.category === "collectible" &&
|
||||
const orbs = actorsAtPos.filter(a =>
|
||||
a.category === "collectible" &&
|
||||
a.type === "exp_orb"
|
||||
) as CollectibleActor[];
|
||||
|
||||
|
||||
for (const orb of orbs) {
|
||||
const amount = orb.expAmount || 0;
|
||||
player.stats.exp += amount;
|
||||
events.push({
|
||||
type: "exp-collected",
|
||||
actorId: player.id,
|
||||
amount,
|
||||
x: player.pos.x,
|
||||
y: player.pos.y
|
||||
events.push({
|
||||
type: "exp-collected",
|
||||
actorId: player.id,
|
||||
amount,
|
||||
x: player.pos.x,
|
||||
y: player.pos.y
|
||||
});
|
||||
|
||||
|
||||
checkLevelUp(player, events);
|
||||
accessor.removeActor(orb.id);
|
||||
}
|
||||
@@ -59,11 +59,11 @@ function handleExpCollection(player: Actor, events: SimEvent[], accessor: Entity
|
||||
|
||||
function checkLevelUp(player: CombatantActor, events: SimEvent[]) {
|
||||
const s = player.stats;
|
||||
|
||||
|
||||
while (s.exp >= s.expToNextLevel) {
|
||||
s.level++;
|
||||
s.exp -= s.expToNextLevel;
|
||||
|
||||
|
||||
// Growth
|
||||
s.maxHp += GAME_CONFIG.leveling.hpGainPerLevel;
|
||||
s.hp = s.maxHp; // Heal on level up
|
||||
@@ -73,16 +73,16 @@ function checkLevelUp(player: CombatantActor, events: SimEvent[]) {
|
||||
|
||||
s.statPoints += GAME_CONFIG.leveling.statPointsPerLevel;
|
||||
s.skillPoints += GAME_CONFIG.leveling.skillPointsPerLevel;
|
||||
|
||||
|
||||
// Scale requirement
|
||||
s.expToNextLevel = Math.floor(s.expToNextLevel * GAME_CONFIG.leveling.expMultiplier);
|
||||
|
||||
events.push({
|
||||
type: "leveled-up",
|
||||
actorId: player.id,
|
||||
level: s.level,
|
||||
x: player.pos.x,
|
||||
y: player.pos.y
|
||||
|
||||
events.push({
|
||||
type: "leveled-up",
|
||||
actorId: player.id,
|
||||
level: s.level,
|
||||
x: player.pos.x,
|
||||
y: player.pos.y
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -98,7 +98,7 @@ function handleMove(w: World, actor: Actor, action: { dx: number; dy: number },
|
||||
actor.pos.y = ny;
|
||||
const to = { ...actor.pos };
|
||||
const events: SimEvent[] = [{ type: "moved", actorId: actor.id, from, to }];
|
||||
|
||||
|
||||
const tileIdx = ny * w.width + nx;
|
||||
if (isDestructibleByWalk(w.tiles[tileIdx])) {
|
||||
tryDestructTile(w, nx, ny);
|
||||
@@ -109,7 +109,7 @@ function handleMove(w: World, actor: Actor, action: { dx: number; dy: number },
|
||||
}
|
||||
return events;
|
||||
}
|
||||
|
||||
|
||||
return [{ type: "move-blocked", actorId: actor.id, x: nx, y: ny }];
|
||||
}
|
||||
|
||||
@@ -119,14 +119,14 @@ function handleAttack(_w: World, actor: Actor, action: { targetId: EntityId }, a
|
||||
const target = accessor.getActor(action.targetId);
|
||||
if (target && target.category === "combatant" && actor.category === "combatant") {
|
||||
const events: SimEvent[] = [{ type: "attacked", attackerId: actor.id, targetId: action.targetId }];
|
||||
|
||||
|
||||
// 1. Calculate Damage
|
||||
const result = calculateDamage(actor.stats, target.stats);
|
||||
|
||||
|
||||
if (!result.hit) {
|
||||
events.push({
|
||||
type: "dodged",
|
||||
targetId: action.targetId,
|
||||
events.push({
|
||||
type: "dodged",
|
||||
targetId: action.targetId,
|
||||
x: target.pos.x,
|
||||
y: target.pos.y
|
||||
});
|
||||
@@ -138,13 +138,13 @@ function handleAttack(_w: World, actor: Actor, action: { targetId: EntityId }, a
|
||||
const isBlock = result.isBlock;
|
||||
|
||||
target.stats.hp -= dmg;
|
||||
|
||||
|
||||
if (target.stats.hp > 0 && target.category === "combatant" && !target.isPlayer) {
|
||||
target.aiState = "pursuing";
|
||||
target.alertedAt = Date.now();
|
||||
if (actor.pos) {
|
||||
target.lastKnownPlayerPos = { ...actor.pos };
|
||||
}
|
||||
target.aiState = "pursuing";
|
||||
target.alertedAt = Date.now();
|
||||
if (actor.pos) {
|
||||
target.lastKnownPlayerPos = { ...actor.pos };
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Lifesteal Logic
|
||||
@@ -153,19 +153,19 @@ function handleAttack(_w: World, actor: Actor, action: { targetId: EntityId }, a
|
||||
if (healAmount > 0) {
|
||||
actor.stats.hp = Math.min(actor.stats.maxHp, actor.stats.hp + healAmount);
|
||||
events.push({
|
||||
type: "healed",
|
||||
actorId: actor.id,
|
||||
amount: healAmount,
|
||||
x: actor.pos.x,
|
||||
y: actor.pos.y
|
||||
type: "healed",
|
||||
actorId: actor.id,
|
||||
amount: healAmount,
|
||||
x: actor.pos.x,
|
||||
y: actor.pos.y
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
events.push({
|
||||
type: "damaged",
|
||||
targetId: action.targetId,
|
||||
amount: dmg,
|
||||
events.push({
|
||||
type: "damaged",
|
||||
targetId: action.targetId,
|
||||
amount: dmg,
|
||||
hp: target.stats.hp,
|
||||
x: target.pos.x,
|
||||
y: target.pos.y,
|
||||
@@ -174,25 +174,37 @@ function handleAttack(_w: World, actor: Actor, action: { targetId: EntityId }, a
|
||||
});
|
||||
|
||||
if (target.stats.hp <= 0) {
|
||||
events.push({
|
||||
type: "killed",
|
||||
targetId: target.id,
|
||||
killerId: actor.id,
|
||||
x: target.pos.x,
|
||||
y: target.pos.y,
|
||||
events.push({
|
||||
type: "killed",
|
||||
targetId: target.id,
|
||||
killerId: actor.id,
|
||||
x: target.pos.x,
|
||||
y: target.pos.y,
|
||||
victimType: target.type as ActorType
|
||||
});
|
||||
|
||||
|
||||
accessor.removeActor(target.id);
|
||||
|
||||
// Extinguish fire at the death position
|
||||
const ecsWorld = accessor.context;
|
||||
if (ecsWorld) {
|
||||
const firesAtPos = ecsWorld.getEntitiesWith("position", "name").filter(id => {
|
||||
const p = ecsWorld.getComponent(id, "position");
|
||||
const n = ecsWorld.getComponent(id, "name");
|
||||
return p?.x === target.pos.x && p?.y === target.pos.y && n?.name === "Fire";
|
||||
});
|
||||
for (const fireId of firesAtPos) {
|
||||
ecsWorld.destroyEntity(fireId);
|
||||
}
|
||||
}
|
||||
|
||||
// Spawn EXP Orb
|
||||
const enemyDef = (GAME_CONFIG.enemies as any)[target.type || ""];
|
||||
const expAmount = enemyDef?.expValue || 0;
|
||||
|
||||
const ecsWorld = accessor.context;
|
||||
|
||||
if (ecsWorld) {
|
||||
const orbId = Prefabs.expOrb(ecsWorld, target.pos.x, target.pos.y, expAmount);
|
||||
events.push({ type: "orb-spawned", orbId, x: target.pos.x, y: target.pos.y });
|
||||
const orbId = Prefabs.expOrb(ecsWorld, target.pos.x, target.pos.y, expAmount);
|
||||
events.push({ type: "orb-spawned", orbId, x: target.pos.x, y: target.pos.y });
|
||||
}
|
||||
}
|
||||
return events;
|
||||
@@ -211,17 +223,17 @@ function handleAttack(_w: World, actor: Actor, action: { targetId: EntityId }, a
|
||||
export function decideEnemyAction(w: World, enemy: CombatantActor, player: CombatantActor, accessor: EntityAccessor): { action: Action; justAlerted: boolean } {
|
||||
const ecsWorld = accessor.context;
|
||||
if (ecsWorld) {
|
||||
const aiSystem = new AISystem(ecsWorld, w, accessor);
|
||||
const result = aiSystem.update(enemy.id, player.id);
|
||||
|
||||
const aiComp = ecsWorld.getComponent(enemy.id, "ai");
|
||||
if (aiComp) {
|
||||
enemy.aiState = aiComp.state;
|
||||
enemy.alertedAt = aiComp.alertedAt;
|
||||
enemy.lastKnownPlayerPos = aiComp.lastKnownPlayerPos;
|
||||
}
|
||||
|
||||
return result;
|
||||
const aiSystem = new AISystem(ecsWorld, w, accessor);
|
||||
const result = aiSystem.update(enemy.id, player.id);
|
||||
|
||||
const aiComp = ecsWorld.getComponent(enemy.id, "ai");
|
||||
if (aiComp) {
|
||||
enemy.aiState = aiComp.state;
|
||||
enemy.alertedAt = aiComp.alertedAt;
|
||||
enemy.lastKnownPlayerPos = aiComp.lastKnownPlayerPos;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return { action: { type: "wait" }, justAlerted: false };
|
||||
@@ -240,51 +252,51 @@ export function stepUntilPlayerTurn(w: World, playerId: EntityId, accessor: Enti
|
||||
const events: SimEvent[] = [];
|
||||
|
||||
if (player.energy >= THRESHOLD) {
|
||||
player.energy -= THRESHOLD;
|
||||
player.energy -= THRESHOLD;
|
||||
}
|
||||
|
||||
|
||||
while (true) {
|
||||
if (player.energy >= THRESHOLD) {
|
||||
return { awaitingPlayerId: playerId, events };
|
||||
return { awaitingPlayerId: playerId, events };
|
||||
}
|
||||
|
||||
const actors = [...accessor.getAllActors()];
|
||||
for (const actor of actors) {
|
||||
if (actor.category === "combatant") {
|
||||
actor.energy += actor.speed;
|
||||
}
|
||||
if (actor.category === "combatant") {
|
||||
actor.energy += actor.speed;
|
||||
}
|
||||
}
|
||||
|
||||
let actionsTaken = 0;
|
||||
while (true) {
|
||||
const eligibleActors = accessor.getEnemies().filter(a => a.energy >= THRESHOLD);
|
||||
|
||||
if (eligibleActors.length === 0) break;
|
||||
|
||||
eligibleActors.sort((a, b) => b.energy - a.energy);
|
||||
const actor = eligibleActors[0];
|
||||
|
||||
actor.energy -= THRESHOLD;
|
||||
|
||||
const decision = decideEnemyAction(w, actor, player, accessor);
|
||||
|
||||
if (decision.justAlerted) {
|
||||
events.push({
|
||||
type: "enemy-alerted",
|
||||
enemyId: actor.id,
|
||||
x: actor.pos.x,
|
||||
y: actor.pos.y
|
||||
});
|
||||
}
|
||||
|
||||
events.push(...applyAction(w, actor.id, decision.action, accessor));
|
||||
const eligibleActors = accessor.getEnemies().filter(a => a.energy >= THRESHOLD);
|
||||
|
||||
if (!accessor.isPlayerAlive()) {
|
||||
return { awaitingPlayerId: null as any, events };
|
||||
}
|
||||
|
||||
actionsTaken++;
|
||||
if (actionsTaken > 1000) break;
|
||||
if (eligibleActors.length === 0) break;
|
||||
|
||||
eligibleActors.sort((a, b) => b.energy - a.energy);
|
||||
const actor = eligibleActors[0];
|
||||
|
||||
actor.energy -= THRESHOLD;
|
||||
|
||||
const decision = decideEnemyAction(w, actor, player, accessor);
|
||||
|
||||
if (decision.justAlerted) {
|
||||
events.push({
|
||||
type: "enemy-alerted",
|
||||
enemyId: actor.id,
|
||||
x: actor.pos.x,
|
||||
y: actor.pos.y
|
||||
});
|
||||
}
|
||||
|
||||
events.push(...applyAction(w, actor.id, decision.action, accessor));
|
||||
|
||||
if (!accessor.isPlayerAlive()) {
|
||||
return { awaitingPlayerId: null as any, events };
|
||||
}
|
||||
|
||||
actionsTaken++;
|
||||
if (actionsTaken > 1000) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user