Fix roll dialog CSS + JS: template <div> wrapper, moon-section, selectors
- Remplace <form class='roll-dialog celestopol'> par <div class='roll-dialog-content'>
pour éviter les formulaires HTML imbriqués invalides (DialogV2 a son propre <form>)
- Corrige le sélecteur CSS de .roll-dialog.celestopol vers .application.roll-dialog .roll-dialog-content
- Remplace .form-group.form-moon par .moon-section (classe custom) pour éviter
les conflits avec le CSS grid de FoundryVTT standard-form (label 130px de hauteur)
- Met à jour le script JS inline pour utiliser document.querySelector('.roll-dialog-content')
- Ajoute white-space: nowrap sur le label Destin pour éviter le wrapping sur 3 lignes
- Supprime .application.roll-dialog .window-content padding override (remplacé par dialog-content)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -50,9 +50,28 @@ export default class CelestopolActorSheet extends HandlebarsApplicationMixin(fou
|
||||
this.element.querySelectorAll(".rollable").forEach(el => {
|
||||
el.addEventListener("click", this._onRoll.bind(this))
|
||||
})
|
||||
|
||||
// Setup sequential checkbox logic for wound tracks
|
||||
this._setupSequentialCheckboxes()
|
||||
|
||||
// Setup sequential checkbox logic for factions
|
||||
this._setupFactionCheckboxes()
|
||||
}
|
||||
|
||||
/** @override */
|
||||
_onClick(event) {
|
||||
// Skip checkbox clicks in edit mode
|
||||
if (this.isEditMode && event.target.classList.contains('skill-level-checkbox')) {
|
||||
return
|
||||
}
|
||||
super._onClick(event)
|
||||
}
|
||||
|
||||
async _onRoll(event) {
|
||||
// Don't roll if clicking on a checkbox
|
||||
if (event.target.classList.contains('skill-level-checkbox')) {
|
||||
return
|
||||
}
|
||||
if (!this.isPlayMode) return
|
||||
const el = event.currentTarget
|
||||
const statId = el.dataset.statId
|
||||
@@ -92,13 +111,12 @@ export default class CelestopolActorSheet extends HandlebarsApplicationMixin(fou
|
||||
await this.document.createEmbeddedDocuments("Item", [item.toObject()], { renderSheet: false })
|
||||
}
|
||||
|
||||
static async #onEditImage(event, target) {
|
||||
const attr = target.dataset.edit
|
||||
const current = foundry.utils.getProperty(this.document, attr)
|
||||
static async #onEditImage(event, _target) {
|
||||
const current = this.document.img
|
||||
const fp = new FilePicker({
|
||||
current,
|
||||
type: "image",
|
||||
callback: (path) => this.document.update({ [attr]: path }),
|
||||
callback: (path) => this.document.update({ img: path }),
|
||||
top: this.position.top + 40,
|
||||
left: this.position.left + 10,
|
||||
})
|
||||
@@ -123,4 +141,133 @@ export default class CelestopolActorSheet extends HandlebarsApplicationMixin(fou
|
||||
const item = await fromUuid(uuid)
|
||||
await item?.deleteDialog()
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup sequential checkbox logic for wound/destin/spleen tracks
|
||||
* Only allows checking the next checkbox in sequence
|
||||
*/
|
||||
_setupSequentialCheckboxes() {
|
||||
this.element.querySelectorAll('.wound-checkbox').forEach(checkbox => {
|
||||
checkbox.addEventListener('change', (event) => {
|
||||
this._handleSequentialCheckboxChange(event)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle sequential checkbox change logic
|
||||
* @param {Event} event - The change event
|
||||
*/
|
||||
_handleSequentialCheckboxChange(event) {
|
||||
const checkbox = event.target
|
||||
if (!checkbox.classList.contains('wound-checkbox') || checkbox.disabled) return
|
||||
|
||||
const track = checkbox.dataset.track
|
||||
const currentIndex = parseInt(checkbox.dataset.index)
|
||||
const isChecked = checkbox.checked
|
||||
|
||||
// Get all checkboxes in this track
|
||||
const trackCheckboxes = Array.from(this.element.querySelectorAll(`.wound-checkbox[data-track="${track}"]`))
|
||||
|
||||
if (isChecked) {
|
||||
// Checking a box: uncheck all boxes after this one
|
||||
for (let i = currentIndex + 1; i < trackCheckboxes.length; i++) {
|
||||
trackCheckboxes[i].checked = false
|
||||
}
|
||||
// Check all boxes before this one
|
||||
for (let i = 0; i < currentIndex; i++) {
|
||||
trackCheckboxes[i].checked = true
|
||||
}
|
||||
} else {
|
||||
// Unchecking a box: uncheck all boxes after this one
|
||||
for (let i = currentIndex; i < trackCheckboxes.length; i++) {
|
||||
trackCheckboxes[i].checked = false
|
||||
}
|
||||
}
|
||||
|
||||
// Update the visual state
|
||||
this._updateTrackVisualState()
|
||||
}
|
||||
|
||||
/**
|
||||
* Update visual state of track boxes based on checkbox states
|
||||
*/
|
||||
_updateTrackVisualState() {
|
||||
this.element.querySelectorAll('.track-box').forEach(box => {
|
||||
const checkbox = box.querySelector('.wound-checkbox')
|
||||
if (checkbox) {
|
||||
if (checkbox.checked) {
|
||||
box.classList.add('checked')
|
||||
} else {
|
||||
box.classList.remove('checked')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup sequential checkbox logic for faction tracks
|
||||
*/
|
||||
_setupFactionCheckboxes() {
|
||||
this.element.querySelectorAll('.faction-checkbox').forEach(checkbox => {
|
||||
checkbox.addEventListener('change', (event) => {
|
||||
this._handleFactionCheckboxChange(event)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle faction checkbox change logic
|
||||
* @param {Event} event - The change event
|
||||
*/
|
||||
_handleFactionCheckboxChange(event) {
|
||||
const checkbox = event.target
|
||||
if (!checkbox.classList.contains('faction-checkbox') || checkbox.disabled) return
|
||||
|
||||
const factionId = checkbox.dataset.faction
|
||||
const currentLevel = parseInt(checkbox.dataset.level)
|
||||
const isChecked = checkbox.checked
|
||||
|
||||
// Get all checkboxes for this faction
|
||||
const factionCheckboxes = Array.from(this.element.querySelectorAll(`.faction-checkbox[data-faction="${factionId}"]`))
|
||||
|
||||
if (isChecked) {
|
||||
// Checking a box: check all boxes before this one, uncheck all boxes after this one
|
||||
for (let i = 0; i < currentLevel; i++) {
|
||||
factionCheckboxes[i].checked = true
|
||||
}
|
||||
for (let i = currentLevel; i < factionCheckboxes.length; i++) {
|
||||
factionCheckboxes[i].checked = false
|
||||
}
|
||||
} else {
|
||||
// Unchecking a box: uncheck all boxes after this one
|
||||
for (let i = currentLevel - 1; i < factionCheckboxes.length; i++) {
|
||||
factionCheckboxes[i].checked = false
|
||||
}
|
||||
}
|
||||
|
||||
// Update the count display
|
||||
this._updateFactionCount(factionId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the faction count display based on checked checkboxes
|
||||
* @param {string} factionId - The faction ID
|
||||
*/
|
||||
_updateFactionCount(factionId) {
|
||||
const checkboxes = Array.from(this.element.querySelectorAll(`.faction-checkbox[data-faction="${factionId}"]:checked`))
|
||||
const count = checkboxes.length
|
||||
|
||||
// Update the hidden input field
|
||||
const input = this.element.querySelector(`input[name="system.factions.${factionId}.value"]`)
|
||||
if (input) {
|
||||
input.value = count
|
||||
}
|
||||
|
||||
// Update the visual count display
|
||||
const countDisplay = this.element.querySelector(`.faction-row[data-faction="${factionId}"] .faction-count`)
|
||||
if (countDisplay) {
|
||||
countDisplay.textContent = count
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,16 +39,16 @@ export default class CelestopolItemSheet extends HandlebarsApplicationMixin(foun
|
||||
})
|
||||
}
|
||||
|
||||
static async #onEditImage(event, target) {
|
||||
const attr = target.dataset.edit
|
||||
const current = foundry.utils.getProperty(this.document, attr)
|
||||
static async #onEditImage(event, _target) {
|
||||
const current = this.document.img
|
||||
const fp = new FilePicker({
|
||||
current,
|
||||
type: "image",
|
||||
callback: (path) => this.document.update({ [attr]: path }),
|
||||
callback: (path) => this.document.update({ img: path }),
|
||||
top: this.position.top + 40,
|
||||
left: this.position.left + 10,
|
||||
})
|
||||
return fp.browse()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ export const ASCII = `
|
||||
░░░░░░░░░░░░░░░░░░1922░░░░░░░░░░░░░░░░░░░
|
||||
`
|
||||
|
||||
/** Les 4 attributs principaux (stats). Chacun a une résistance (res) et 4 compétences. */
|
||||
/** Les 4 attributs principaux (stats). Chacun a une résistance (res) et 4 domaines. */
|
||||
export const STATS = {
|
||||
ame: { id: "ame", label: "CELESTOPOL.Stat.ame" },
|
||||
corps: { id: "corps", label: "CELESTOPOL.Stat.corps" },
|
||||
@@ -15,7 +15,7 @@ export const STATS = {
|
||||
esprit: { id: "esprit", label: "CELESTOPOL.Stat.esprit" },
|
||||
}
|
||||
|
||||
/** Compétences groupées par attribut. */
|
||||
/** Domaines groupées par attribut. */
|
||||
export const SKILLS = {
|
||||
ame: {
|
||||
artifice: { id: "artifice", label: "CELESTOPOL.Skill.artifice", stat: "ame" },
|
||||
@@ -43,7 +43,7 @@ export const SKILLS = {
|
||||
},
|
||||
}
|
||||
|
||||
/** Liste plate de toutes les compétences (utile pour les DataModels d'items). */
|
||||
/** Liste plate de tous les domaines (utile pour les DataModels d'items). */
|
||||
export const ALL_SKILLS = Object.values(SKILLS).flatMap(group => Object.values(group))
|
||||
|
||||
/** Types d'anomalies (pouvoirs paranormaux). */
|
||||
|
||||
@@ -1,24 +1,40 @@
|
||||
import { SYSTEM } from "../config/system.mjs"
|
||||
|
||||
/** Symboles Unicode pour chaque phase de lune. */
|
||||
const MOON_SYMBOLS = {
|
||||
none: "☽",
|
||||
nouvellelune: "🌑",
|
||||
premiercroissant: "🌒",
|
||||
premierquartier: "🌓",
|
||||
lunegibbeuse: "🌔",
|
||||
lunevoutee: "🌕",
|
||||
derniercroissant: "🌖",
|
||||
dernierquartier: "🌗",
|
||||
pleinelune: "🌕",
|
||||
}
|
||||
|
||||
/**
|
||||
* Système de dés de Célestopol 1922.
|
||||
*
|
||||
* Le jet de base est : (valeur compétence)d6 comparé à un seuil de difficulté.
|
||||
* Le dé de lune ajoute un bonus selon la phase actuelle.
|
||||
* Destin et Spleen modifient le nombre de dés.
|
||||
* Le jet de base est : (valeur domaine + malus blessures)d6 + bonus lune + modificateurs
|
||||
* comparé à un seuil de difficulté.
|
||||
* - Succès critique : marge ≥ 5
|
||||
* - Échec critique : marge ≤ −5
|
||||
*/
|
||||
export class CelestopolRoll extends Roll {
|
||||
static CHAT_TEMPLATE = "systems/fvtt-celestopol/templates/chat-message.hbs"
|
||||
|
||||
get resultType() { return this.options.resultType }
|
||||
get isSuccess() { return this.resultType === "success" }
|
||||
get isFailure() { return this.resultType === "failure" }
|
||||
get actorId() { return this.options.actorId }
|
||||
get actorName() { return this.options.actorName }
|
||||
get actorImage() { return this.options.actorImage }
|
||||
get skillLabel() { return this.options.skillLabel }
|
||||
get difficulty() { return this.options.difficulty }
|
||||
get moonBonus() { return this.options.moonBonus ?? 0 }
|
||||
get resultType() { return this.options.resultType }
|
||||
get isSuccess() { return this.resultType === "success" || this.resultType === "critical-success" }
|
||||
get isFailure() { return this.resultType === "failure" || this.resultType === "critical-failure" }
|
||||
get isCriticalSuccess(){ return this.resultType === "critical-success" }
|
||||
get isCriticalFailure(){ return this.resultType === "critical-failure" }
|
||||
get actorId() { return this.options.actorId }
|
||||
get actorName() { return this.options.actorName }
|
||||
get actorImage() { return this.options.actorImage }
|
||||
get skillLabel() { return this.options.skillLabel }
|
||||
get difficulty() { return this.options.difficulty }
|
||||
get moonBonus() { return this.options.moonBonus ?? 0 }
|
||||
|
||||
/**
|
||||
* Ouvre le dialogue de configuration du jet via DialogV2 et exécute le jet.
|
||||
@@ -26,26 +42,33 @@ export class CelestopolRoll extends Roll {
|
||||
* @returns {Promise<CelestopolRoll|null>}
|
||||
*/
|
||||
static async prompt(options = {}) {
|
||||
const rollModes = foundry.utils.duplicate(CONFIG.Dice.rollModes)
|
||||
const fieldRollMode = new foundry.data.fields.StringField({
|
||||
choices: rollModes,
|
||||
blank: false,
|
||||
default: "publicroll",
|
||||
})
|
||||
const woundMalus = options.woundMalus ?? 0
|
||||
const baseSkillVal = options.skillValue ?? 0
|
||||
const nbDiceBase = Math.max(1, baseSkillVal + woundMalus)
|
||||
const woundLevelId = options.woundLevel ?? 0
|
||||
const woundLabel = woundLevelId > 0
|
||||
? game.i18n.localize(SYSTEM.WOUND_LEVELS[woundLevelId]?.label ?? "")
|
||||
: null
|
||||
|
||||
// Construire les phases lune avec symboles
|
||||
const moonPhaseChoices = Object.fromEntries(
|
||||
Object.entries(SYSTEM.MOON_DICE_PHASES).map(([key, val]) => [
|
||||
key, { ...val, symbol: MOON_SYMBOLS[key] ?? "☽" }
|
||||
])
|
||||
)
|
||||
|
||||
const dialogContext = {
|
||||
actorName: options.actorName,
|
||||
statLabel: options.statLabel,
|
||||
skillLabel: options.skillLabel,
|
||||
skillValue: options.skillValue,
|
||||
woundMalus: options.woundMalus ?? 0,
|
||||
nbDice: Math.max(1, options.skillValue ?? 1),
|
||||
difficultyChoices:SYSTEM.DIFFICULTY_CHOICES,
|
||||
moonPhaseChoices: SYSTEM.MOON_DICE_PHASES,
|
||||
defaultDifficulty:options.difficulty ?? "normal",
|
||||
defaultMoonPhase: options.moonPhase ?? "none",
|
||||
rollModes,
|
||||
fieldRollMode,
|
||||
actorName: options.actorName,
|
||||
statLabel: options.statLabel,
|
||||
skillLabel: options.skillLabel,
|
||||
skillValue: baseSkillVal,
|
||||
woundMalus,
|
||||
woundLabel,
|
||||
nbDiceBase,
|
||||
difficultyChoices: SYSTEM.DIFFICULTY_CHOICES,
|
||||
moonPhaseChoices,
|
||||
defaultDifficulty: options.difficulty ?? "normal",
|
||||
defaultMoonPhase: options.moonPhase ?? "none",
|
||||
}
|
||||
|
||||
const content = await foundry.applications.handlebars.renderTemplate(
|
||||
@@ -53,7 +76,11 @@ export class CelestopolRoll extends Roll {
|
||||
dialogContext
|
||||
)
|
||||
|
||||
const title = `${game.i18n.localize("CELESTOPOL.Roll.title")} — ${game.i18n.localize(options.skillLabel ?? "")}`
|
||||
const skillLocalized = game.i18n.localize(options.skillLabel ?? "")
|
||||
const statLocalized = options.statLabel ? game.i18n.localize(options.statLabel) : null
|
||||
const title = statLocalized
|
||||
? `${statLocalized} › ${skillLocalized}`
|
||||
: skillLocalized
|
||||
|
||||
const rollContext = await foundry.applications.api.DialogV2.wait({
|
||||
window: { title },
|
||||
@@ -62,9 +89,12 @@ export class CelestopolRoll extends Roll {
|
||||
buttons: [
|
||||
{
|
||||
label: game.i18n.localize("CELESTOPOL.Roll.roll"),
|
||||
icon: "fa-solid fa-dice",
|
||||
callback: (event, button) => {
|
||||
return Array.from(button.form.elements).reduce((obj, input) => {
|
||||
if (input.name) obj[input.name] = input.value
|
||||
if (input.name) {
|
||||
obj[input.name] = input.type === "checkbox" ? input.checked : input.value
|
||||
}
|
||||
return obj
|
||||
}, {})
|
||||
},
|
||||
@@ -80,11 +110,13 @@ export class CelestopolRoll extends Roll {
|
||||
const moonPhase = rollContext.moonPhase ?? "none"
|
||||
const moonConfig = SYSTEM.MOON_DICE_PHASES[moonPhase] ?? SYSTEM.MOON_DICE_PHASES.none
|
||||
const modifier = parseInt(rollContext.modifier ?? 0) || 0
|
||||
const woundMalus = options.woundMalus ?? 0
|
||||
const skillValue = Math.max(0, (options.skillValue ?? 0) + woundMalus)
|
||||
const nbDice = Math.max(1, skillValue)
|
||||
const aspectMod = parseInt(rollContext.aspectModifier ?? 0) || 0
|
||||
const useDestin = rollContext.useDestin === true || rollContext.useDestin === "true"
|
||||
const destinDice = useDestin ? 2 : 0
|
||||
const skillValue = Math.max(0, baseSkillVal + woundMalus)
|
||||
const nbDice = Math.max(1, skillValue) + destinDice
|
||||
const moonBonus = moonConfig.bonus ?? 0
|
||||
const totalModifier = moonBonus + modifier
|
||||
const totalModifier = moonBonus + modifier + aspectMod
|
||||
|
||||
const formula = totalModifier !== 0
|
||||
? `${nbDice}d6 + ${totalModifier}`
|
||||
@@ -97,6 +129,10 @@ export class CelestopolRoll extends Roll {
|
||||
moonPhase,
|
||||
moonBonus,
|
||||
modifier,
|
||||
aspectMod,
|
||||
useDestin,
|
||||
destinDice,
|
||||
nbDice,
|
||||
formula,
|
||||
rollMode: rollContext.visibility ?? "publicroll",
|
||||
}
|
||||
@@ -118,16 +154,26 @@ export class CelestopolRoll extends Roll {
|
||||
return roll
|
||||
}
|
||||
|
||||
/** Détermine succès/échec selon le total vs le seuil. */
|
||||
/**
|
||||
* Détermine succès/échec et critiques selon la marge (total − seuil).
|
||||
* - Marge ≥ 5 → succès critique
|
||||
* - Marge ≥ 0 → succès
|
||||
* - Marge ≤ −5 → échec critique
|
||||
* - Marge < 0 → échec
|
||||
*/
|
||||
computeResult() {
|
||||
const threshold = SYSTEM.DIFFICULTY_CHOICES[this.options.difficulty]?.value ?? 0
|
||||
if (threshold === 0) {
|
||||
this.options.resultType = "unknown"
|
||||
} else if (this.total >= threshold) {
|
||||
this.options.resultType = "success"
|
||||
} else {
|
||||
this.options.resultType = "failure"
|
||||
this.options.margin = null
|
||||
return
|
||||
}
|
||||
const margin = this.total - threshold
|
||||
this.options.margin = margin
|
||||
if (margin >= 5) this.options.resultType = "critical-success"
|
||||
else if (margin >= 0) this.options.resultType = "success"
|
||||
else if (margin <= -5) this.options.resultType = "critical-failure"
|
||||
else this.options.resultType = "failure"
|
||||
}
|
||||
|
||||
/** @override */
|
||||
@@ -137,44 +183,77 @@ export class CelestopolRoll extends Roll {
|
||||
}
|
||||
|
||||
async _getChatCardData(isPrivate) {
|
||||
const statLabel = this.options.statLabel
|
||||
const skillLabel = this.options.skillLabel
|
||||
const resultType = this.resultType
|
||||
const diceResults = this.dice[0]?.results?.map(r => r.result) ?? []
|
||||
const statLabel = this.options.statLabel
|
||||
const skillLabel = this.options.skillLabel
|
||||
const resultType = this.resultType
|
||||
const diceResults = this.dice[0]?.results?.map(r => r.result) ?? []
|
||||
const diceSum = diceResults.reduce((a, b) => a + b, 0)
|
||||
const threshold = SYSTEM.DIFFICULTY_CHOICES[this.options.difficulty]?.value ?? 0
|
||||
const margin = this.options.margin
|
||||
const moonPhase = this.options.moonPhase ?? "none"
|
||||
const moonSymbol = MOON_SYMBOLS[moonPhase] ?? "☽"
|
||||
const woundMalus = this.options.woundMalus ?? 0
|
||||
const woundLevelId = this.options.woundLevel ?? 0
|
||||
const woundLabel = woundLevelId > 0
|
||||
? game.i18n.localize(SYSTEM.WOUND_LEVELS[woundLevelId]?.label ?? "")
|
||||
: null
|
||||
|
||||
// Classe CSS principale du résultat
|
||||
const resultClassMap = {
|
||||
"critical-success": "critical-success",
|
||||
"success": "success",
|
||||
"failure": "failure",
|
||||
"critical-failure": "critical-failure",
|
||||
"unknown": "",
|
||||
}
|
||||
|
||||
return {
|
||||
cssClass: [SYSTEM.id, "dice-roll"].join(" "),
|
||||
actorId: this.actorId,
|
||||
actorName: this.actorName,
|
||||
actorImg: this.actorImage,
|
||||
statLabel: statLabel ? game.i18n.localize(statLabel) : "",
|
||||
skillLabel: skillLabel ? game.i18n.localize(skillLabel) : "",
|
||||
formula: this.formula,
|
||||
total: this.total,
|
||||
resultType,
|
||||
resultClass: resultType === "success" ? "success" : resultType === "failure" ? "failure" : "",
|
||||
success: this.isSuccess,
|
||||
failure: this.isFailure,
|
||||
difficulty: this.options.difficulty,
|
||||
difficultyLabel:game.i18n.localize(SYSTEM.DIFFICULTY_CHOICES[this.options.difficulty]?.label ?? ""),
|
||||
moonPhase: this.options.moonPhase,
|
||||
moonPhaseLabel: game.i18n.localize(SYSTEM.MOON_DICE_PHASES[this.options.moonPhase]?.label ?? ""),
|
||||
moonBonus: this.moonBonus,
|
||||
modifier: this.options.modifier,
|
||||
isPrivate,
|
||||
tooltip: isPrivate ? "" : await this.getTooltip(),
|
||||
cssClass: [SYSTEM.id, "dice-roll"].join(" "),
|
||||
actorId: this.actorId,
|
||||
actorName: this.actorName,
|
||||
actorImg: this.actorImage,
|
||||
statLabel: statLabel ? game.i18n.localize(statLabel) : "",
|
||||
skillLabel: skillLabel ? game.i18n.localize(skillLabel) : "",
|
||||
formula: this.formula,
|
||||
total: this.total,
|
||||
diceSum,
|
||||
diceResults,
|
||||
resultType,
|
||||
resultClass: resultClassMap[resultType] ?? "",
|
||||
isSuccess: this.isSuccess,
|
||||
isFailure: this.isFailure,
|
||||
isCriticalSuccess: this.isCriticalSuccess,
|
||||
isCriticalFailure: this.isCriticalFailure,
|
||||
difficulty: this.options.difficulty,
|
||||
difficultyLabel: game.i18n.localize(SYSTEM.DIFFICULTY_CHOICES[this.options.difficulty]?.label ?? ""),
|
||||
difficultyValue: threshold,
|
||||
margin,
|
||||
marginAbs: margin !== null ? Math.abs(margin) : null,
|
||||
marginAbove: margin !== null && margin >= 0,
|
||||
moonPhase,
|
||||
moonPhaseLabel: game.i18n.localize(SYSTEM.MOON_DICE_PHASES[moonPhase]?.label ?? ""),
|
||||
moonSymbol,
|
||||
moonBonus: this.moonBonus,
|
||||
modifier: this.options.modifier,
|
||||
aspectMod: this.options.aspectMod ?? 0,
|
||||
useDestin: this.options.useDestin ?? false,
|
||||
destinDice: this.options.destinDice ?? 0,
|
||||
nbDice: this.options.nbDice ?? diceResults.length,
|
||||
woundMalus,
|
||||
woundLabel,
|
||||
isPrivate,
|
||||
tooltip: isPrivate ? "" : await this.getTooltip(),
|
||||
}
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async toMessage(messageData = {}, { rollMode, create = true } = {}) {
|
||||
return super.toMessage(
|
||||
{
|
||||
flavor: `<strong>${game.i18n.localize(this.skillLabel ?? "")}</strong>`,
|
||||
...messageData,
|
||||
},
|
||||
{ rollMode }
|
||||
)
|
||||
const skillLocalized = this.skillLabel ? game.i18n.localize(this.skillLabel) : ""
|
||||
const statLocalized = this.options.statLabel
|
||||
? game.i18n.localize(this.options.statLabel) : ""
|
||||
const flavor = statLocalized
|
||||
? `<strong>${statLocalized} › ${skillLocalized}</strong>`
|
||||
: `<strong>${skillLocalized}</strong>`
|
||||
return super.toMessage({ flavor, ...messageData }, { rollMode })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,18 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
|
||||
value: new fields.NumberField({ ...reqInt, initial: 0, min: 0, max: 8 }),
|
||||
})
|
||||
|
||||
// Les 4 stats avec leurs compétences
|
||||
// Les 4 stats avec leurs domaines
|
||||
const skillField = (label) => new fields.SchemaField({
|
||||
label: new fields.StringField({ required: true, initial: label }),
|
||||
value: new fields.NumberField({ ...reqInt, initial: 0, min: 0, max: 8 }),
|
||||
level1: new fields.BooleanField({ required: true, initial: false }),
|
||||
level2: new fields.BooleanField({ required: true, initial: false }),
|
||||
level3: new fields.BooleanField({ required: true, initial: false }),
|
||||
level4: new fields.BooleanField({ required: true, initial: false }),
|
||||
level5: new fields.BooleanField({ required: true, initial: false }),
|
||||
level6: new fields.BooleanField({ required: true, initial: false }),
|
||||
level7: new fields.BooleanField({ required: true, initial: false }),
|
||||
level8: new fields.BooleanField({ required: true, initial: false }),
|
||||
})
|
||||
|
||||
const statField = (statId) => {
|
||||
@@ -85,9 +93,18 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
|
||||
vision: persoAttrField(),
|
||||
})
|
||||
|
||||
// Factions
|
||||
// Factions - 9 checkboxes per faction (like wound tracks)
|
||||
const factionField = () => new fields.SchemaField({
|
||||
value: new fields.NumberField({ ...reqInt, initial: 0 }),
|
||||
level1: new fields.BooleanField({ required: true, initial: false }),
|
||||
level2: new fields.BooleanField({ required: true, initial: false }),
|
||||
level3: new fields.BooleanField({ required: true, initial: false }),
|
||||
level4: new fields.BooleanField({ required: true, initial: false }),
|
||||
level5: new fields.BooleanField({ required: true, initial: false }),
|
||||
level6: new fields.BooleanField({ required: true, initial: false }),
|
||||
level7: new fields.BooleanField({ required: true, initial: false }),
|
||||
level8: new fields.BooleanField({ required: true, initial: false }),
|
||||
level9: new fields.BooleanField({ required: true, initial: false }),
|
||||
})
|
||||
schema.factions = new fields.SchemaField({
|
||||
pinkerton: factionField(),
|
||||
@@ -150,9 +167,9 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
|
||||
}
|
||||
|
||||
/**
|
||||
* Lance les dés pour une compétence donnée.
|
||||
* Lance les dés pour un domaine donné.
|
||||
* @param {string} statId - Id de la stat (ame, corps, coeur, esprit)
|
||||
* @param {string} skillId - Id de la compétence
|
||||
* @param {string} skillId - Id du domaine
|
||||
*/
|
||||
async roll(statId, skillId) {
|
||||
const { CelestopolRoll } = await import("../documents/roll.mjs")
|
||||
@@ -169,6 +186,7 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
|
||||
skillLabel: skill.label,
|
||||
skillValue: skill.value,
|
||||
woundMalus: this.getWoundMalus(),
|
||||
woundLevel: this.blessures.lvl,
|
||||
moonPhase: this.prefs.moonPhase,
|
||||
difficulty: this.prefs.difficulty,
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { SYSTEM } from "../config/system.mjs"
|
||||
|
||||
/** Schéma partagé pour les bonus/malus par compétence (utilisé dans anomaly/aspect/attribute). */
|
||||
/** Schéma partagé pour les bonus/malus par domaine (utilisé dans anomaly/aspect/attribute). */
|
||||
function skillScoresSchema() {
|
||||
const fields = foundry.data.fields
|
||||
const reqInt = { required: true, nullable: false, integer: true }
|
||||
|
||||
Reference in New Issue
Block a user