Files
fvtt-donjon-et-cie/modules/donjon-et-cie-actor.mjs

204 lines
6.1 KiB
JavaScript

import { DonjonEtCieUtility } from "./donjon-et-cie-utility.mjs";
import { DonjonEtCieRollDialog } from "./applications/donjon-et-cie-roll-dialog.mjs";
export class DonjonEtCieActor extends Actor {
prepareDerivedData() {
super.prepareDerivedData();
const pv = this.system.sante?.pv;
if (pv && pv.value > pv.max) {
pv.max = pv.value;
}
}
getCharacteristicEntries() {
return DonjonEtCieUtility.getCharacteristicEntries(this.system);
}
getSectionData() {
return DonjonEtCieUtility.buildActorSections(this);
}
getFavorEntries() {
return DonjonEtCieUtility.getFavorEntries(this.system);
}
#getStoredArmorContext() {
if (this.type === "pnj") {
const stored = Number(this.system.defense?.armure?.resultatProtection ?? 0);
return {
label: "ARM",
hasArmor: true,
before: stored,
update: async (value) => this.update({ "system.defense.armure.resultatProtection": Math.max(0, Number(value ?? 0)) })
};
}
const armors = [...this.items.filter((item) => item.type === "armure")].sort((a, b) => {
const equippedScore = Number(Boolean(b.system.equipee)) - Number(Boolean(a.system.equipee));
if (equippedScore) return equippedScore;
const protectionScore = Number(b.system.resultatProtection ?? 0) - Number(a.system.resultatProtection ?? 0);
if (protectionScore) return protectionScore;
return a.name.localeCompare(b.name, "fr", { sensitivity: "base" });
});
const armor = armors.find((item) => item.system.equipee || Number(item.system.resultatProtection ?? 0) > 0) ?? null;
if (!armor) {
return {
label: "Armure",
hasArmor: false,
before: 0,
update: null
};
}
return {
label: armor.name,
hasArmor: true,
before: Number(armor.system.resultatProtection ?? 0),
update: async (value) => armor.update({ "system.resultatProtection": Math.max(0, Number(value ?? 0)) })
};
}
async adjustNumericField(path, delta) {
const current = Number(foundry.utils.getProperty(this, path) ?? 0);
let next = current + Number(delta);
if (path === "system.sante.pv.value") {
const max = Number(this.system.sante?.pv?.max ?? next);
next = Math.max(0, Math.min(next, max));
} else {
next = Math.max(0, next);
}
return this.update({ [path]: next });
}
async applyIncomingDamage(damage, { useArmor = false } = {}) {
const incoming = Math.max(0, Number(damage ?? 0));
const pvBefore = Number(this.system.sante?.pv?.value ?? 0);
const pvMax = Number(this.system.sante?.pv?.max ?? pvBefore);
const armor = this.#getStoredArmorContext();
const armorBefore = useArmor ? Number(armor.before ?? 0) : 0;
const armorAbsorbed = Math.min(incoming, armorBefore);
const armorAfter = Math.max(armorBefore - armorAbsorbed, 0);
const hpDamage = Math.max(incoming - armorAbsorbed, 0);
const pvAfter = Math.max(pvBefore - hpDamage, 0);
if (useArmor && armor.hasArmor && armor.update && armorAfter !== armorBefore) {
await armor.update(armorAfter);
}
if (hpDamage !== 0) {
await this.update({ "system.sante.pv.value": pvAfter });
}
return {
incoming,
useArmor,
armorLabel: armor.label,
armorAvailable: armor.hasArmor,
armorBefore,
armorAbsorbed,
armorAfter,
hpDamage,
pvBefore,
pvAfter,
pvMax
};
}
async rollCharacteristic(key) {
return DonjonEtCieRollDialog.createCharacteristic(this, key);
}
async useFavorService(departmentKey) {
return game.system.donjonEtCie.rolls.useFavorService(this, departmentKey);
}
async rollInitiative() {
return DonjonEtCieRollDialog.createInitiative(this);
}
async rollHitDice() {
return game.system.donjonEtCie.rolls.rollHitDice(this);
}
async rollWeapon(itemId) {
const item = this.items.get(itemId);
if (item) return DonjonEtCieRollDialog.createWeapon(this, item);
}
async rollDamage(itemId) {
const item = this.items.get(itemId);
if (item) return DonjonEtCieRollDialog.createDamage(this, item);
}
async rollSpell(itemId) {
const item = this.items.get(itemId);
if (item) return DonjonEtCieRollDialog.createSpell(this, item);
}
async rollUsage(itemId) {
const item = this.items.get(itemId);
if (item) return DonjonEtCieRollDialog.createUsage(item);
}
#createPnjResourceProxy({ label, deltaPath, protectionPath = null }) {
const delta = Number(foundry.utils.getProperty(this, deltaPath) ?? 0);
const protection = protectionPath ? Number(foundry.utils.getProperty(this, protectionPath) ?? 0) : 0;
return {
actor: this,
type: protectionPath ? "armure" : "ressource",
name: `${this.name} · ${label}`,
system: {
delta,
resultatProtection: protection
},
update: async (data) => {
const updateData = {};
if (Object.hasOwn(data, "system.delta")) {
updateData[deltaPath] = data["system.delta"];
}
if (protectionPath && Object.hasOwn(data, "system.resultatProtection")) {
updateData[protectionPath] = data["system.resultatProtection"];
}
return Object.keys(updateData).length ? this.update(updateData) : this;
}
};
}
async rollPnjArmor() {
return DonjonEtCieRollDialog.createUsage(this.#createPnjResourceProxy({
label: "ARM",
deltaPath: "system.defense.armure.delta",
protectionPath: "system.defense.armure.resultatProtection"
}));
}
async rollPnjCourage() {
return DonjonEtCieRollDialog.createUsage(this.#createPnjResourceProxy({
label: "COU",
deltaPath: "system.defense.courage.delta"
}));
}
async rollPnjAttackDamage() {
const attackName = this.system.attaque?.nom || "Attaque";
const attackDamage = this.system.attaque?.degats || "";
if (!attackDamage) return null;
return DonjonEtCieRollDialog.createDamage(this, {
name: `${this.name} · ${attackName}`,
type: "attaque",
system: {
degats: attackDamage,
portee: this.system.attaque?.notes || ""
}
});
}
}