216 lines
6.4 KiB
JavaScript
216 lines
6.4 KiB
JavaScript
/**
|
||
* Donjon & Cie - Systeme FoundryVTT
|
||
*
|
||
* Donjon & Cie est un jeu de role edite par John Doe.
|
||
* Ce systeme FoundryVTT est une implementation independante et n'est pas
|
||
* affilie a John Doe.
|
||
*
|
||
* @author LeRatierBretonnien
|
||
* @copyright 2025–2026 LeRatierBretonnien
|
||
* @license CC BY-NC-SA 4.0 – https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||
*/
|
||
|
||
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 || ""
|
||
}
|
||
});
|
||
}
|
||
}
|