Files
fvtt-chroniques-de-l-etrange/src/data/actors/character.js
T
uberwald 75f79c1c08 feat(magic): reorder schools, fix Wu Xing aspect & power formula
- `magicOrder` ArrayField + ▲/▼ buttons for manual reordering
- Magic rolls use school's aspect for Wu Xing, not speciality's element
- Spell power: `difficulty × (aspectValue + freePowerLevels)` (not `successes × diff`)
- Prompt replaces `aspectspeciality`/`bonusmalusspeciality`/`heispend` with `freepowerlevels`

fix: code review issues
- combat.js: guard undefined `ids` in rollInitiative
- rolling.js: catch Dice So Nice promise, normalize French→English kungfu aspects
- weapon/armor/ingredient: `{ min: 0 }` on quantity
- character.js/npc.js: catch rollForActor fire-and-forget promises
- roll-actions.js/tinji-app.js: await ChatMessage.create
- sanhei.js: null guard on properties
- spell.js/kungfu.js: fix aspect name comments (French→English)
2026-06-10 15:54:31 +02:00

199 lines
7.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Chroniques de l'Étrange — Système FoundryVTT
*
* Chroniques de l'Étrange est un jeu de rôle édité par Antre-Monde Éditions.
* Ce système FoundryVTT est une implémentation indépendante et n'est pas
* affilié à Antre-Monde Éditions,
* mais a été réalisé avec l'autorisation d'Antre-Monde Éditions.
*
* @author LeRatierBretonnien
* @copyright 20242026 LeRatierBretonnien
* @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
*/
export default class CharacterDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const { fields } = foundry.data
const numberField = (initial = 0, extra = {}) => new fields.NumberField({ required: true, nullable: false, integer: true, initial, ...extra })
const stringField = (initial = "") => new fields.StringField({ required: true, nullable: false, initial })
const boolField = (initial = false) => new fields.BooleanField({ required: true, initial })
const htmlField = (initial = "") => new fields.HTMLField({ required: true, nullable: false, initial, textSearch: true })
const aspectField = (label, chinese) =>
new fields.SchemaField({
chinese: stringField(chinese),
label: stringField(label),
value: numberField(15, { min: 0 }),
})
const skillField = (label) =>
new fields.SchemaField({
label: stringField(label),
specialities: stringField(""),
value: numberField(0, { min: 0 }),
})
const resourceField = (label) =>
new fields.SchemaField({
label: stringField(label),
specialities: stringField(""),
value: numberField(0, { min: 0 }),
debt: boolField(false),
})
const componentField = () =>
new fields.SchemaField({
value: stringField(""),
})
const magicSpecialityField = () =>
new fields.SchemaField({
check: boolField(false),
})
const magicField = () =>
new fields.SchemaField({
visible: boolField(true),
value: numberField(0, { min: 0 }),
speciality: new fields.SchemaField({
essence: magicSpecialityField(),
mind: magicSpecialityField(),
purification: magicSpecialityField(),
manipulation: magicSpecialityField(),
aura: magicSpecialityField(),
acupuncture: magicSpecialityField(),
elixirs: magicSpecialityField(),
poisons: magicSpecialityField(),
arsenal: magicSpecialityField(),
potions: magicSpecialityField(),
curse: magicSpecialityField(),
transfiguration: magicSpecialityField(),
necromancy: magicSpecialityField(),
climatecontrol: magicSpecialityField(),
goldenmagic: magicSpecialityField(),
invocation: magicSpecialityField(),
tracking: magicSpecialityField(),
protection: magicSpecialityField(),
punishment: magicSpecialityField(),
domination: magicSpecialityField(),
neutralization: magicSpecialityField(),
divination: magicSpecialityField(),
earthlyprayer: magicSpecialityField(),
heavenlyprayer: magicSpecialityField(),
fungseoi: magicSpecialityField(),
}),
})
const treasureBranch = () =>
new fields.SchemaField({
value: numberField(0, { min: 0 }),
max: numberField(0, { min: 0 }),
min: numberField(0, { min: 0 }),
})
const treasureLevel = () =>
new fields.SchemaField({
san: treasureBranch(),
zing: treasureBranch(),
})
const schema = {
concept: stringField(""),
guardian: numberField(0, { min: 0, max: 5 }),
initiative: numberField(1, { min: 0 }),
anti_initiative: numberField(24, { min: 0 }),
description: htmlField(""),
prefs: new fields.SchemaField({
typeofthrow: new fields.SchemaField({
check: boolField(true),
choice: stringField("0"),
}),
}),
prompt: new fields.SchemaField({
typeofthrow: new fields.SchemaField({
check: boolField(true),
choice: stringField("0"),
}),
configure: new fields.SchemaField({
numberofdice: numberField(0),
aspect: numberField(0),
bonus: numberField(0),
bonusauspiciousdice: numberField(0),
typeofthrow: numberField(0),
aspectskill: numberField(0),
bonusmalusskill: numberField(0),
rolldifficulty: numberField(0),
freepowerlevels: numberField(0),
}),
}),
aspect: new fields.SchemaField({
fire: aspectField("CDE.Fire", "㊋"),
earth: aspectField("CDE.Earth", "㊏"),
metal: aspectField("CDE.Metal", "㊎"),
water: aspectField("CDE.Water", "㊌"),
wood: aspectField("CDE.Wood", "㊍"),
}),
skills: new fields.SchemaField({
art: skillField("CDE.Art"),
investigation: skillField("CDE.Investigation"),
erudition: skillField("CDE.Erudition"),
knavery: skillField("CDE.Knavery"),
wordliness: skillField("CDE.Wordliness"),
prowess: skillField("CDE.Prowess"),
sciences: skillField("CDE.Sciences"),
technologies: skillField("CDE.Technologies"),
kungfu: skillField("CDE.KungFu"),
rangedcombat: skillField("CDE.RangedCombat"),
}),
resources: new fields.SchemaField({
supply: resourceField("CDE.Supply"),
inquiry: resourceField("CDE.Inquiry"),
influence: resourceField("CDE.Influence"),
}),
component: new fields.SchemaField({
one: componentField(),
two: componentField(),
three: componentField(),
four: componentField(),
five: componentField(),
six: componentField(),
seven: componentField(),
eight: componentField(),
nine: componentField(),
zero: componentField(),
}),
magicOrder: new fields.ArrayField(
new fields.StringField({ required: true, nullable: false, initial: "" }),
{ required: true, initial: [] }
),
magics: new fields.SchemaField({
internalcinnabar: magicField(),
alchemy: magicField(),
masteryoftheway: magicField(),
exorcism: magicField(),
geomancy: magicField(),
}),
threetreasures: new fields.SchemaField({
heiyang: new fields.SchemaField({ value: numberField(0, { min: 0 }), max: numberField(0, { min: 0 }) }),
heiyin: new fields.SchemaField({ value: numberField(0, { min: 0 }), max: numberField(0, { min: 0 }) }),
dicelevel: new fields.SchemaField({
level0d: treasureLevel(),
level1d: treasureLevel(),
level2d: treasureLevel(),
}),
}),
experience: new fields.SchemaField({
value: numberField(0, { min: 0 }),
max: numberField(0, { min: 0 }),
min: numberField(0, { min: 0 }),
}),
}
return schema
}
prepareDerivedData() {
this.anti_initiative = 25 - (this.initiative ?? 0)
}
}