diff --git a/src/common/rpg/components/stat.ts b/src/common/rpg/components/stat.ts index cedc723..478fb32 100644 --- a/src/common/rpg/components/stat.ts +++ b/src/common/rpg/components/stat.ts @@ -65,7 +65,10 @@ export class Stat extends Component { } @component -export class Health extends Stat { +export class Health extends Stat<{ damageTypes?: string[]; priority?: number }> { + get damageTypes(): readonly string[] { return this.state.damageTypes ?? []; } + get priority(): number { return this.state.priority ?? 0; } + @action kill() { this.set(0); diff --git a/src/common/rpg/systems/combat.ts b/src/common/rpg/systems/combat.ts index b8e05c6..34e31f3 100644 --- a/src/common/rpg/systems/combat.ts +++ b/src/common/rpg/systems/combat.ts @@ -18,8 +18,8 @@ export class CombatSystem extends System { let random: Random | undefined; for (const [target] of world.query(Attacked)) { - const health = target.get(Health); - if (!health) { + const healths = target.getAll(Health).sort((a, b) => b.priority - a.priority); + if (healths.length === 0) { console.warn(`[CombatSystem] Target ${target.id} has no Health component`); for (const attack of target.getAll(Attacked)) target.remove(attack); continue; @@ -116,14 +116,43 @@ export class CombatSystem extends System { if (damageSum === 0) continue; - const wasAlive = health.value > 0; - health.update(-damageSum); + let totalBefore = 0; + for (const pool of healths) totalBefore += pool.value; + + for (const hit of hitEvents) { + let remaining = hit.amount; + + if (hit.damageType) { + for (const pool of healths) { + if (remaining <= 0) break; + if (pool.value <= 0) continue; + if (!pool.damageTypes.includes(hit.damageType)) continue; + const take = Math.min(pool.value, remaining); + pool.update(-take); + remaining -= take; + } + } + + if (remaining > 0) { + for (const pool of healths) { + if (remaining <= 0) break; + if (pool.value <= 0) continue; + if (pool.damageTypes.length > 0) continue; + const take = Math.min(pool.value, remaining); + pool.update(-take); + remaining -= take; + } + } + } for (const info of hitEvents) { target.emit('Combat.hit', info); } - if (wasAlive && health.value <= 0 && lastHit) { + let totalAfter = 0; + for (const pool of healths) totalAfter += pool.value; + + if (totalBefore > 0 && totalAfter <= 0 && lastHit) { target.emit('Combat.killed', lastHit); } }