feat: portée armes, récupération 3T, socket loksyu, import batch
Portée des armes - Suppression des malus de portée (mediane/longue/extreme) - Sélecteur filtré : seules les portées <= portée max de l'arme Récupération (3 Trésors) - Nouveau bouton dans l'onglet Trésors - Jet basé sur l'aspect du Gardien Céleste - Dé pair : +1 Hei Yang, +1 San - Dé impair : +1 Hei Yin, +1 Zing - Fenêtre de bonus dice pour les pouvoirs (Acupuncture...) - Ignore les malus de palier San/Zing Socket Loksyu/TinJi - Les joueurs peuvent mettre à jour Loksyu/TinJi via socket - Le MJ proxy les écritures (game.settings world = GM only) - Opérations atomiques pour éviter les races conditions (roll simultanés) Import batch - 4 nouveaux migrators item (weapon/armor/sanhei/ingredient) - magicOrder dans la migration character - Confirmation en 2 clics avant l'import - Détection des doublons avec le monde - Progression en direct pendant l'import - Dédoublonnage non-silencieux + flag _duplicate calculé en direct UI - Noms d'items en Signika (plus lisible qu'Averia) - ASCII art au lancement dans la console - Clés i18n manquantes ajoutées (BonusMalus, EnablePrompt, Material, Roll, ThrowType) Fix divers - Calcul puissance sort : utilise l'élément de la spécialité, pas celui de l'école - preLocalizeConfig préserve labelelementkey pour la résolution d'aspect - combat.js: guard undefined ids dans rollInitiative
This commit is contained in:
Vendored
+114
-19
@@ -804,15 +804,6 @@ function preLocalizeConfig() {
|
||||
|
||||
// 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
|
||||
@@ -1682,12 +1673,13 @@ var LABELELEMENT_TO_ASPECT = {
|
||||
"CDE.Fire": "fire",
|
||||
"CDE.Wood": "wood"
|
||||
};
|
||||
var RANGE_MALUS = {
|
||||
contact: 0,
|
||||
courte: 0,
|
||||
mediane: -1,
|
||||
longue: -2,
|
||||
extreme: -3
|
||||
var RANGE_ORDER = ["contact", "courte", "mediane", "longue", "extreme"];
|
||||
var RANGE_LABELS = {
|
||||
contact: "CDE.RangeContact",
|
||||
courte: "CDE.RangeCourte",
|
||||
mediane: "CDE.RangeMediane",
|
||||
longue: "CDE.RangeLongue",
|
||||
extreme: "CDE.RangeExtreme"
|
||||
};
|
||||
var WEAPON_TYPE_SKILL = {
|
||||
melee: "kungfu",
|
||||
@@ -1787,6 +1779,13 @@ async function showMagicPrompt(params) {
|
||||
});
|
||||
}
|
||||
async function showWeaponPrompt(params) {
|
||||
const maxRange = params.effectiverange ?? "contact";
|
||||
const maxIdx = RANGE_ORDER.indexOf(maxRange);
|
||||
const validRanges = RANGE_ORDER.slice(0, maxIdx + 1).map((r) => ({
|
||||
value: r,
|
||||
label: RANGE_LABELS[r],
|
||||
selected: r === maxRange
|
||||
}));
|
||||
return showRollPrompt({
|
||||
title: params.title,
|
||||
template: WEAPON_PROMPT_TEMPLATE,
|
||||
@@ -1800,6 +1799,7 @@ async function showWeaponPrompt(params) {
|
||||
weaponskill: params.weaponskill ?? "kungfu",
|
||||
aspect: Number(params.aspect ?? 0),
|
||||
effectiverange: params.effectiverange ?? "contact",
|
||||
validRanges,
|
||||
bonusmalus: params.bonusmalus ?? 0,
|
||||
woundmalus: params.woundmalus ?? 0,
|
||||
bonusauspiciousdice: params.bonusauspiciousdice ?? 0,
|
||||
@@ -1956,14 +1956,13 @@ async function rollForActor(actor, rollKey) {
|
||||
const wpSkillDice = sys.skills?.[wpChosenSkill]?.value ?? 0;
|
||||
const wpAspFinal = Number(wParams.aspect ?? wpAspectIdx);
|
||||
const wpAspectDice = sys.aspect[ASPECT_NAMES[wpAspFinal]]?.value ?? 0;
|
||||
const wpRangeMalus = RANGE_MALUS[wParams.effectiverange ?? "contact"] ?? 0;
|
||||
const wpBonusMalus = Number(wParams.bonusmalus ?? 0);
|
||||
const wpWoundMalus = Number(wParams.woundmalus ?? 0);
|
||||
const wpBonusAusp = Number(wParams.bonusauspiciousdice ?? 0);
|
||||
const wpThrowMode = Number(wParams.typeofthrow ?? 0);
|
||||
const wpDamageBase = wpItem.system.damageBase ?? 0;
|
||||
const wpSpecialtyBonus = wpItem.system.hasSpeciality ? 1 : 0;
|
||||
const wpTotalDice = wpSkillDice + wpAspectDice + wpRangeMalus + wpBonusMalus - wpWoundMalus + wpSpecialtyBonus;
|
||||
const wpTotalDice = wpSkillDice + wpAspectDice + wpBonusMalus - wpWoundMalus + wpSpecialtyBonus;
|
||||
if (wpTotalDice <= 0) {
|
||||
ui.notifications.warn(game.i18n.localize("CDE.Error0"));
|
||||
return;
|
||||
@@ -1975,7 +1974,6 @@ async function rollForActor(actor, rollKey) {
|
||||
const wpResults = computeWuXingResults(wpFaces, wpAspectName, wpBonusAusp);
|
||||
if (!wpResults) return;
|
||||
const wpModParts = [];
|
||||
if (wpRangeMalus !== 0) wpModParts.push(`${wpRangeMalus} ${game.i18n.localize("CDE.RangePenalty")}`);
|
||||
if (wpBonusMalus !== 0) wpModParts.push(`${wpBonusMalus > 0 ? "+" : ""}${wpBonusMalus} ${game.i18n.localize("CDE.BonusMalus")}`);
|
||||
if (wpWoundMalus !== 0) wpModParts.push(`-${wpWoundMalus} ${game.i18n.localize("CDE.WoundMalus")}`);
|
||||
if (wpBonusAusp !== 0) wpModParts.push(`+${wpBonusAusp} ${game.i18n.localize("CDE.BonusAuspiciousDice")}`);
|
||||
@@ -2277,7 +2275,8 @@ var CDECharacterSheet = class _CDECharacterSheet extends CDEBaseActorSheet {
|
||||
classes: ["character"],
|
||||
actions: {
|
||||
moveMagicUp: _CDECharacterSheet.#onMoveMagicUp,
|
||||
moveMagicDown: _CDECharacterSheet.#onMoveMagicDown
|
||||
moveMagicDown: _CDECharacterSheet.#onMoveMagicDown,
|
||||
recovery: _CDECharacterSheet.#onRecovery
|
||||
}
|
||||
};
|
||||
static PARTS = {
|
||||
@@ -2447,6 +2446,102 @@ var CDECharacterSheet = class _CDECharacterSheet extends CDEBaseActorSheet {
|
||||
[order[idx], order[idx + 1]] = [order[idx + 1], order[idx]];
|
||||
await this.document.update({ "system.magicOrder": order });
|
||||
}
|
||||
static async #onRecovery(event, target) {
|
||||
const actor = this.document;
|
||||
const sys = actor.system;
|
||||
const guardian = Number(sys.guardian ?? 0);
|
||||
if (guardian < 1 || guardian > 5) {
|
||||
ui.notifications.warn(game.i18n.localize("CDE.RecoveryNoGuardian"));
|
||||
return;
|
||||
}
|
||||
const aspectName = ASPECT_NAMES[guardian - 1];
|
||||
const aspectLabel = game.i18n.localize(ASPECT_LABELS[aspectName]);
|
||||
const aspectValue = sys.aspect?.[aspectName]?.value ?? 0;
|
||||
if (aspectValue <= 0) {
|
||||
ui.notifications.warn(game.i18n.localize("CDE.RecoveryNoAspect"));
|
||||
return;
|
||||
}
|
||||
const RECOVERY_PROMPT = `systems/${SYSTEM_ID}/templates/form/cde-recovery-prompt.html`;
|
||||
const content = await foundry.applications.handlebars.renderTemplate(RECOVERY_PROMPT, {
|
||||
basedice: aspectValue
|
||||
});
|
||||
const params = await foundry.applications.api.DialogV2.prompt({
|
||||
window: { title: game.i18n.localize("CDE.Recovery") },
|
||||
content,
|
||||
rejectClose: false,
|
||||
ok: {
|
||||
label: game.i18n.localize("CDE.Validate"),
|
||||
callback: (event2, button, dialog) => {
|
||||
const root = dialog.element ?? dialog;
|
||||
return { bonusdice: Number(root.querySelector("[name=bonusdice]")?.value ?? 0) };
|
||||
}
|
||||
}
|
||||
});
|
||||
if (params === void 0) return;
|
||||
const totalDice = aspectValue + Math.max(0, Number(params.bonusdice ?? 0));
|
||||
const roll = new Roll(`${totalDice}d10`);
|
||||
await roll.evaluate();
|
||||
const results = roll.dice[0].results.map((r) => r.result);
|
||||
const evenCount = results.filter((r) => r % 2 === 0).length;
|
||||
const oddCount = results.filter((r) => r % 2 !== 0).length;
|
||||
const cap = (max) => max > 0 ? max : Infinity;
|
||||
const heiYang = Math.min(
|
||||
(sys.threetreasures?.heiyang?.value ?? 0) + evenCount,
|
||||
cap(sys.threetreasures?.heiyang?.max)
|
||||
);
|
||||
const heiYin = Math.min(
|
||||
(sys.threetreasures?.heiyin?.value ?? 0) + oddCount,
|
||||
cap(sys.threetreasures?.heiyin?.max)
|
||||
);
|
||||
const san = Math.min(
|
||||
(sys.threetreasures?.dicelevel?.level0d?.san?.value ?? 0) + evenCount,
|
||||
cap(sys.threetreasures?.dicelevel?.level0d?.san?.max)
|
||||
);
|
||||
const zing = Math.min(
|
||||
(sys.threetreasures?.dicelevel?.level0d?.zing?.value ?? 0) + oddCount,
|
||||
cap(sys.threetreasures?.dicelevel?.level0d?.zing?.max)
|
||||
);
|
||||
await actor.update({
|
||||
"system.threetreasures.heiyang.value": heiYang,
|
||||
"system.threetreasures.heiyin.value": heiYin,
|
||||
"system.threetreasures.dicelevel.level0d.san.value": san,
|
||||
"system.threetreasures.dicelevel.level0d.zing.value": zing
|
||||
});
|
||||
const rollLabel = `${game.i18n.localize("CDE.Recovery")} \u2014 ${aspectLabel}`;
|
||||
const diceStr = results.join(" \xB7 ");
|
||||
const typeOfThrow = Number(sys.prefs?.typeofthrow?.choice ?? 0);
|
||||
const ROLL_MODES2 = ["roll", "gmroll", "blindroll", "selfroll"];
|
||||
const rollMode = ROLL_MODES2[typeOfThrow] ?? "roll";
|
||||
await ChatMessage.create({
|
||||
user: game.user.id,
|
||||
speaker: ChatMessage.getSpeaker({ actor }),
|
||||
content: `
|
||||
<div class="cde-chat-recovery">
|
||||
<div class="cde-chat-recovery-header">
|
||||
<img src="${ASPECT_ICONS[aspectName]}" class="cde-chat-recovery-icon" alt="${aspectLabel}"/>
|
||||
<span class="cde-chat-recovery-title">${rollLabel}</span>
|
||||
</div>
|
||||
<div class="cde-chat-recovery-dice">
|
||||
<span class="cde-chat-recovery-dice-label">${totalDice}d10</span>
|
||||
<span class="cde-chat-recovery-dice-results">${diceStr}</span>
|
||||
</div>
|
||||
<div class="cde-chat-recovery-results">
|
||||
<div class="cde-chat-recovery-even">
|
||||
<span class="cde-chat-recovery-count">${evenCount}</span>
|
||||
<span><strong>${game.i18n.localize("CDE.RecoveryEvenLabel")}</strong></span>
|
||||
<span>\u2192 +${evenCount} Hei Yang, +${evenCount} San</span>
|
||||
</div>
|
||||
<div class="cde-chat-recovery-odd">
|
||||
<span class="cde-chat-recovery-count">${oddCount}</span>
|
||||
<span><strong>${game.i18n.localize("CDE.RecoveryOddLabel")}</strong></span>
|
||||
<span>\u2192 +${oddCount} Hei Yin, +${oddCount} Zing</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>`,
|
||||
rolls: [roll],
|
||||
rollMode
|
||||
});
|
||||
}
|
||||
#bindComponentRandomize() {
|
||||
const btn = this.element?.querySelector("[data-action='randomize-component']");
|
||||
if (!btn) return;
|
||||
|
||||
Vendored
+3
-3
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user