Initial commit
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
/**
|
||||
* Rest logic.
|
||||
*
|
||||
* Short Rest (Krótki Odpoczynek):
|
||||
* - Restore HP equal to actor.body.value (capped at max).
|
||||
* - Restore mana to max-per-spell? — no: short rest does NOT restore mana.
|
||||
* - Holders of `Nadzwyczajna Odporność` (alchemy passive) restore 1 elixir tolerance.
|
||||
*
|
||||
* Long Rest (Długi Odpoczynek):
|
||||
* - Restore HP, mana, zeal to max.
|
||||
* - Restore 1 elixir tolerance (or all, if `Nadzwyczajna Odporność`? — book says
|
||||
* elixir tolerance recovers per long rest by default; tracked here as -1).
|
||||
* - Reset blood pool to max (assumption — refine when AS spell list lands).
|
||||
* - Clear runic counter on magical armor.
|
||||
*
|
||||
* Hooks `hbm.beforeRest` and `hbm.afterRest` fire for module integration.
|
||||
*/
|
||||
|
||||
import type { CastableActor } from './spell-cast';
|
||||
import { CONDITIONS } from './conditions';
|
||||
|
||||
export type RestKind = 'breather' | 'short' | 'long';
|
||||
|
||||
declare const Hooks: any;
|
||||
declare const Roll: any;
|
||||
|
||||
interface ActorWithItems extends CastableActor {
|
||||
items?: Iterable<{ type: string; system?: { slug?: string }; flags?: any }>;
|
||||
}
|
||||
|
||||
function hasExtraordinaryResilience(actor: ActorWithItems): boolean {
|
||||
if (!actor.items) return false;
|
||||
for (const it of actor.items) {
|
||||
const slug = (it as any).system?.slug ?? (it as any).flags?.['hbm-rpg-v3']?.slug ?? '';
|
||||
if (slug === 'extraordinary-resilience' || slug === 'nadzwyczajna-odpornosc') return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export interface RestResult {
|
||||
kind: RestKind;
|
||||
hpRestored: number;
|
||||
manaRestored: number;
|
||||
zealRestored: number;
|
||||
bloodRestored: number;
|
||||
toleranceRecovered: number;
|
||||
effectsCleared: number;
|
||||
}
|
||||
|
||||
export async function rest(actor: CastableActor, kind: RestKind): Promise<RestResult> {
|
||||
Hooks.callAll('hbm.beforeRest', actor, kind);
|
||||
const a = actor.system.attributes as any;
|
||||
const update: Record<string, unknown> = {};
|
||||
|
||||
const hpBefore = a.health?.value ?? 0;
|
||||
const manaBefore = a.mana?.value ?? 0;
|
||||
const zealBefore = a.zeal?.value ?? 0;
|
||||
const bloodBefore = a.blood?.value ?? 0;
|
||||
const toleranceBefore = a.elixirTolerance ?? 0;
|
||||
|
||||
let hpRestored = 0;
|
||||
let manaRestored = 0;
|
||||
let zealRestored = 0;
|
||||
let bloodRestored = 0;
|
||||
let toleranceRecovered = 0;
|
||||
|
||||
if (kind === 'breather') {
|
||||
if (a.mana?.max != null && manaBefore < a.mana.max) {
|
||||
update['system.attributes.mana.value'] = a.mana.max;
|
||||
manaRestored = a.mana.max - manaBefore;
|
||||
}
|
||||
const endurance = actor.system.skills?.endurance?.value ?? 0;
|
||||
const roll = new Roll('1d6 + @endurance', { endurance });
|
||||
await roll.evaluate();
|
||||
const rollTotal = roll.total;
|
||||
const heal = Math.min(rollTotal, (a.health?.max ?? 0) - hpBefore);
|
||||
if (heal > 0) {
|
||||
update['system.attributes.health.value'] = hpBefore + heal;
|
||||
hpRestored = heal;
|
||||
}
|
||||
} else if (kind === 'short') {
|
||||
if (a.mana?.max != null && manaBefore < a.mana.max) {
|
||||
update['system.attributes.mana.value'] = a.mana.max;
|
||||
manaRestored = a.mana.max - manaBefore;
|
||||
}
|
||||
const healAmount = Math.ceil((a.health?.max ?? 0) / 3);
|
||||
const heal = Math.min(healAmount, (a.health?.max ?? 0) - hpBefore);
|
||||
if (heal > 0) {
|
||||
update['system.attributes.health.value'] = hpBefore + heal;
|
||||
hpRestored = heal;
|
||||
}
|
||||
if (hasExtraordinaryResilience(actor as ActorWithItems) && toleranceBefore > 0) {
|
||||
update['system.attributes.elixirTolerance'] = toleranceBefore - 1;
|
||||
toleranceRecovered = 1;
|
||||
}
|
||||
} else {
|
||||
if (a.health?.max != null && hpBefore < a.health.max) {
|
||||
update['system.attributes.health.value'] = a.health.max;
|
||||
hpRestored = a.health.max - hpBefore;
|
||||
}
|
||||
if (a.mana?.max != null && manaBefore < a.mana.max) {
|
||||
update['system.attributes.mana.value'] = a.mana.max;
|
||||
manaRestored = a.mana.max - manaBefore;
|
||||
}
|
||||
if (a.zeal?.max != null && zealBefore < a.zeal.max) {
|
||||
update['system.attributes.zeal.value'] = a.zeal.max;
|
||||
zealRestored = a.zeal.max - zealBefore;
|
||||
}
|
||||
if (a.blood?.max != null && bloodBefore < a.blood.max) {
|
||||
update['system.attributes.blood.value'] = a.blood.max;
|
||||
bloodRestored = a.blood.max - bloodBefore;
|
||||
}
|
||||
if (toleranceBefore > 0) {
|
||||
update['system.attributes.elixirTolerance'] = Math.max(0, toleranceBefore - 1);
|
||||
toleranceRecovered = 1;
|
||||
}
|
||||
if (a.magicalArmor?.runicCounter && a.magicalArmor.runicCounter > 0) {
|
||||
update['system.attributes.magicalArmor.runicCounter'] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(update).length > 0) await actor.update(update);
|
||||
|
||||
let effectsCleared = 0;
|
||||
const actorAny = actor as unknown as { effects?: any; deleteEmbeddedDocuments?: (t: string, ids: string[]) => Promise<unknown> };
|
||||
const ids: string[] = [];
|
||||
|
||||
if (kind === 'short') {
|
||||
for (const ef of actorAny.effects ?? []) {
|
||||
const isUnconscious = ef.statuses?.has('nieprzytomny') || ef.flags?.core?.statusId === 'nieprzytomny' || ef.id === 'nieprzytomny';
|
||||
if (isUnconscious) ids.push(ef.id);
|
||||
}
|
||||
} else if (kind === 'long') {
|
||||
const conditionIds = new Set(CONDITIONS.map(c => c.id));
|
||||
for (const ef of actorAny.effects ?? []) {
|
||||
const isState = conditionIds.has(ef.id) ||
|
||||
(ef.statuses && [...ef.statuses].some(s => conditionIds.has(s))) ||
|
||||
conditionIds.has(ef.flags?.core?.statusId);
|
||||
|
||||
if (isState) {
|
||||
ids.push(ef.id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ef.origin) continue;
|
||||
const dur = ef.duration ?? {};
|
||||
const isTemporary = (dur.seconds ?? 0) > 0 || (dur.rounds ?? 0) > 0 || (dur.turns ?? 0) > 0 || ef.flags?.['hbm-rpg-v3']?.untilLongRest === true;
|
||||
if (isTemporary) ids.push(ef.id);
|
||||
}
|
||||
}
|
||||
|
||||
if (ids.length > 0 && actorAny.deleteEmbeddedDocuments) {
|
||||
await actorAny.deleteEmbeddedDocuments('ActiveEffect', ids);
|
||||
effectsCleared = ids.length;
|
||||
}
|
||||
|
||||
const result: RestResult = { kind, hpRestored, manaRestored, zealRestored, bloodRestored, toleranceRecovered, effectsCleared };
|
||||
Hooks.callAll('hbm.afterRest', actor, result);
|
||||
return result;
|
||||
}
|
||||
Reference in New Issue
Block a user