- Nouvelle palette : #080c14 fond, accents néon par type (#00d4d4 item, #ff3d5a kungfu, #4a9eff spell, #cc44ff supernatural) - Nouveaux composants LESS : .cde-neon-header (clip-path angulaire + accent line), .cde-avatar (clip-path), .cde-stat-grid/.cde-stat-cell (style terminal), .cde-badge (parallélogramme), .cde-neon-tabs (underline néon animé), .cde-check-cell - Fix layout : .cde-sheet width: 100% + height: 100% + overflow: hidden, .cde-tab-body flex: 1 + min-height: 0, .cde-notes-editor flex stretch - Fix positions : DEFAULT_OPTIONS height explicite pour tous les types (item 620x580, spell 660x680, kungfu 720x680, supernatural 560x520) - 4 templates items reécrits avec nouvelles classes et structure épurée Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1188 lines
50 KiB
JavaScript
1188 lines
50 KiB
JavaScript
// src/config/constants.js
|
|
var SYSTEM_ID = "fvtt-chroniques-de-l-etrange";
|
|
var ACTOR_TYPES = {
|
|
character: "character",
|
|
npc: "npc",
|
|
tinji: "tinji",
|
|
loksyu: "loksyu"
|
|
};
|
|
var ITEM_TYPES = {
|
|
item: "item",
|
|
kungfu: "kungfu",
|
|
spell: "spell",
|
|
supernatural: "supernatural"
|
|
};
|
|
var SUBTYPES = {
|
|
weapon: { id: "weapon", label: "CDE.Weapon" },
|
|
armor: { id: "armor", label: "CDE.Armor" },
|
|
sanhei: { id: "sanhei", label: "CDE.Sanhei" },
|
|
other: { id: "other", label: "CDE.Other" }
|
|
};
|
|
var MAGICS = {
|
|
internalcinnabar: {
|
|
id: "internalcinnabar",
|
|
background: "linear-grey",
|
|
label: "CDE.InternalCinnabar",
|
|
aspectlabel: "CDE.Metal",
|
|
speciality: {
|
|
essence: { label: "CDE.Essence", classicon: "icon-yin", icon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_yin.png", elementicon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_metal.png", labelicon: "Yin", labelelement: "CDE.Metal" },
|
|
mind: { label: "CDE.Mind", classicon: "icon-yin", icon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_yin.png", elementicon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_eau.png", labelicon: "Yin", labelelement: "CDE.Water" },
|
|
purification: { label: "CDE.Purification", classicon: "icon-yinyang", icon: "/systems/fvtt-chroniques-de-l-etrange/images/yin_yang.png", elementicon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_terre.png", labelicon: "Yin/Yang", labelelement: "CDE.Earth" },
|
|
manipulation: { label: "CDE.Manipulation", classicon: "icon-yang", icon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_yang.png", elementicon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_feu.png", labelicon: "Yang", labelelement: "CDE.Fire" },
|
|
aura: { label: "CDE.Aura", classicon: "icon-yang", icon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_yang.png", elementicon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_bois.png", labelicon: "Yang", labelelement: "CDE.Wood" }
|
|
}
|
|
},
|
|
alchemy: {
|
|
id: "alchemy",
|
|
background: "linear-blue",
|
|
label: "CDE.Alchemy",
|
|
aspectlabel: "CDE.Water",
|
|
speciality: {
|
|
acupuncture: { label: "CDE.Acupuncture", classicon: "icon-yin", icon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_yin.png", elementicon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_metal.png", labelicon: "Yin", labelelement: "CDE.Metal" },
|
|
elixirs: { label: "CDE.Elixirs", classicon: "icon-yin", icon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_yin.png", elementicon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_eau.png", labelicon: "Yin", labelelement: "CDE.Water" },
|
|
poisons: { label: "CDE.Poisons", classicon: "icon-yinyang", icon: "/systems/fvtt-chroniques-de-l-etrange/images/yin_yang.png", elementicon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_terre.png", labelicon: "Yin/Yang", labelelement: "CDE.Earth" },
|
|
arsenal: { label: "CDE.Arsenal", classicon: "icon-yang", icon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_yang.png", elementicon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_feu.png", labelicon: "Yang", labelelement: "CDE.Fire" },
|
|
potions: { label: "CDE.Potions", classicon: "icon-yang", icon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_yang.png", elementicon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_bois.png", labelicon: "Yang", labelelement: "CDE.Wood" }
|
|
}
|
|
},
|
|
masteryoftheway: {
|
|
id: "masteryoftheway",
|
|
background: "linear-brown",
|
|
label: "CDE.MasteryOfTheWay",
|
|
aspectlabel: "CDE.Earth",
|
|
speciality: {
|
|
curse: { label: "CDE.Curse", classicon: "icon-yin", icon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_yin.png", elementicon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_metal.png", labelicon: "Yin", labelelement: "CDE.Metal" },
|
|
transfiguration: { label: "CDE.Transfiguration", classicon: "icon-yin", icon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_yin.png", elementicon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_eau.png", labelicon: "Yin", labelelement: "CDE.Water" },
|
|
necromancy: { label: "CDE.Necromancy", classicon: "icon-yinyang", icon: "/systems/fvtt-chroniques-de-l-etrange/images/yin_yang.png", elementicon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_terre.png", labelicon: "Yin/Yang", labelelement: "CDE.Earth" },
|
|
climatecontrol: { label: "CDE.ClimateControl", classicon: "icon-yang", icon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_yang.png", elementicon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_feu.png", labelicon: "Yang", labelelement: "CDE.Fire" },
|
|
goldenmagic: { label: "CDE.GoldenMagic", classicon: "icon-yang", icon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_yang.png", elementicon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_bois.png", labelicon: "Yang", labelelement: "CDE.Wood" }
|
|
}
|
|
},
|
|
exorcism: {
|
|
id: "exorcism",
|
|
background: "linear-red",
|
|
label: "CDE.Exorcism",
|
|
aspectlabel: "CDE.Fire",
|
|
speciality: {
|
|
invocation: { label: "CDE.Invocation", classicon: "icon-yin", icon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_yin.png", elementicon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_metal.png", labelicon: "Yin", labelelement: "CDE.Metal" },
|
|
tracking: { label: "CDE.Tracking", classicon: "icon-yin", icon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_yin.png", elementicon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_eau.png", labelicon: "Yin", labelelement: "CDE.Water" },
|
|
protection: { label: "CDE.Protection", classicon: "icon-yinyang", icon: "/systems/fvtt-chroniques-de-l-etrange/images/yin_yang.png", elementicon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_terre.png", labelicon: "Yin/Yang", labelelement: "CDE.Earth" },
|
|
punishment: { label: "CDE.Punishment", classicon: "icon-yang", icon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_yang.png", elementicon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_feu.png", labelicon: "Yang", labelelement: "CDE.Fire" },
|
|
domination: { label: "CDE.Domination", classicon: "icon-yang", icon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_yang.png", elementicon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_bois.png", labelicon: "Yang", labelelement: "CDE.Wood" }
|
|
}
|
|
},
|
|
geomancy: {
|
|
id: "geomancy",
|
|
background: "linear-green",
|
|
label: "CDE.Geomancy",
|
|
aspectlabel: "CDE.Wood",
|
|
speciality: {
|
|
neutralization: { label: "CDE.Neutralization", classicon: "icon-yin", icon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_yin.png", elementicon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_metal.png", labelicon: "Yin", labelelement: "CDE.Metal" },
|
|
divination: { label: "CDE.Divination", classicon: "icon-yin", icon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_yin.png", elementicon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_eau.png", labelicon: "Yin", labelelement: "CDE.Water" },
|
|
earthlyprayer: { label: "CDE.EarthlyPrayer", classicon: "icon-yinyang", icon: "/systems/fvtt-chroniques-de-l-etrange/images/yin_yang.png", elementicon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_terre.png", labelicon: "Yin/Yang", labelelement: "CDE.Earth" },
|
|
heavenlyprayer: { label: "CDE.HeavenlyPrayer", classicon: "icon-yang", icon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_yang.png", elementicon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_feu.png", labelicon: "Yang", labelelement: "CDE.Fire" },
|
|
fungseoi: { label: "CDE.Fungseoi", classicon: "icon-yang", icon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_yang.png", elementicon: "/systems/fvtt-chroniques-de-l-etrange/images/cde_bois.png", labelicon: "Yang", labelelement: "CDE.Wood" }
|
|
}
|
|
}
|
|
};
|
|
var TEMPLATE_PARTIALS = [
|
|
"systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-character-skills.html",
|
|
"systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-character-magics.html",
|
|
"systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-character-nghang.html",
|
|
"systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-character-treasures.html",
|
|
"systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-character-items.html",
|
|
"systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-character-kungfus.html",
|
|
"systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-character-spells.html",
|
|
"systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-npc-supernaturals.html",
|
|
"systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-npc-spells.html",
|
|
"systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-npc-kungfus.html",
|
|
"systems/fvtt-chroniques-de-l-etrange/templates/actor/parts/cde-npc-items.html"
|
|
];
|
|
|
|
// src/config/localize.js
|
|
function preLocalizeConfig() {
|
|
const localizeConfigObject = (obj, keys) => {
|
|
for (const o of Object.values(obj)) {
|
|
for (const key of keys) {
|
|
o[key] = game.i18n.localize(o[key]);
|
|
}
|
|
}
|
|
};
|
|
localizeConfigObject(SUBTYPES, ["label"]);
|
|
Object.values(MAGICS).forEach((magic) => {
|
|
magic.label = game.i18n.localize(magic.label);
|
|
magic.aspectlabel = game.i18n.localize(magic.aspectlabel);
|
|
Object.values(magic.speciality).forEach((spec) => {
|
|
spec.label = game.i18n.localize(spec.label);
|
|
spec.labelelement = game.i18n.localize(spec.labelelement);
|
|
});
|
|
});
|
|
}
|
|
|
|
// src/config/runtime.js
|
|
function configureRuntime() {
|
|
CONFIG.Actor.compendiumBanner = "/systems/fvtt-chroniques-de-l-etrange/images/banners/actor-banner.webp";
|
|
CONFIG.Adventure.compendiumBanner = "/systems/fvtt-chroniques-de-l-etrange/images/banners/adventure-banner.webp";
|
|
CONFIG.Cards.compendiumBanner = "ui/banners/cards-banner.webp";
|
|
CONFIG.Item.compendiumBanner = "/systems/fvtt-chroniques-de-l-etrange/images/banners/item-banner.webp";
|
|
CONFIG.JournalEntry.compendiumBanner = "/systems/fvtt-chroniques-de-l-etrange/images/banners/journalentry-banner.webp";
|
|
CONFIG.Macro.compendiumBanner = "ui/banners/macro-banner.webp";
|
|
CONFIG.Playlist.compendiumBanner = "ui/banners/playlist-banner.webp";
|
|
CONFIG.RollTable.compendiumBanner = "ui/banners/rolltable-banner.webp";
|
|
CONFIG.Scene.compendiumBanner = "/systems/fvtt-chroniques-de-l-etrange/images/banners/scene-banner.webp";
|
|
}
|
|
|
|
// src/data/actors/character.js
|
|
var CharacterDataModel = class 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: stringField("0"),
|
|
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),
|
|
aspectspeciality: numberField(0),
|
|
rolldifficulty: numberField(0),
|
|
bonusmalusspeciality: numberField(0)
|
|
})
|
|
}),
|
|
aspect: new fields.SchemaField({
|
|
fire: aspectField("CDE.Fire", "\u328B"),
|
|
earth: aspectField("CDE.Earth", "\u328F"),
|
|
metal: aspectField("CDE.Metal", "\u328E"),
|
|
water: aspectField("CDE.Water", "\u328C"),
|
|
wood: aspectField("CDE.Wood", "\u328D")
|
|
}),
|
|
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()
|
|
}),
|
|
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 }) }),
|
|
heiyin: new fields.SchemaField({ value: 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;
|
|
}
|
|
};
|
|
|
|
// src/data/actors/npc.js
|
|
var NpcDataModel = class 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 aptitudeField = () => new fields.SchemaField({
|
|
value: numberField(0, { min: 0 }),
|
|
speciality: stringField("")
|
|
});
|
|
const trackedField = () => new fields.SchemaField({
|
|
value: numberField(0, { min: 0 }),
|
|
calcul: numberField(0, { min: 0 }),
|
|
note: stringField("")
|
|
});
|
|
return {
|
|
type: stringField(""),
|
|
levelofthreat: numberField(0, { min: 0 }),
|
|
powerofnuisance: numberField(0, { min: 0 }),
|
|
initiative: numberField(1, { min: 0 }),
|
|
anti_initiative: numberField(24, { min: 0 }),
|
|
aptitudes: new fields.SchemaField({
|
|
physical: aptitudeField(),
|
|
martial: aptitudeField(),
|
|
mental: aptitudeField(),
|
|
social: aptitudeField(),
|
|
spiritual: aptitudeField()
|
|
}),
|
|
vitality: trackedField(),
|
|
hei: trackedField(),
|
|
description: htmlField(""),
|
|
prefs: new fields.SchemaField({
|
|
typeofthrow: new fields.SchemaField({
|
|
check: boolField(false),
|
|
choice: stringField("0")
|
|
})
|
|
})
|
|
};
|
|
}
|
|
};
|
|
|
|
// src/data/actors/tinji.js
|
|
var TinjiDataModel = class 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 htmlField = (initial = "") => new fields.HTMLField({ required: true, nullable: false, initial, textSearch: true });
|
|
return {
|
|
value: numberField(0, { min: 0 }),
|
|
description: htmlField("")
|
|
};
|
|
}
|
|
};
|
|
|
|
// src/data/actors/loksyu.js
|
|
var LoksyuDataModel = class 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 htmlField = (initial = "") => new fields.HTMLField({ required: true, nullable: false, initial, textSearch: true });
|
|
const polarity = () => new fields.SchemaField({
|
|
yin: new fields.SchemaField({ value: numberField(0, { min: 0 }) }),
|
|
yang: new fields.SchemaField({ value: numberField(0, { min: 0 }) })
|
|
});
|
|
return {
|
|
fire: polarity(),
|
|
earth: polarity(),
|
|
metal: polarity(),
|
|
water: polarity(),
|
|
wood: polarity(),
|
|
description: htmlField("")
|
|
};
|
|
}
|
|
};
|
|
|
|
// src/data/items/item.js
|
|
var EquipmentDataModel = class 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 htmlField = (initial = "") => new fields.HTMLField({ required: true, nullable: false, initial, textSearch: true });
|
|
return {
|
|
subtype: stringField(""),
|
|
reference: stringField(""),
|
|
description: htmlField(""),
|
|
quantity: numberField(1, { min: 0 }),
|
|
weight: numberField(0, { min: 0 }),
|
|
protection: stringField(""),
|
|
damage: stringField(""),
|
|
range: stringField(""),
|
|
notes: htmlField("")
|
|
};
|
|
}
|
|
};
|
|
|
|
// src/data/items/kungfu.js
|
|
var KungfuDataModel = class extends foundry.abstract.TypeDataModel {
|
|
static defineSchema() {
|
|
const { fields } = foundry.data;
|
|
const stringField = (initial = "") => new fields.StringField({ required: true, nullable: false, initial });
|
|
const htmlField = (initial = "") => new fields.HTMLField({ required: true, nullable: false, initial, textSearch: true });
|
|
const boolField = (initial = false) => new fields.BooleanField({ required: true, initial });
|
|
const techniqueField = () => new fields.SchemaField({
|
|
check: boolField(false),
|
|
name: stringField(""),
|
|
activation: stringField(""),
|
|
technique: htmlField("")
|
|
});
|
|
return {
|
|
reference: stringField(""),
|
|
description: htmlField(""),
|
|
orientation: stringField(""),
|
|
aspect: stringField(""),
|
|
skill: stringField(""),
|
|
speciality: stringField(""),
|
|
style: stringField(""),
|
|
techniques: new fields.SchemaField({
|
|
technique1: techniqueField(),
|
|
technique2: techniqueField(),
|
|
technique3: techniqueField()
|
|
}),
|
|
notes: htmlField("")
|
|
};
|
|
}
|
|
};
|
|
|
|
// src/data/items/spell.js
|
|
var SpellDataModel = class extends foundry.abstract.TypeDataModel {
|
|
static defineSchema() {
|
|
const { fields } = foundry.data;
|
|
const stringField = (initial = "") => new fields.StringField({ required: true, nullable: false, initial });
|
|
const htmlField = (initial = "") => new fields.HTMLField({ required: true, nullable: false, initial, textSearch: true });
|
|
return {
|
|
reference: stringField(""),
|
|
description: htmlField(""),
|
|
specialityname: stringField(""),
|
|
associatedelement: stringField(""),
|
|
hei: stringField(""),
|
|
realizationtimeritual: stringField(""),
|
|
realizationtimeaccelerated: stringField(""),
|
|
flashback: stringField(""),
|
|
components: htmlField(""),
|
|
effects: htmlField(""),
|
|
examples: htmlField(""),
|
|
notes: htmlField("")
|
|
};
|
|
}
|
|
};
|
|
|
|
// src/data/items/supernatural.js
|
|
var SupernaturalDataModel = class extends foundry.abstract.TypeDataModel {
|
|
static defineSchema() {
|
|
const { fields } = foundry.data;
|
|
const stringField = (initial = "") => new fields.StringField({ required: true, nullable: false, initial });
|
|
const htmlField = (initial = "") => new fields.HTMLField({ required: true, nullable: false, initial, textSearch: true });
|
|
return {
|
|
reference: stringField(""),
|
|
description: htmlField(""),
|
|
notes: htmlField("")
|
|
};
|
|
}
|
|
};
|
|
|
|
// src/documents/chat-message.js
|
|
var CDEMessage = class extends ChatMessage {
|
|
async renderHTML({ canDelete, canClose = false, ...rest } = {}) {
|
|
const html = await super.renderHTML({ canDelete, canClose, ...rest });
|
|
this.#enrichChatCard(html);
|
|
return html;
|
|
}
|
|
getAssociatedActor() {
|
|
if (this.speaker.scene && this.speaker.token) {
|
|
const scene = game.scenes.get(this.speaker.scene);
|
|
const token = scene?.tokens.get(this.speaker.token);
|
|
if (token) return token.actor;
|
|
}
|
|
return game.actors.get(this.speaker.actor);
|
|
}
|
|
#enrichChatCard(html) {
|
|
const actor = this.getAssociatedActor();
|
|
let img;
|
|
let nameText;
|
|
if (this.isContentVisible) {
|
|
img = actor?.img ?? this.author.avatar;
|
|
nameText = this.alias;
|
|
} else {
|
|
img = this.author.avatar;
|
|
nameText = this.author.name;
|
|
}
|
|
const avatar = document.createElement("a");
|
|
avatar.classList.add("avatar");
|
|
if (actor) avatar.dataset.uuid = actor.uuid;
|
|
const avatarImg = document.createElement("img");
|
|
Object.assign(avatarImg, { src: img, alt: nameText });
|
|
avatar.append(avatarImg);
|
|
const name = document.createElement("span");
|
|
name.classList.add("name-stacked");
|
|
const title = document.createElement("span");
|
|
title.classList.add("title");
|
|
title.append(nameText);
|
|
name.append(title);
|
|
const sender = html.querySelector(".message-sender");
|
|
sender?.replaceChildren(avatar, name);
|
|
}
|
|
};
|
|
|
|
// src/documents/actor.js
|
|
var CDEActor = class extends Actor {
|
|
getRollData() {
|
|
const data = this.toObject(false).system;
|
|
return data;
|
|
}
|
|
prepareBaseData() {
|
|
super.prepareBaseData();
|
|
if (this.type === ACTOR_TYPES.character) {
|
|
this.system.anti_initiative = 25 - (this.system.initiative ?? 0);
|
|
}
|
|
if (this.type === ACTOR_TYPES.npc) {
|
|
this.system.vitality.calcul = (this.system.aptitudes.physical.value ?? 0) * 4;
|
|
this.system.hei.calcul = (this.system.aptitudes.spiritual.value ?? 0) * 4;
|
|
this.system.anti_initiative = 25 - (this.system.initiative ?? 0);
|
|
}
|
|
}
|
|
};
|
|
|
|
// src/documents/item.js
|
|
var CDEItem = class extends Item {
|
|
get isWeapon() {
|
|
return this.system.subtype === "weapon";
|
|
}
|
|
get isArmor() {
|
|
return this.system.subtype === "armor";
|
|
}
|
|
get isSanhei() {
|
|
return this.system.subtype === "sanhei";
|
|
}
|
|
get isOther() {
|
|
return this.system.subtype === "other";
|
|
}
|
|
};
|
|
|
|
// src/ui/dice.js
|
|
var DIGIT_LABELS = [
|
|
"systems/fvtt-chroniques-de-l-etrange/images/dice-so-nice/digit/d10-1.webp",
|
|
"systems/fvtt-chroniques-de-l-etrange/images/dice-so-nice/digit/d10-2.webp",
|
|
"systems/fvtt-chroniques-de-l-etrange/images/dice-so-nice/digit/d10-3.webp",
|
|
"systems/fvtt-chroniques-de-l-etrange/images/dice-so-nice/digit/d10-4.webp",
|
|
"systems/fvtt-chroniques-de-l-etrange/images/dice-so-nice/digit/d10-5.webp",
|
|
"systems/fvtt-chroniques-de-l-etrange/images/dice-so-nice/digit/d10-6.webp",
|
|
"systems/fvtt-chroniques-de-l-etrange/images/dice-so-nice/digit/d10-7.webp",
|
|
"systems/fvtt-chroniques-de-l-etrange/images/dice-so-nice/digit/d10-8.webp",
|
|
"systems/fvtt-chroniques-de-l-etrange/images/dice-so-nice/digit/d10-9.webp",
|
|
"systems/fvtt-chroniques-de-l-etrange/images/dice-so-nice/digit/d10-10.webp"
|
|
];
|
|
var CLASSIC_LABELS = [
|
|
"systems/fvtt-chroniques-de-l-etrange/images/dice-so-nice/d10-1.webp",
|
|
"systems/fvtt-chroniques-de-l-etrange/images/dice-so-nice/d10-2.webp",
|
|
"systems/fvtt-chroniques-de-l-etrange/images/dice-so-nice/d10-3.webp",
|
|
"systems/fvtt-chroniques-de-l-etrange/images/dice-so-nice/d10-4.webp",
|
|
"systems/fvtt-chroniques-de-l-etrange/images/dice-so-nice/d10-5.webp",
|
|
"systems/fvtt-chroniques-de-l-etrange/images/dice-so-nice/d10-6.webp",
|
|
"systems/fvtt-chroniques-de-l-etrange/images/dice-so-nice/d10-7.webp",
|
|
"systems/fvtt-chroniques-de-l-etrange/images/dice-so-nice/d10-8.webp",
|
|
"systems/fvtt-chroniques-de-l-etrange/images/dice-so-nice/d10-9.webp",
|
|
"systems/fvtt-chroniques-de-l-etrange/images/dice-so-nice/d10-10.webp"
|
|
];
|
|
function registerDice() {
|
|
Hooks.once("diceSoNiceReady", (dice3d) => {
|
|
dice3d.addColorset(
|
|
{
|
|
name: "cde",
|
|
description: "CdE",
|
|
foreground: "#000000",
|
|
background: "#ffffff",
|
|
edge: "#ffffff",
|
|
font: "DeliusUnicase",
|
|
texture: "ice",
|
|
material: "plastic"
|
|
},
|
|
"preferred"
|
|
);
|
|
dice3d.addSystem({ id: "fvtt-chroniques-de-l-etrangedigit", name: "Chroniques de l'\xE9trange digits" }, "preferred");
|
|
dice3d.addDicePreset({ type: "d10", labels: DIGIT_LABELS, system: "fvtt-chroniques-de-l-etrangedigit" });
|
|
dice3d.addSystem({ id: "fvtt-chroniques-de-l-etrange", name: "Chroniques de l'\xE9trange" }, "preferred");
|
|
dice3d.addDicePreset({ type: "d10", labels: CLASSIC_LABELS, system: "fvtt-chroniques-de-l-etrange" });
|
|
});
|
|
}
|
|
|
|
// src/ui/helpers.js
|
|
function registerHandlebarsHelpers() {
|
|
const { Handlebars } = globalThis;
|
|
if (!Handlebars) return;
|
|
Handlebars.registerHelper("select", function(selected, options) {
|
|
const escapedValue = RegExp.escape(Handlebars.escapeExpression(selected));
|
|
const rgx = new RegExp(` value=["']${escapedValue}["']`);
|
|
const html = options.fn(this);
|
|
return html.replace(rgx, "$& selected");
|
|
});
|
|
Handlebars.registerHelper("getMagicBackground", function(magic) {
|
|
return game.i18n.localize(MAGICS[magic]?.background ?? "");
|
|
});
|
|
Handlebars.registerHelper("getMagicLabel", function(magic) {
|
|
return game.i18n.localize(MAGICS[magic]?.label ?? "");
|
|
});
|
|
Handlebars.registerHelper("getMagicAspectLabel", function(magic) {
|
|
return game.i18n.localize(MAGICS[magic]?.aspectlabel ?? "");
|
|
});
|
|
Handlebars.registerHelper("getMagicSpecialityLabel", function(magic, speciality) {
|
|
return game.i18n.localize(MAGICS[magic]?.speciality?.[speciality]?.label ?? "");
|
|
});
|
|
Handlebars.registerHelper("getMagicSpecialityClassIcon", function(magic, speciality) {
|
|
return MAGICS[magic]?.speciality?.[speciality]?.classicon ?? "";
|
|
});
|
|
Handlebars.registerHelper("getMagicSpecialityIcon", function(magic, speciality) {
|
|
return MAGICS[magic]?.speciality?.[speciality]?.icon ?? "";
|
|
});
|
|
Handlebars.registerHelper("getMagicSpecialityElementIcon", function(magic, speciality) {
|
|
return MAGICS[magic]?.speciality?.[speciality]?.elementicon ?? "";
|
|
});
|
|
Handlebars.registerHelper("getMagicSpecialityLabelIcon", function(magic, speciality) {
|
|
return MAGICS[magic]?.speciality?.[speciality]?.labelicon ?? "";
|
|
});
|
|
Handlebars.registerHelper("getMagicSpecialityLabelElement", function(magic, speciality) {
|
|
return game.i18n.localize(MAGICS[magic]?.speciality?.[speciality]?.labelelement ?? "");
|
|
});
|
|
}
|
|
|
|
// src/ui/templates.js
|
|
async function preloadPartials() {
|
|
return loadTemplates(TEMPLATE_PARTIALS);
|
|
}
|
|
|
|
// src/ui/sheets/actors/base.js
|
|
var { HandlebarsApplicationMixin } = foundry.applications.api;
|
|
var CDEBaseActorSheet = class _CDEBaseActorSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ActorSheetV2) {
|
|
static DEFAULT_OPTIONS = {
|
|
classes: ["fvtt-chroniques-de-l-etrange", "actor"],
|
|
position: { width: 920, height: "auto" },
|
|
window: { resizable: true },
|
|
form: { submitOnChange: true },
|
|
dragDrop: [{ dragSelector: ".item, [data-drag='true']", dropSelector: null }],
|
|
actions: {
|
|
create: _CDEBaseActorSheet.#onItemCreate,
|
|
edit: _CDEBaseActorSheet.#onItemEdit,
|
|
delete: _CDEBaseActorSheet.#onItemDelete
|
|
}
|
|
};
|
|
tabGroups = { primary: "description" };
|
|
async _prepareContext() {
|
|
const descriptionHTML = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description ?? "", { async: true });
|
|
const cssClass = this.options.classes?.join(" ") ?? "";
|
|
return {
|
|
actor: this.document,
|
|
system: this.document.system,
|
|
systemData: this.document.system,
|
|
items: this.document.items.contents,
|
|
descriptionHTML,
|
|
editable: this.isEditable,
|
|
cssClass
|
|
};
|
|
}
|
|
async _onFirstRender(context, options) {
|
|
await super._onFirstRender(context, options);
|
|
for (const [group, tab] of Object.entries(this.tabGroups)) {
|
|
this.changeTab(tab, group, { force: true });
|
|
}
|
|
}
|
|
_onRender(context, options) {
|
|
for (const [group, tab] of Object.entries(this.tabGroups)) {
|
|
this.changeTab(tab, group, { force: true });
|
|
}
|
|
}
|
|
static async #onItemCreate(event, target) {
|
|
const type = target.dataset.type ?? "item";
|
|
const cls = getDocumentClass("Item");
|
|
const labels = {
|
|
item: "CDE.ItemNew",
|
|
kungfu: "CDE.KFNew",
|
|
spell: "CDE.SpellNew",
|
|
supernatural: "CDE.SupernaturalNew"
|
|
};
|
|
const name = game.i18n.localize(labels[type] ?? "CDE.ItemNew");
|
|
return cls.create({ name, type }, { parent: this.document });
|
|
}
|
|
static #onItemEdit(event, target) {
|
|
const itemId = target.closest(".item")?.dataset.itemId;
|
|
const item = this.document.items.get(itemId);
|
|
if (item) item.sheet.render(true);
|
|
}
|
|
static #onItemDelete(event, target) {
|
|
const itemId = target.closest(".item")?.dataset.itemId;
|
|
const item = this.document.items.get(itemId);
|
|
if (item) item.delete();
|
|
}
|
|
};
|
|
|
|
// src/ui/sheets/actors/character.js
|
|
var CDECharacterSheet = class extends CDEBaseActorSheet {
|
|
static DEFAULT_OPTIONS = {
|
|
classes: ["character"]
|
|
};
|
|
static PARTS = {
|
|
main: { template: "systems/fvtt-chroniques-de-l-etrange/templates/actor/cde-character-sheet.html" }
|
|
};
|
|
tabGroups = { primary: "description" };
|
|
async _prepareContext() {
|
|
const context = await super._prepareContext();
|
|
context.equipments = context.items.filter((item) => item.type === "item");
|
|
context.spells = context.items.filter((item) => item.type === "spell");
|
|
context.kungfus = context.items.filter((item) => item.type === "kungfu");
|
|
context.CDE = { MAGICS, SUBTYPES };
|
|
return context;
|
|
}
|
|
_onRender(context, options) {
|
|
super._onRender?.(context, options);
|
|
this.#bindInitiativeControls();
|
|
this.#bindPrefs();
|
|
}
|
|
#bindInitiativeControls() {
|
|
const buttons = this.element?.querySelectorAll(".click-initiative");
|
|
if (!buttons?.length) return;
|
|
buttons.forEach((button) => {
|
|
button.addEventListener("click", async () => {
|
|
const action = button.dataset.libelId;
|
|
let initiative = this.document.system.initiative ?? 1;
|
|
if (action === "plus") {
|
|
initiative = initiative >= 24 ? 1 : initiative + 1;
|
|
await this.document.update({ "system.initiative": initiative });
|
|
return;
|
|
}
|
|
if (action === "minus") {
|
|
initiative = initiative <= 1 ? 24 : initiative - 1;
|
|
await this.document.update({ "system.initiative": initiative });
|
|
return;
|
|
}
|
|
if (action === "create") {
|
|
const html = `
|
|
<form class="flexcol">
|
|
<div class="form-group">
|
|
<label>${game.i18n.localize("CDE.TurnOrder")}</label>
|
|
<input type="number" name="initiative" value="${initiative}" min="1" max="24" />
|
|
</div>
|
|
</form>`;
|
|
const value = await Dialog.prompt({
|
|
title: game.i18n.localize("CDE.TurnOrder"),
|
|
content: html,
|
|
label: game.i18n.localize("CDE.Validate"),
|
|
callback: (dlg) => {
|
|
const input = dlg.querySelector("input[name='initiative']");
|
|
return Number(input?.value ?? initiative);
|
|
}
|
|
});
|
|
if (Number.isFinite(value)) {
|
|
const sanitized = foundry.utils.clamp(Number(value), 1, 24);
|
|
await this.document.update({ "system.initiative": sanitized });
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
#bindPrefs() {
|
|
const button = this.element?.querySelector(".click-prefs");
|
|
if (!button) return;
|
|
button.addEventListener("click", async () => {
|
|
const current = this.document.system.prefs?.typeofthrow ?? { choice: "0", check: true };
|
|
const html = `
|
|
<form class="flexcol">
|
|
<div class="form-group">
|
|
<label>${game.i18n.localize("CDE.ThrowType")}</label>
|
|
<select name="choice" value="${current.choice}">
|
|
<option value="0"${current.choice === "0" ? " selected" : ""}>0</option>
|
|
<option value="1"${current.choice === "1" ? " selected" : ""}>1</option>
|
|
<option value="2"${current.choice === "2" ? " selected" : ""}>2</option>
|
|
<option value="3"${current.choice === "3" ? " selected" : ""}>3</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>${game.i18n.localize("CDE.EnablePrompt")}</label>
|
|
<input type="checkbox" name="check" ${current.check ? "checked" : ""}/>
|
|
</div>
|
|
</form>`;
|
|
const prefs = await Dialog.prompt({
|
|
title: game.i18n.localize("CDE.Preferences"),
|
|
content: html,
|
|
label: game.i18n.localize("CDE.Validate"),
|
|
callback: (dlg) => {
|
|
const choice = dlg.querySelector("select[name='choice']")?.value ?? "0";
|
|
const check = dlg.querySelector("input[name='check']")?.checked ?? false;
|
|
return { choice, check };
|
|
}
|
|
});
|
|
if (prefs) {
|
|
await this.document.update({
|
|
"system.prefs.typeofthrow.choice": String(prefs.choice),
|
|
"system.prefs.typeofthrow.check": !!prefs.check
|
|
});
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
// src/ui/sheets/actors/npc.js
|
|
var CDENpcSheet = class extends CDEBaseActorSheet {
|
|
static DEFAULT_OPTIONS = {
|
|
classes: ["npc"]
|
|
};
|
|
static PARTS = {
|
|
main: { template: "systems/fvtt-chroniques-de-l-etrange/templates/actor/cde-npc-sheet.html" }
|
|
};
|
|
tabGroups = { primary: "description" };
|
|
async _prepareContext() {
|
|
const context = await super._prepareContext();
|
|
context.supernaturals = context.items.filter((item) => item.type === "supernatural");
|
|
context.spells = context.items.filter((item) => item.type === "spell");
|
|
context.kungfus = context.items.filter((item) => item.type === "kungfu");
|
|
context.equipments = context.items.filter((item) => item.type === "item");
|
|
return context;
|
|
}
|
|
_onRender(context, options) {
|
|
super._onRender?.(context, options);
|
|
this.#bindInitiativeControls();
|
|
}
|
|
#bindInitiativeControls() {
|
|
const buttons = this.element?.querySelectorAll(".click-initiative-npc");
|
|
if (!buttons?.length) return;
|
|
buttons.forEach((button) => {
|
|
button.addEventListener("click", async () => {
|
|
const action = button.dataset.libelId;
|
|
let initiative = this.document.system.initiative ?? 1;
|
|
if (action === "plus") {
|
|
initiative = initiative >= 24 ? 1 : initiative + 1;
|
|
await this.document.update({ "system.initiative": initiative });
|
|
return;
|
|
}
|
|
if (action === "minus") {
|
|
initiative = initiative <= 1 ? 24 : initiative - 1;
|
|
await this.document.update({ "system.initiative": initiative });
|
|
return;
|
|
}
|
|
if (action === "create") {
|
|
const html = `
|
|
<form class="flexcol">
|
|
<div class="form-group">
|
|
<label>${game.i18n.localize("CDE.TurnOrder")}</label>
|
|
<input type="number" name="initiative" value="${initiative}" min="1" max="24" />
|
|
</div>
|
|
</form>`;
|
|
const value = await Dialog.prompt({
|
|
title: game.i18n.localize("CDE.TurnOrder"),
|
|
content: html,
|
|
label: game.i18n.localize("CDE.Validate"),
|
|
callback: (dlg) => Number(dlg.querySelector("input[name='initiative']")?.value ?? initiative)
|
|
});
|
|
if (Number.isFinite(value)) {
|
|
const sanitized = foundry.utils.clamp(Number(value), 1, 24);
|
|
await this.document.update({ "system.initiative": sanitized });
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
};
|
|
|
|
// src/ui/sheets/actors/tinji.js
|
|
var CDETinjiSheet = class extends CDEBaseActorSheet {
|
|
static DEFAULT_OPTIONS = {
|
|
classes: ["tinji"]
|
|
};
|
|
static PARTS = {
|
|
main: { template: "systems/fvtt-chroniques-de-l-etrange/templates/actor/cde-tinji-sheet.html" }
|
|
};
|
|
tabGroups = { primary: "tinji" };
|
|
};
|
|
|
|
// src/ui/sheets/actors/loksyu.js
|
|
var CDELoksyuSheet = class extends CDEBaseActorSheet {
|
|
static DEFAULT_OPTIONS = {
|
|
classes: ["loksyu"]
|
|
};
|
|
static PARTS = {
|
|
main: { template: "systems/fvtt-chroniques-de-l-etrange/templates/actor/cde-loksyu-sheet.html" }
|
|
};
|
|
tabGroups = { primary: "loksyu" };
|
|
};
|
|
|
|
// src/ui/sheets/items/base.js
|
|
var { HandlebarsApplicationMixin: HandlebarsApplicationMixin2 } = foundry.applications.api;
|
|
var CDEBaseItemSheet = class extends HandlebarsApplicationMixin2(foundry.applications.sheets.ItemSheetV2) {
|
|
static DEFAULT_OPTIONS = {
|
|
classes: ["fvtt-chroniques-de-l-etrange", "item"],
|
|
position: { width: 520, height: "auto" },
|
|
window: { resizable: true },
|
|
form: { submitOnChange: true },
|
|
actions: {}
|
|
};
|
|
tabGroups = { primary: "description" };
|
|
async _prepareContext() {
|
|
const cssClass = this.options.classes?.join(" ") ?? "";
|
|
const enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description ?? "", { async: true });
|
|
const enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.notes ?? "", { async: true });
|
|
return {
|
|
item: this.document,
|
|
system: this.document.system,
|
|
systemData: this.document.system,
|
|
editable: this.isEditable,
|
|
cssClass,
|
|
enrichedDescription,
|
|
enrichedNotes,
|
|
descriptionHTML: enrichedDescription,
|
|
notesHTML: enrichedNotes
|
|
};
|
|
}
|
|
async _onFirstRender(context, options) {
|
|
await super._onFirstRender(context, options);
|
|
for (const [group, tab] of Object.entries(this.tabGroups)) {
|
|
this.changeTab(tab, group, { force: true });
|
|
}
|
|
}
|
|
_onRender(context, options) {
|
|
for (const [group, tab] of Object.entries(this.tabGroups)) {
|
|
this.changeTab(tab, group, { force: true });
|
|
}
|
|
}
|
|
};
|
|
|
|
// src/ui/sheets/items/item.js
|
|
var CDEItemSheet = class extends CDEBaseItemSheet {
|
|
static DEFAULT_OPTIONS = {
|
|
classes: ["equipment"],
|
|
position: { width: 620, height: 580 }
|
|
};
|
|
static PARTS = {
|
|
main: { template: "systems/fvtt-chroniques-de-l-etrange/templates/item/cde-item-sheet.html" }
|
|
};
|
|
async _prepareContext() {
|
|
const context = await super._prepareContext();
|
|
context.subtypes = SUBTYPES;
|
|
context.isWeapon = this.document.isWeapon;
|
|
context.isArmor = this.document.isArmor;
|
|
context.isSanhei = this.document.isSanhei;
|
|
context.isOther = this.document.isOther;
|
|
return context;
|
|
}
|
|
};
|
|
|
|
// src/ui/sheets/items/kungfu.js
|
|
var CDEKungfuSheet = class extends CDEBaseItemSheet {
|
|
static DEFAULT_OPTIONS = {
|
|
classes: ["kungfu"],
|
|
position: { width: 720, height: 680 }
|
|
};
|
|
static PARTS = {
|
|
main: { template: "systems/fvtt-chroniques-de-l-etrange/templates/item/cde-kungfu-sheet.html" }
|
|
};
|
|
async _prepareContext() {
|
|
const context = await super._prepareContext();
|
|
const techniques = this.document.system.techniques ?? {};
|
|
const enrich = (value) => foundry.applications.ux.TextEditor.implementation.enrichHTML(value ?? "", { async: true });
|
|
context.descriptionTechnique1HTML = await enrich(techniques.technique1?.technique);
|
|
context.descriptionTechnique2HTML = await enrich(techniques.technique2?.technique);
|
|
context.descriptionTechnique3HTML = await enrich(techniques.technique3?.technique);
|
|
return context;
|
|
}
|
|
};
|
|
|
|
// src/ui/sheets/items/spell.js
|
|
var CDESpellSheet = class extends CDEBaseItemSheet {
|
|
static DEFAULT_OPTIONS = {
|
|
classes: ["spell"],
|
|
position: { width: 660, height: 680 }
|
|
};
|
|
static PARTS = {
|
|
main: { template: "systems/fvtt-chroniques-de-l-etrange/templates/item/cde-spell-sheet.html" }
|
|
};
|
|
async _prepareContext() {
|
|
const context = await super._prepareContext();
|
|
const enrich = (content) => foundry.applications.ux.TextEditor.implementation.enrichHTML(content ?? "", { async: true });
|
|
context.spellDescriptionHTML = await enrich(this.document.system.description);
|
|
context.componentsDescriptionHTML = await enrich(this.document.system.components);
|
|
context.effectsDescriptionHTML = await enrich(this.document.system.effects);
|
|
context.examplesDescriptionHTML = await enrich(this.document.system.examples);
|
|
return context;
|
|
}
|
|
};
|
|
|
|
// src/ui/sheets/items/supernatural.js
|
|
var CDESupernaturalSheet = class extends CDEBaseItemSheet {
|
|
static DEFAULT_OPTIONS = {
|
|
classes: ["supernatural"],
|
|
position: { width: 560, height: 520 }
|
|
};
|
|
static PARTS = {
|
|
main: { template: "systems/fvtt-chroniques-de-l-etrange/templates/item/cde-supernatural-sheet.html" }
|
|
};
|
|
};
|
|
|
|
// src/migration.js
|
|
var MIGRATION_VERSION = "3.0.0";
|
|
function registerSettings() {
|
|
game.settings.register(SYSTEM_ID, "migrationVersion", {
|
|
name: "Migration version",
|
|
scope: "world",
|
|
config: false,
|
|
type: String,
|
|
default: "0.0.0"
|
|
});
|
|
}
|
|
async function migrateIfNeeded() {
|
|
const current = game.system.version ?? MIGRATION_VERSION;
|
|
const stored = game.settings.get(SYSTEM_ID, "migrationVersion") ?? "0.0.0";
|
|
if (!isNewerVersion(current, stored)) return;
|
|
ui.notifications.info(`CHRONIQUESDELETRANGE | Migration vers ${current} en cours...`, { permanent: true });
|
|
await migrateActors();
|
|
await migrateItems();
|
|
await migrateCompendiumActors();
|
|
await migrateCompendiumItems();
|
|
await game.settings.set(SYSTEM_ID, "migrationVersion", current);
|
|
ui.notifications.info(`CHRONIQUESDELETRANGE | Migration vers ${current} termin\xE9e.`);
|
|
}
|
|
async function migrateActors() {
|
|
const updates = [];
|
|
for (const actor of game.actors.contents) {
|
|
const updateData = migrateActorData(actor);
|
|
if (Object.keys(updateData).length > 0) {
|
|
updates.push(actor.update(updateData, { enforceTypes: false }));
|
|
}
|
|
}
|
|
await Promise.all(updates);
|
|
}
|
|
async function migrateCompendiumActors() {
|
|
const packs = game.packs.filter((p) => p.documentName === "Actor" && p.metadata.system === SYSTEM_ID);
|
|
for (const pack of packs) {
|
|
const content = await pack.getDocuments();
|
|
for (const actor of content) {
|
|
const updateData = migrateActorData(actor);
|
|
if (Object.keys(updateData).length > 0) {
|
|
await actor.update(updateData, { pack: pack.collection, enforceTypes: false });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
async function migrateItems() {
|
|
const updates = [];
|
|
for (const item of game.items.contents) {
|
|
const updateData = migrateItemData(item);
|
|
if (Object.keys(updateData).length > 0) {
|
|
updates.push(item.update(updateData, { enforceTypes: false }));
|
|
}
|
|
}
|
|
await Promise.all(updates);
|
|
}
|
|
async function migrateCompendiumItems() {
|
|
const packs = game.packs.filter((p) => p.documentName === "Item" && p.metadata.system === SYSTEM_ID);
|
|
for (const pack of packs) {
|
|
const content = await pack.getDocuments();
|
|
for (const item of content) {
|
|
const updateData = migrateItemData(item);
|
|
if (Object.keys(updateData).length > 0) {
|
|
await item.update(updateData, { pack: pack.collection, enforceTypes: false });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function migrateActorData(actor) {
|
|
const updateData = {};
|
|
const system = actor.system ?? {};
|
|
const actorType = actor.type;
|
|
const legacyMagic = system.magics?.masteryofthway;
|
|
if (legacyMagic && !system.magics?.masteryoftheway) {
|
|
updateData["system.magics.masteryoftheway"] = legacyMagic;
|
|
updateData["system.magics.-=masteryofthway"] = null;
|
|
}
|
|
if ((actorType === "character" || actorType === "npc") && !system.prefs?.typeofthrow) {
|
|
const defaultCheck = actorType === "character";
|
|
updateData["system.prefs.typeofthrow"] = { check: defaultCheck, choice: "0" };
|
|
}
|
|
return updateData;
|
|
}
|
|
function migrateItemData(item) {
|
|
const updateData = {};
|
|
const system = item.system ?? {};
|
|
return updateData;
|
|
}
|
|
|
|
// src/system.js
|
|
Hooks.once("i18nInit", preLocalizeConfig);
|
|
Hooks.once("init", async () => {
|
|
console.info(`CHRONIQUESDELETRANGE | Initializing ${SYSTEM_ID}`);
|
|
registerSettings();
|
|
game.system.CONST = { MAGICS, SUBTYPES };
|
|
CONFIG.Actor.systemDataModels = {
|
|
[ACTOR_TYPES.character]: CharacterDataModel,
|
|
[ACTOR_TYPES.npc]: NpcDataModel,
|
|
[ACTOR_TYPES.tinji]: TinjiDataModel,
|
|
[ACTOR_TYPES.loksyu]: LoksyuDataModel
|
|
};
|
|
CONFIG.Item.systemDataModels = {
|
|
[ITEM_TYPES.item]: EquipmentDataModel,
|
|
[ITEM_TYPES.kungfu]: KungfuDataModel,
|
|
[ITEM_TYPES.spell]: SpellDataModel,
|
|
[ITEM_TYPES.supernatural]: SupernaturalDataModel
|
|
};
|
|
CONFIG.Actor.documentClass = CDEActor;
|
|
CONFIG.Item.documentClass = CDEItem;
|
|
CONFIG.ChatMessage.documentClass = CDEMessage;
|
|
configureRuntime();
|
|
DocumentSheetConfig.unregisterSheet(Actor, "core", ActorSheet);
|
|
DocumentSheetConfig.unregisterSheet(Item, "core", ItemSheet);
|
|
DocumentSheetConfig.registerSheet(Actor, SYSTEM_ID, CDECharacterSheet, {
|
|
types: [ACTOR_TYPES.character],
|
|
makeDefault: true,
|
|
label: "CDE Character Sheet (V2)"
|
|
});
|
|
DocumentSheetConfig.registerSheet(Actor, SYSTEM_ID, CDENpcSheet, {
|
|
types: [ACTOR_TYPES.npc],
|
|
makeDefault: true,
|
|
label: "CDE NPC Sheet (V2)"
|
|
});
|
|
DocumentSheetConfig.registerSheet(Actor, SYSTEM_ID, CDETinjiSheet, {
|
|
types: [ACTOR_TYPES.tinji],
|
|
makeDefault: true,
|
|
label: "CDE Tinji Sheet (V2)"
|
|
});
|
|
DocumentSheetConfig.registerSheet(Actor, SYSTEM_ID, CDELoksyuSheet, {
|
|
types: [ACTOR_TYPES.loksyu],
|
|
makeDefault: true,
|
|
label: "CDE Loksyu Sheet (V2)"
|
|
});
|
|
DocumentSheetConfig.registerSheet(Item, SYSTEM_ID, CDEItemSheet, {
|
|
types: [ITEM_TYPES.item],
|
|
makeDefault: true,
|
|
label: "CDE Item Sheet (V2)"
|
|
});
|
|
DocumentSheetConfig.registerSheet(Item, SYSTEM_ID, CDEKungfuSheet, {
|
|
types: [ITEM_TYPES.kungfu],
|
|
makeDefault: true,
|
|
label: "CDE KungFu Sheet (V2)"
|
|
});
|
|
DocumentSheetConfig.registerSheet(Item, SYSTEM_ID, CDESpellSheet, {
|
|
types: [ITEM_TYPES.spell],
|
|
makeDefault: true,
|
|
label: "CDE Spell Sheet (V2)"
|
|
});
|
|
DocumentSheetConfig.registerSheet(Item, SYSTEM_ID, CDESupernaturalSheet, {
|
|
types: [ITEM_TYPES.supernatural],
|
|
makeDefault: true,
|
|
label: "CDE Supernatural Sheet (V2)"
|
|
});
|
|
await preloadPartials();
|
|
registerHandlebarsHelpers();
|
|
registerDice();
|
|
Hooks.on("renderSettings", (_app, html) => injectCompendiumLink(html));
|
|
console.info(`CHRONIQUESDELETRANGE | Initialized`);
|
|
});
|
|
Hooks.once("ready", async () => {
|
|
if (!game.modules.get("lib-wrapper")?.active && game.user.isGM) {
|
|
ui.notifications.error("System fvtt-chroniques-de-l-etrange requires the 'libWrapper' module. Please install and activate it.");
|
|
}
|
|
await migrateIfNeeded();
|
|
});
|
|
function injectCompendiumLink(html) {
|
|
const header = html[0]?.querySelector?.("h4.divider");
|
|
if (!header) return;
|
|
const section = document.createElement("section");
|
|
section.classList.add("settings", "flexcol");
|
|
section.innerHTML = `
|
|
<section class="links flexcol">
|
|
<img class="logo-info" src="systems/fvtt-chroniques-de-l-etrange/images/logo_jeu.png" />
|
|
<h4 class="divider"> Lien utile <i class="fa-light fa-up-right-from-square"></i> </h4>
|
|
</section>
|
|
<section class="settings flexcol">
|
|
<button type="button" data-action="open-cde-link">
|
|
<i class="fa fa-download"></i> Compendium pour Les CdE <i class="fa-light fa-up-right-from-square"></i>
|
|
</button>
|
|
<details>
|
|
<summary><small>Guide d'installation</small></summary>
|
|
<small style="text-align: center;">
|
|
<p>Rendez-vous sur le site de l'\xE9diteur, t\xE9l\xE9chargez les PDF contenant les liens vers les compendia, puis ajoutez leurs manifestes dans Foundry.</p>
|
|
</small>
|
|
</details>
|
|
</section>
|
|
`;
|
|
section.querySelector("button[data-action='open-cde-link']")?.addEventListener("click", () => {
|
|
window.open("https://antre-monde.com/les-chroniques-de-letrengae/", "_blank");
|
|
});
|
|
header.parentNode.insertBefore(section, header);
|
|
}
|
|
//# sourceMappingURL=system.js.map
|