75f79c1c08
- `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)
199 lines
7.0 KiB
JavaScript
199 lines
7.0 KiB
JavaScript
/**
|
||
* 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 2024–2026 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)
|
||
}
|
||
}
|