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:
@@ -65,35 +65,35 @@ Hooks.once("init", () => {
|
|||||||
|
|
||||||
// ── Sheets: unregister core, register system sheets ─────────────────────
|
// ── Sheets: unregister core, register system sheets ─────────────────────
|
||||||
foundry.applications.sheets.ActorSheetV2.unregisterSheet?.("core", "Actor", { types: ["character", "npc"] })
|
foundry.applications.sheets.ActorSheetV2.unregisterSheet?.("core", "Actor", { types: ["character", "npc"] })
|
||||||
Actors.unregisterSheet("core", ActorSheet)
|
foundry.documents.collections.Actors.unregisterSheet("core", ActorSheet)
|
||||||
Actors.registerSheet(SYSTEM_ID, CelestopolCharacterSheet, {
|
foundry.documents.collections.Actors.registerSheet(SYSTEM_ID, CelestopolCharacterSheet, {
|
||||||
types: ["character"],
|
types: ["character"],
|
||||||
makeDefault: true,
|
makeDefault: true,
|
||||||
label: "CELESTOPOL.Sheet.character",
|
label: "CELESTOPOL.Sheet.character",
|
||||||
})
|
})
|
||||||
Actors.registerSheet(SYSTEM_ID, CelestopolNPCSheet, {
|
foundry.documents.collections.Actors.registerSheet(SYSTEM_ID, CelestopolNPCSheet, {
|
||||||
types: ["npc"],
|
types: ["npc"],
|
||||||
makeDefault: true,
|
makeDefault: true,
|
||||||
label: "CELESTOPOL.Sheet.npc",
|
label: "CELESTOPOL.Sheet.npc",
|
||||||
})
|
})
|
||||||
|
|
||||||
Items.unregisterSheet("core", ItemSheet)
|
foundry.documents.collections.Items.unregisterSheet("core", ItemSheet)
|
||||||
Items.registerSheet(SYSTEM_ID, CelestopolAnomalySheet, {
|
foundry.documents.collections.Items.registerSheet(SYSTEM_ID, CelestopolAnomalySheet, {
|
||||||
types: ["anomaly"],
|
types: ["anomaly"],
|
||||||
makeDefault: true,
|
makeDefault: true,
|
||||||
label: "CELESTOPOL.Sheet.anomaly",
|
label: "CELESTOPOL.Sheet.anomaly",
|
||||||
})
|
})
|
||||||
Items.registerSheet(SYSTEM_ID, CelestopolAspectSheet, {
|
foundry.documents.collections.Items.registerSheet(SYSTEM_ID, CelestopolAspectSheet, {
|
||||||
types: ["aspect"],
|
types: ["aspect"],
|
||||||
makeDefault: true,
|
makeDefault: true,
|
||||||
label: "CELESTOPOL.Sheet.aspect",
|
label: "CELESTOPOL.Sheet.aspect",
|
||||||
})
|
})
|
||||||
Items.registerSheet(SYSTEM_ID, CelestopolAttributeSheet, {
|
foundry.documents.collections.Items.registerSheet(SYSTEM_ID, CelestopolAttributeSheet, {
|
||||||
types: ["attribute"],
|
types: ["attribute"],
|
||||||
makeDefault: true,
|
makeDefault: true,
|
||||||
label: "CELESTOPOL.Sheet.attribute",
|
label: "CELESTOPOL.Sheet.attribute",
|
||||||
})
|
})
|
||||||
Items.registerSheet(SYSTEM_ID, CelestopolEquipmentSheet, {
|
foundry.documents.collections.Items.registerSheet(SYSTEM_ID, CelestopolEquipmentSheet, {
|
||||||
types: ["equipment"],
|
types: ["equipment"],
|
||||||
makeDefault: true,
|
makeDefault: true,
|
||||||
label: "CELESTOPOL.Sheet.equipment",
|
label: "CELESTOPOL.Sheet.equipment",
|
||||||
@@ -144,7 +144,15 @@ function _registerHandlebarsHelpers() {
|
|||||||
return args.reduce((acc, key) => (acc && acc[key] !== undefined ? acc[key] : undefined), obj)
|
return args.reduce((acc, key) => (acc && acc[key] !== undefined ? acc[key] : undefined), obj)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Helper : let (scope variable assignment inside template)
|
// Helper : negate a number (abs value helper)
|
||||||
|
Handlebars.registerHelper("neg", n => -n)
|
||||||
|
|
||||||
|
// Helper : absolute value
|
||||||
|
Handlebars.registerHelper("abs", n => Math.abs(n))
|
||||||
|
|
||||||
|
// Helper : add two numbers
|
||||||
|
Handlebars.registerHelper("add", (a, b) => a + b)
|
||||||
|
|
||||||
Handlebars.registerHelper("let", function(value, options) {
|
Handlebars.registerHelper("let", function(value, options) {
|
||||||
return options.fn({ value })
|
return options.fn({ value })
|
||||||
})
|
})
|
||||||
|
|||||||
21
lang/fr.json
21
lang/fr.json
@@ -85,7 +85,7 @@
|
|||||||
},
|
},
|
||||||
"Tab": {
|
"Tab": {
|
||||||
"main": "Principal",
|
"main": "Principal",
|
||||||
"competences": "Compétences",
|
"competences": "Domaines",
|
||||||
"blessures": "Blessures",
|
"blessures": "Blessures",
|
||||||
"factions": "Factions",
|
"factions": "Factions",
|
||||||
"biography": "Biographie",
|
"biography": "Biographie",
|
||||||
@@ -106,7 +106,22 @@
|
|||||||
"moonBonus": "Bonus de lune",
|
"moonBonus": "Bonus de lune",
|
||||||
"title": "Jet de dés",
|
"title": "Jet de dés",
|
||||||
"roll": "Lancer !",
|
"roll": "Lancer !",
|
||||||
"rollTitle": "Lancer les dés"
|
"rollTitle": "Lancer les dés",
|
||||||
|
"aspect": "Modificateur d'Aspect",
|
||||||
|
"woundMalus": "Malus de blessures",
|
||||||
|
"diceSum": "Somme des dés",
|
||||||
|
"margin": "Marge",
|
||||||
|
"above": "au-dessus du seuil",
|
||||||
|
"below": "en dessous du seuil",
|
||||||
|
"destin": "Dépenser 1 Destin (+2 dés)",
|
||||||
|
"usedAspect": "Aspect utilisé",
|
||||||
|
"usedDestin": "Destin dépensé",
|
||||||
|
"criticalSuccessDesc": "Marge ≥ 5 — résultat exceptionnel !",
|
||||||
|
"criticalFailureDesc": "Marge ≤ −5 — résultat désastreux !",
|
||||||
|
"woundLevel": "Niveau de blessures",
|
||||||
|
"diceBreakdown": "Détail du jet",
|
||||||
|
"threshold": "Seuil",
|
||||||
|
"nbDiceBase": "Dés de base"
|
||||||
},
|
},
|
||||||
"Moon": {
|
"Moon": {
|
||||||
"none": "Aucune phase",
|
"none": "Aucune phase",
|
||||||
@@ -174,4 +189,4 @@
|
|||||||
"rollFor": "Jet de {skill} ({stat})"
|
"rollFor": "Jet de {skill} ({stat})"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -50,9 +50,28 @@ export default class CelestopolActorSheet extends HandlebarsApplicationMixin(fou
|
|||||||
this.element.querySelectorAll(".rollable").forEach(el => {
|
this.element.querySelectorAll(".rollable").forEach(el => {
|
||||||
el.addEventListener("click", this._onRoll.bind(this))
|
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) {
|
async _onRoll(event) {
|
||||||
|
// Don't roll if clicking on a checkbox
|
||||||
|
if (event.target.classList.contains('skill-level-checkbox')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
if (!this.isPlayMode) return
|
if (!this.isPlayMode) return
|
||||||
const el = event.currentTarget
|
const el = event.currentTarget
|
||||||
const statId = el.dataset.statId
|
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 })
|
await this.document.createEmbeddedDocuments("Item", [item.toObject()], { renderSheet: false })
|
||||||
}
|
}
|
||||||
|
|
||||||
static async #onEditImage(event, target) {
|
static async #onEditImage(event, _target) {
|
||||||
const attr = target.dataset.edit
|
const current = this.document.img
|
||||||
const current = foundry.utils.getProperty(this.document, attr)
|
|
||||||
const fp = new FilePicker({
|
const fp = new FilePicker({
|
||||||
current,
|
current,
|
||||||
type: "image",
|
type: "image",
|
||||||
callback: (path) => this.document.update({ [attr]: path }),
|
callback: (path) => this.document.update({ img: path }),
|
||||||
top: this.position.top + 40,
|
top: this.position.top + 40,
|
||||||
left: this.position.left + 10,
|
left: this.position.left + 10,
|
||||||
})
|
})
|
||||||
@@ -123,4 +141,133 @@ export default class CelestopolActorSheet extends HandlebarsApplicationMixin(fou
|
|||||||
const item = await fromUuid(uuid)
|
const item = await fromUuid(uuid)
|
||||||
await item?.deleteDialog()
|
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) {
|
static async #onEditImage(event, _target) {
|
||||||
const attr = target.dataset.edit
|
const current = this.document.img
|
||||||
const current = foundry.utils.getProperty(this.document, attr)
|
|
||||||
const fp = new FilePicker({
|
const fp = new FilePicker({
|
||||||
current,
|
current,
|
||||||
type: "image",
|
type: "image",
|
||||||
callback: (path) => this.document.update({ [attr]: path }),
|
callback: (path) => this.document.update({ img: path }),
|
||||||
top: this.position.top + 40,
|
top: this.position.top + 40,
|
||||||
left: this.position.left + 10,
|
left: this.position.left + 10,
|
||||||
})
|
})
|
||||||
return fp.browse()
|
return fp.browse()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export const ASCII = `
|
|||||||
░░░░░░░░░░░░░░░░░░1922░░░░░░░░░░░░░░░░░░░
|
░░░░░░░░░░░░░░░░░░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 = {
|
export const STATS = {
|
||||||
ame: { id: "ame", label: "CELESTOPOL.Stat.ame" },
|
ame: { id: "ame", label: "CELESTOPOL.Stat.ame" },
|
||||||
corps: { id: "corps", label: "CELESTOPOL.Stat.corps" },
|
corps: { id: "corps", label: "CELESTOPOL.Stat.corps" },
|
||||||
@@ -15,7 +15,7 @@ export const STATS = {
|
|||||||
esprit: { id: "esprit", label: "CELESTOPOL.Stat.esprit" },
|
esprit: { id: "esprit", label: "CELESTOPOL.Stat.esprit" },
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Compétences groupées par attribut. */
|
/** Domaines groupées par attribut. */
|
||||||
export const SKILLS = {
|
export const SKILLS = {
|
||||||
ame: {
|
ame: {
|
||||||
artifice: { id: "artifice", label: "CELESTOPOL.Skill.artifice", stat: "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))
|
export const ALL_SKILLS = Object.values(SKILLS).flatMap(group => Object.values(group))
|
||||||
|
|
||||||
/** Types d'anomalies (pouvoirs paranormaux). */
|
/** Types d'anomalies (pouvoirs paranormaux). */
|
||||||
|
|||||||
@@ -1,24 +1,40 @@
|
|||||||
import { SYSTEM } from "../config/system.mjs"
|
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.
|
* Système de dés de Célestopol 1922.
|
||||||
*
|
*
|
||||||
* Le jet de base est : (valeur compétence)d6 comparé à un seuil de difficulté.
|
* Le jet de base est : (valeur domaine + malus blessures)d6 + bonus lune + modificateurs
|
||||||
* Le dé de lune ajoute un bonus selon la phase actuelle.
|
* comparé à un seuil de difficulté.
|
||||||
* Destin et Spleen modifient le nombre de dés.
|
* - Succès critique : marge ≥ 5
|
||||||
|
* - Échec critique : marge ≤ −5
|
||||||
*/
|
*/
|
||||||
export class CelestopolRoll extends Roll {
|
export class CelestopolRoll extends Roll {
|
||||||
static CHAT_TEMPLATE = "systems/fvtt-celestopol/templates/chat-message.hbs"
|
static CHAT_TEMPLATE = "systems/fvtt-celestopol/templates/chat-message.hbs"
|
||||||
|
|
||||||
get resultType() { return this.options.resultType }
|
get resultType() { return this.options.resultType }
|
||||||
get isSuccess() { return this.resultType === "success" }
|
get isSuccess() { return this.resultType === "success" || this.resultType === "critical-success" }
|
||||||
get isFailure() { return this.resultType === "failure" }
|
get isFailure() { return this.resultType === "failure" || this.resultType === "critical-failure" }
|
||||||
get actorId() { return this.options.actorId }
|
get isCriticalSuccess(){ return this.resultType === "critical-success" }
|
||||||
get actorName() { return this.options.actorName }
|
get isCriticalFailure(){ return this.resultType === "critical-failure" }
|
||||||
get actorImage() { return this.options.actorImage }
|
get actorId() { return this.options.actorId }
|
||||||
get skillLabel() { return this.options.skillLabel }
|
get actorName() { return this.options.actorName }
|
||||||
get difficulty() { return this.options.difficulty }
|
get actorImage() { return this.options.actorImage }
|
||||||
get moonBonus() { return this.options.moonBonus ?? 0 }
|
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.
|
* 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>}
|
* @returns {Promise<CelestopolRoll|null>}
|
||||||
*/
|
*/
|
||||||
static async prompt(options = {}) {
|
static async prompt(options = {}) {
|
||||||
const rollModes = foundry.utils.duplicate(CONFIG.Dice.rollModes)
|
const woundMalus = options.woundMalus ?? 0
|
||||||
const fieldRollMode = new foundry.data.fields.StringField({
|
const baseSkillVal = options.skillValue ?? 0
|
||||||
choices: rollModes,
|
const nbDiceBase = Math.max(1, baseSkillVal + woundMalus)
|
||||||
blank: false,
|
const woundLevelId = options.woundLevel ?? 0
|
||||||
default: "publicroll",
|
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 = {
|
const dialogContext = {
|
||||||
actorName: options.actorName,
|
actorName: options.actorName,
|
||||||
statLabel: options.statLabel,
|
statLabel: options.statLabel,
|
||||||
skillLabel: options.skillLabel,
|
skillLabel: options.skillLabel,
|
||||||
skillValue: options.skillValue,
|
skillValue: baseSkillVal,
|
||||||
woundMalus: options.woundMalus ?? 0,
|
woundMalus,
|
||||||
nbDice: Math.max(1, options.skillValue ?? 1),
|
woundLabel,
|
||||||
difficultyChoices:SYSTEM.DIFFICULTY_CHOICES,
|
nbDiceBase,
|
||||||
moonPhaseChoices: SYSTEM.MOON_DICE_PHASES,
|
difficultyChoices: SYSTEM.DIFFICULTY_CHOICES,
|
||||||
defaultDifficulty:options.difficulty ?? "normal",
|
moonPhaseChoices,
|
||||||
defaultMoonPhase: options.moonPhase ?? "none",
|
defaultDifficulty: options.difficulty ?? "normal",
|
||||||
rollModes,
|
defaultMoonPhase: options.moonPhase ?? "none",
|
||||||
fieldRollMode,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = await foundry.applications.handlebars.renderTemplate(
|
const content = await foundry.applications.handlebars.renderTemplate(
|
||||||
@@ -53,7 +76,11 @@ export class CelestopolRoll extends Roll {
|
|||||||
dialogContext
|
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({
|
const rollContext = await foundry.applications.api.DialogV2.wait({
|
||||||
window: { title },
|
window: { title },
|
||||||
@@ -62,9 +89,12 @@ export class CelestopolRoll extends Roll {
|
|||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
label: game.i18n.localize("CELESTOPOL.Roll.roll"),
|
label: game.i18n.localize("CELESTOPOL.Roll.roll"),
|
||||||
|
icon: "fa-solid fa-dice",
|
||||||
callback: (event, button) => {
|
callback: (event, button) => {
|
||||||
return Array.from(button.form.elements).reduce((obj, input) => {
|
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
|
return obj
|
||||||
}, {})
|
}, {})
|
||||||
},
|
},
|
||||||
@@ -80,11 +110,13 @@ export class CelestopolRoll extends Roll {
|
|||||||
const moonPhase = rollContext.moonPhase ?? "none"
|
const moonPhase = rollContext.moonPhase ?? "none"
|
||||||
const moonConfig = SYSTEM.MOON_DICE_PHASES[moonPhase] ?? SYSTEM.MOON_DICE_PHASES.none
|
const moonConfig = SYSTEM.MOON_DICE_PHASES[moonPhase] ?? SYSTEM.MOON_DICE_PHASES.none
|
||||||
const modifier = parseInt(rollContext.modifier ?? 0) || 0
|
const modifier = parseInt(rollContext.modifier ?? 0) || 0
|
||||||
const woundMalus = options.woundMalus ?? 0
|
const aspectMod = parseInt(rollContext.aspectModifier ?? 0) || 0
|
||||||
const skillValue = Math.max(0, (options.skillValue ?? 0) + woundMalus)
|
const useDestin = rollContext.useDestin === true || rollContext.useDestin === "true"
|
||||||
const nbDice = Math.max(1, skillValue)
|
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 moonBonus = moonConfig.bonus ?? 0
|
||||||
const totalModifier = moonBonus + modifier
|
const totalModifier = moonBonus + modifier + aspectMod
|
||||||
|
|
||||||
const formula = totalModifier !== 0
|
const formula = totalModifier !== 0
|
||||||
? `${nbDice}d6 + ${totalModifier}`
|
? `${nbDice}d6 + ${totalModifier}`
|
||||||
@@ -97,6 +129,10 @@ export class CelestopolRoll extends Roll {
|
|||||||
moonPhase,
|
moonPhase,
|
||||||
moonBonus,
|
moonBonus,
|
||||||
modifier,
|
modifier,
|
||||||
|
aspectMod,
|
||||||
|
useDestin,
|
||||||
|
destinDice,
|
||||||
|
nbDice,
|
||||||
formula,
|
formula,
|
||||||
rollMode: rollContext.visibility ?? "publicroll",
|
rollMode: rollContext.visibility ?? "publicroll",
|
||||||
}
|
}
|
||||||
@@ -118,16 +154,26 @@ export class CelestopolRoll extends Roll {
|
|||||||
return 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() {
|
computeResult() {
|
||||||
const threshold = SYSTEM.DIFFICULTY_CHOICES[this.options.difficulty]?.value ?? 0
|
const threshold = SYSTEM.DIFFICULTY_CHOICES[this.options.difficulty]?.value ?? 0
|
||||||
if (threshold === 0) {
|
if (threshold === 0) {
|
||||||
this.options.resultType = "unknown"
|
this.options.resultType = "unknown"
|
||||||
} else if (this.total >= threshold) {
|
this.options.margin = null
|
||||||
this.options.resultType = "success"
|
return
|
||||||
} else {
|
|
||||||
this.options.resultType = "failure"
|
|
||||||
}
|
}
|
||||||
|
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 */
|
/** @override */
|
||||||
@@ -137,44 +183,77 @@ export class CelestopolRoll extends Roll {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async _getChatCardData(isPrivate) {
|
async _getChatCardData(isPrivate) {
|
||||||
const statLabel = this.options.statLabel
|
const statLabel = this.options.statLabel
|
||||||
const skillLabel = this.options.skillLabel
|
const skillLabel = this.options.skillLabel
|
||||||
const resultType = this.resultType
|
const resultType = this.resultType
|
||||||
const diceResults = this.dice[0]?.results?.map(r => r.result) ?? []
|
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 {
|
return {
|
||||||
cssClass: [SYSTEM.id, "dice-roll"].join(" "),
|
cssClass: [SYSTEM.id, "dice-roll"].join(" "),
|
||||||
actorId: this.actorId,
|
actorId: this.actorId,
|
||||||
actorName: this.actorName,
|
actorName: this.actorName,
|
||||||
actorImg: this.actorImage,
|
actorImg: this.actorImage,
|
||||||
statLabel: statLabel ? game.i18n.localize(statLabel) : "",
|
statLabel: statLabel ? game.i18n.localize(statLabel) : "",
|
||||||
skillLabel: skillLabel ? game.i18n.localize(skillLabel) : "",
|
skillLabel: skillLabel ? game.i18n.localize(skillLabel) : "",
|
||||||
formula: this.formula,
|
formula: this.formula,
|
||||||
total: this.total,
|
total: this.total,
|
||||||
resultType,
|
diceSum,
|
||||||
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(),
|
|
||||||
diceResults,
|
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 */
|
/** @override */
|
||||||
async toMessage(messageData = {}, { rollMode, create = true } = {}) {
|
async toMessage(messageData = {}, { rollMode, create = true } = {}) {
|
||||||
return super.toMessage(
|
const skillLocalized = this.skillLabel ? game.i18n.localize(this.skillLabel) : ""
|
||||||
{
|
const statLocalized = this.options.statLabel
|
||||||
flavor: `<strong>${game.i18n.localize(this.skillLabel ?? "")}</strong>`,
|
? game.i18n.localize(this.options.statLabel) : ""
|
||||||
...messageData,
|
const flavor = statLocalized
|
||||||
},
|
? `<strong>${statLocalized} › ${skillLocalized}</strong>`
|
||||||
{ rollMode }
|
: `<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 }),
|
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({
|
const skillField = (label) => new fields.SchemaField({
|
||||||
label: new fields.StringField({ required: true, initial: label }),
|
label: new fields.StringField({ required: true, initial: label }),
|
||||||
value: new fields.NumberField({ ...reqInt, initial: 0, min: 0, max: 8 }),
|
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) => {
|
const statField = (statId) => {
|
||||||
@@ -85,9 +93,18 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
|
|||||||
vision: persoAttrField(),
|
vision: persoAttrField(),
|
||||||
})
|
})
|
||||||
|
|
||||||
// Factions
|
// Factions - 9 checkboxes per faction (like wound tracks)
|
||||||
const factionField = () => new fields.SchemaField({
|
const factionField = () => new fields.SchemaField({
|
||||||
value: new fields.NumberField({ ...reqInt, initial: 0 }),
|
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({
|
schema.factions = new fields.SchemaField({
|
||||||
pinkerton: factionField(),
|
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} 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) {
|
async roll(statId, skillId) {
|
||||||
const { CelestopolRoll } = await import("../documents/roll.mjs")
|
const { CelestopolRoll } = await import("../documents/roll.mjs")
|
||||||
@@ -169,6 +186,7 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
|
|||||||
skillLabel: skill.label,
|
skillLabel: skill.label,
|
||||||
skillValue: skill.value,
|
skillValue: skill.value,
|
||||||
woundMalus: this.getWoundMalus(),
|
woundMalus: this.getWoundMalus(),
|
||||||
|
woundLevel: this.blessures.lvl,
|
||||||
moonPhase: this.prefs.moonPhase,
|
moonPhase: this.prefs.moonPhase,
|
||||||
difficulty: this.prefs.difficulty,
|
difficulty: this.prefs.difficulty,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { SYSTEM } from "../config/system.mjs"
|
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() {
|
function skillScoresSchema() {
|
||||||
const fields = foundry.data.fields
|
const fields = foundry.data.fields
|
||||||
const reqInt = { required: true, nullable: false, integer: true }
|
const reqInt = { required: true, nullable: false, integer: true }
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stats × Compétences grid
|
// Stats × Domaines grid
|
||||||
.stats-grid {
|
.stats-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
|
|||||||
@@ -157,8 +157,88 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ─── Tabs ────────────────────────────────────────────────────────────────
|
// ─── Tabs ────────────────────────────────────────────────────────────────
|
||||||
|
// Updated: 1774698726 - Enhanced tab styling
|
||||||
|
|
||||||
.sheet-tabs {
|
.sheet-tabs {
|
||||||
|
|
||||||
|
// ─── Faction checkboxes ─────────────────────────────────────────────────
|
||||||
|
|
||||||
|
.faction-checkboxes-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
pointer-events: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faction-checkboxes {
|
||||||
|
display: inline-flex;
|
||||||
|
gap: 2px;
|
||||||
|
pointer-events: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faction-checkbox-wrapper {
|
||||||
|
display: inline-block;
|
||||||
|
pointer-events: auto !important;
|
||||||
|
position: relative;
|
||||||
|
z-index: 101;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faction-checkbox {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
pointer-events: auto !important;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
position: relative;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faction-checkbox:disabled {
|
||||||
|
cursor: default;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faction-count {
|
||||||
|
margin-left: 8px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--cel-orange);
|
||||||
|
min-width: 20px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faction-value-input {
|
||||||
|
width: 40px;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fix pointer-events for faction checkboxes in table cells
|
||||||
|
.faction-row {
|
||||||
|
pointer-events: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faction-row td {
|
||||||
|
pointer-events: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure labels are clickable
|
||||||
|
.faction-checkbox-wrapper label {
|
||||||
|
cursor: pointer;
|
||||||
|
pointer-events: auto !important;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure table cells allow pointer events
|
||||||
|
.faction-row {
|
||||||
|
pointer-events: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faction-row td {
|
||||||
|
pointer-events: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sheet-tabs {
|
||||||
display: flex;
|
display: flex;
|
||||||
background: var(--cel-green-dark);
|
background: var(--cel-green-dark);
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@@ -167,29 +247,51 @@
|
|||||||
border-bottom: 2px solid var(--cel-orange);
|
border-bottom: 2px solid var(--cel-orange);
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
padding: 6px 14px;
|
padding: 8px 16px;
|
||||||
color: rgba(240,232,212,0.7);
|
color: rgba(240,232,212,0.8);
|
||||||
font-family: var(--cel-font-title);
|
font-family: var(--cel-font-title);
|
||||||
font-size: 0.82em;
|
font-size: 0.85em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.07em;
|
letter-spacing: 0.07em;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-right: 1px solid rgba(196,154,26,0.2);
|
border-right: 1px solid rgba(196,154,26,0.2);
|
||||||
transition: color 0.2s, border-bottom-color 0.2s;
|
transition: all 0.2s ease;
|
||||||
// Art Déco underline indicator
|
// Art Déco underline indicator
|
||||||
border-bottom: 3px solid transparent;
|
border-bottom: 3px solid transparent;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
background: rgba(240,232,212,0.1);
|
||||||
|
margin: 0 1px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: var(--cel-orange-light);
|
color: var(--cel-orange-light);
|
||||||
background: rgba(196,154,26,0.07);
|
background: rgba(196,154,26,0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
color: var(--cel-orange);
|
color: var(--cel-cream) !important;
|
||||||
background: rgba(196,154,26,0.1);
|
background: linear-gradient(to bottom, var(--cel-orange), var(--cel-orange-light)) !important;
|
||||||
border-bottom-color: var(--cel-orange);
|
border-bottom: 3px solid var(--cel-accent) !important;
|
||||||
font-weight: bold;
|
font-weight: bold !important;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) !important;
|
||||||
|
transform: translateY(-1px) !important;
|
||||||
|
position: relative !important;
|
||||||
|
z-index: 2 !important;
|
||||||
|
border-right-color: transparent !important;
|
||||||
|
border: 2px solid red !important; // DEBUG: bordure rouge très visible
|
||||||
|
|
||||||
|
// Art Déco triangle indicator
|
||||||
|
&::before {
|
||||||
|
content: '' !important;
|
||||||
|
position: absolute !important;
|
||||||
|
top: -5px !important;
|
||||||
|
left: 50% !important;
|
||||||
|
transform: translateX(-50%) !important;
|
||||||
|
width: 0 !important;
|
||||||
|
height: 0 !important;
|
||||||
|
border-left: 8px solid transparent !important;
|
||||||
|
border-right: 8px solid transparent !important;
|
||||||
|
border-bottom: 8px solid var(--cel-accent) !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
412
styles/roll.less
412
styles/roll.less
@@ -1,22 +1,137 @@
|
|||||||
// ─── Roll dialog ─────────────────────────────────────────────────────────────
|
// ─── Roll dialog ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
.roll-dialog.celestopol {
|
// DialogV2 sets roll-dialog class on the .application element, not on the form.
|
||||||
padding: 8px 12px;
|
// All dialog content lives in .application.roll-dialog .dialog-content
|
||||||
|
|
||||||
|
.application.roll-dialog .window-content {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.application.roll-dialog .dialog-content {
|
||||||
|
padding: 0 !important;
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.application.roll-dialog .roll-dialog-content {
|
||||||
|
padding: 10px 14px;
|
||||||
font-family: var(--cel-font-body, "Palatino Linotype", serif);
|
font-family: var(--cel-font-body, "Palatino Linotype", serif);
|
||||||
|
min-width: 340px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0;
|
||||||
|
|
||||||
.roll-title {
|
// ── Bloc infos acteur ──
|
||||||
|
.roll-info-block {
|
||||||
|
background: var(--cel-green, #1b3828);
|
||||||
|
background-image: url("../assets/ui/fond_cadrille.jpg");
|
||||||
|
background-blend-mode: soft-light;
|
||||||
|
border-bottom: 2px solid var(--cel-orange, #c49a1a);
|
||||||
|
padding: 10px 14px 12px;
|
||||||
|
margin: -10px -14px 14px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-family: var(--cel-font-title, "CopaseticNF", serif);
|
|
||||||
font-size: 1.1em;
|
|
||||||
color: var(--cel-orange, #c49a1a);
|
|
||||||
margin-bottom: 10px;
|
|
||||||
|
|
||||||
.separator {
|
.roll-actor {
|
||||||
margin: 0 6px;
|
font-family: var(--cel-font-title, "CopaseticNF", serif);
|
||||||
color: var(--cel-border, #7a5c20);
|
color: var(--cel-orange-light, #ddb84a);
|
||||||
|
font-size: 0.82em;
|
||||||
|
letter-spacing: 0.06em;
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
|
||||||
|
.roll-skill-line {
|
||||||
|
font-family: var(--cel-font-title, "CopaseticNF", serif);
|
||||||
|
font-size: 1.2em;
|
||||||
|
color: var(--cel-cream, #f0e8d4);
|
||||||
|
margin-top: 1px;
|
||||||
|
|
||||||
|
.stat-label { color: var(--cel-orange-light, #ddb84a); }
|
||||||
|
.sep { color: var(--cel-border, #7a5c20); margin: 0 4px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.roll-dice-summary {
|
||||||
|
margin-top: 8px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
background: rgba(0,0,0,0.25);
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
.dice-breakdown {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 5px;
|
||||||
|
font-size: 0.85em;
|
||||||
|
color: var(--cel-cream, #f0e8d4);
|
||||||
|
|
||||||
|
.dval, .nb-dice {
|
||||||
|
font-family: var(--cel-font-title, "CopaseticNF", serif);
|
||||||
|
font-size: 1.6em;
|
||||||
|
color: var(--cel-orange, #c49a1a);
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dlabel { font-size: 0.8em; text-transform: uppercase; letter-spacing: 0.04em; opacity: 0.8; }
|
||||||
|
.dminus { color: #e8a0a0; font-weight: bold; }
|
||||||
|
.deq { opacity: 0.6; }
|
||||||
|
.ddice { font-size: 1em; color: var(--cel-orange, #c49a1a); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.wound-info {
|
||||||
|
font-size: 0.75em;
|
||||||
|
color: #e8a0a0;
|
||||||
|
margin-top: 3px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Phases de lune ──
|
||||||
|
.moon-section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
.moon-section-label {
|
||||||
|
font-size: 0.75em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.06em;
|
||||||
|
color: var(--cel-orange, #c49a1a);
|
||||||
|
}
|
||||||
|
|
||||||
|
.moon-phases {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
.moon-option {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 4px 5px;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 52px;
|
||||||
|
transition: all 0.15s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(196,154,26,0.12);
|
||||||
|
border-color: var(--cel-border, #7a5c20);
|
||||||
|
}
|
||||||
|
&.active {
|
||||||
|
background: rgba(27,56,40,0.15);
|
||||||
|
border-color: var(--cel-orange, #c49a1a);
|
||||||
|
}
|
||||||
|
|
||||||
|
.moon-symbol { font-size: 1.4em; line-height: 1; }
|
||||||
|
.moon-name { font-size: 0.6em; text-align: center; margin-top: 2px; color: #555; }
|
||||||
|
.moon-bonus { font-size: 0.7em; color: var(--cel-orange, #c49a1a); font-weight: bold; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Champs de formulaire ──
|
||||||
.form-group {
|
.form-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -24,8 +139,8 @@
|
|||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
|
|
||||||
label {
|
label {
|
||||||
flex: 0 0 140px;
|
flex: 0 0 130px;
|
||||||
font-size: 0.85em;
|
font-size: 0.82em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.04em;
|
letter-spacing: 0.04em;
|
||||||
color: var(--cel-orange, #c49a1a);
|
color: var(--cel-orange, #c49a1a);
|
||||||
@@ -35,23 +150,62 @@
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
border: 1px solid var(--cel-border, #7a5c20);
|
border: 1px solid var(--cel-border, #7a5c20);
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
padding: 2px 6px;
|
padding: 3px 6px;
|
||||||
background: rgba(255,255,255,0.7);
|
background: rgba(255,255,255,0.75);
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.form-row label { flex: 0 0 130px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-two-col {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
label {
|
||||||
|
flex: none;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-destin {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
.destin-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--cel-orange, #c49a1a);
|
||||||
|
font-size: 0.85em;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
|
||||||
|
input[type="checkbox"] { width: 14px; height: 14px; flex-shrink: 0; }
|
||||||
|
.destin-icon { font-size: 1em; flex-shrink: 0; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Prévisualisation ──
|
||||||
.dice-preview {
|
.dice-preview {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 1em;
|
margin-top: 4px;
|
||||||
margin-top: 10px;
|
padding: 8px;
|
||||||
padding: 6px;
|
|
||||||
background: rgba(27,56,40,0.08);
|
background: rgba(27,56,40,0.08);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
border: 1px solid var(--cel-green, #1b3828);
|
border: 1px solid var(--cel-green, #1b3828);
|
||||||
|
|
||||||
.nb-dice {
|
.preview-formula {
|
||||||
font-family: var(--cel-font-title, "CopaseticNF", serif);
|
font-family: var(--cel-font-title, "CopaseticNF", serif);
|
||||||
font-size: 1.5em;
|
font-size: 1.4em;
|
||||||
color: var(--cel-orange, #c49a1a);
|
color: var(--cel-orange, #c49a1a);
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
@@ -66,6 +220,7 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
font-family: var(--cel-font-body, "Palatino Linotype", serif);
|
font-family: var(--cel-font-body, "Palatino Linotype", serif);
|
||||||
|
|
||||||
|
// ── En-tête ──
|
||||||
.roll-header {
|
.roll-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -77,100 +232,183 @@
|
|||||||
border-bottom: 2px solid var(--cel-orange, #c49a1a);
|
border-bottom: 2px solid var(--cel-orange, #c49a1a);
|
||||||
|
|
||||||
.actor-img {
|
.actor-img {
|
||||||
width: 36px;
|
width: 40px;
|
||||||
height: 36px;
|
height: 40px;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
border: 1px solid var(--cel-orange, #c49a1a);
|
border: 1px solid var(--cel-orange, #c49a1a);
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
box-shadow: inset 0 0 4px rgba(196,154,26,0.3);
|
box-shadow: inset 0 0 4px rgba(196,154,26,0.3);
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.roll-info {
|
.roll-info {
|
||||||
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
gap: 1px;
|
||||||
|
|
||||||
.actor-name {
|
.actor-name {
|
||||||
font-family: var(--cel-font-title, "CopaseticNF", serif);
|
font-family: var(--cel-font-title, "CopaseticNF", serif);
|
||||||
color: var(--cel-orange, #c49a1a);
|
color: var(--cel-orange, #c49a1a);
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
letter-spacing: 0.05em;
|
letter-spacing: 0.05em;
|
||||||
|
font-size: 0.95em;
|
||||||
}
|
}
|
||||||
.skill-info {
|
.skill-info {
|
||||||
color: var(--cel-cream, #f0e8d4);
|
color: var(--cel-cream, #f0e8d4);
|
||||||
font-size: 0.8em;
|
font-size: 0.78em;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
|
.stat-lbl { color: var(--cel-orange-light, #ddb84a); }
|
||||||
|
.sep { margin: 0 2px; color: var(--cel-border, #7a5c20); }
|
||||||
}
|
}
|
||||||
}
|
.wound-info {
|
||||||
}
|
font-size: 0.72em;
|
||||||
|
color: #e8a0a0;
|
||||||
.roll-details {
|
|
||||||
padding: 8px;
|
|
||||||
background: var(--cel-cream, #f0e8d4);
|
|
||||||
|
|
||||||
.dice-results {
|
|
||||||
display: flex;
|
|
||||||
gap: 4px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
margin-bottom: 6px;
|
|
||||||
|
|
||||||
.die.d6 {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 28px;
|
|
||||||
height: 28px;
|
|
||||||
border: 2px solid var(--cel-border, #7a5c20);
|
|
||||||
border-radius: 3px;
|
|
||||||
background: white;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 1em;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.bonus-line {
|
.moon-badge {
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
font-size: 0.85em;
|
|
||||||
color: #666;
|
|
||||||
padding: 1px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.roll-total-line {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
font-size: 0.75em;
|
||||||
margin-top: 6px;
|
.moon-sym { font-size: 1.3em; line-height: 1; }
|
||||||
padding-top: 4px;
|
.moon-bon { color: var(--cel-orange-light, #ddb84a); font-weight: bold; }
|
||||||
border-top: 1px solid var(--cel-border, #7a5c20);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.total-label {
|
// ── Zone dés ──
|
||||||
text-transform: uppercase;
|
.dice-zone {
|
||||||
font-size: 0.75em;
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 5px;
|
||||||
|
padding: 8px 10px 4px;
|
||||||
|
background: var(--cel-cream, #f0e8d4);
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.die-face {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
border: 2px solid var(--cel-border, #7a5c20);
|
||||||
|
border-radius: 4px;
|
||||||
|
background: white;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1em;
|
||||||
|
font-family: var(--cel-font-title, "CopaseticNF", serif);
|
||||||
|
box-shadow: 1px 1px 2px rgba(0,0,0,0.15);
|
||||||
|
|
||||||
|
&.max {
|
||||||
|
background: var(--cel-green, #1b3828);
|
||||||
color: var(--cel-orange, #c49a1a);
|
color: var(--cel-orange, #c49a1a);
|
||||||
|
border-color: var(--cel-orange, #c49a1a);
|
||||||
}
|
}
|
||||||
|
&.min {
|
||||||
.total-value {
|
background: #f9e8e8;
|
||||||
font-family: var(--cel-font-title, "CopaseticNF", serif);
|
color: #a03030;
|
||||||
font-size: 1.6em;
|
border-color: #c07070;
|
||||||
font-weight: bold;
|
|
||||||
color: var(--cel-orange, #c49a1a);
|
|
||||||
}
|
|
||||||
|
|
||||||
.vs-difficulty {
|
|
||||||
font-size: 0.8em;
|
|
||||||
color: #888;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.roll-result-banner {
|
// ── Formule ──
|
||||||
|
.formula-line {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 3px;
|
||||||
|
padding: 4px 10px;
|
||||||
|
background: var(--cel-cream, #f0e8d4);
|
||||||
|
font-size: 0.85em;
|
||||||
|
color: #555;
|
||||||
|
|
||||||
|
.fl-ndice { color: var(--cel-green, #1b3828); font-weight: bold; }
|
||||||
|
.fl-sum { font-weight: bold; }
|
||||||
|
.fl-total {
|
||||||
|
font-family: var(--cel-font-title, "CopaseticNF", serif);
|
||||||
|
font-size: 1.4em;
|
||||||
|
color: var(--cel-orange, #c49a1a);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.fl-moon { color: #666; }
|
||||||
|
.fl-mod { color: #444; }
|
||||||
|
.fl-asp { color: var(--cel-orange, #c49a1a); }
|
||||||
|
.fl-sep { font-weight: bold; color: var(--cel-border, #7a5c20); margin: 0 2px; }
|
||||||
|
.fl-eq { color: #888; }
|
||||||
|
.fl-op { color: #888; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Seuil et marge ──
|
||||||
|
.threshold-line {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 5px;
|
||||||
|
padding: 4px 10px 6px;
|
||||||
|
background: var(--cel-cream, #f0e8d4);
|
||||||
|
border-top: 1px solid rgba(122,92,32,0.25);
|
||||||
|
font-size: 0.82em;
|
||||||
|
|
||||||
|
.vs-label { color: #888; text-transform: uppercase; font-size: 0.85em; }
|
||||||
|
.diff-label{ font-style: italic; color: var(--cel-green, #1b3828); }
|
||||||
|
.diff-val { color: #888; }
|
||||||
|
|
||||||
|
.margin-badge {
|
||||||
|
margin-left: 6px;
|
||||||
|
padding: 1px 7px;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 0.95em;
|
||||||
|
font-family: var(--cel-font-title, "CopaseticNF", serif);
|
||||||
|
|
||||||
|
&.above {
|
||||||
|
background: var(--cel-green, #1b3828);
|
||||||
|
color: var(--cel-orange, #c49a1a);
|
||||||
|
border: 1px solid var(--cel-orange, #c49a1a);
|
||||||
|
}
|
||||||
|
&.below {
|
||||||
|
background: var(--cel-accent, #6b1e28);
|
||||||
|
color: #e8c8c8;
|
||||||
|
border: 1px solid #8b3e48;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Destin utilisé ──
|
||||||
|
.used-info {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 5px 8px;
|
font-size: 0.78em;
|
||||||
|
color: var(--cel-orange, #c49a1a);
|
||||||
|
padding: 2px 8px;
|
||||||
|
background: var(--cel-cream-dark, #e0d4b8);
|
||||||
|
border-top: 1px dashed var(--cel-border, #7a5c20);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Bandeau résultat ──
|
||||||
|
.roll-result-banner {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 6px 8px;
|
||||||
font-family: var(--cel-font-title, "CopaseticNF", serif);
|
font-family: var(--cel-font-title, "CopaseticNF", serif);
|
||||||
font-size: 1.1em;
|
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.1em;
|
letter-spacing: 0.1em;
|
||||||
|
|
||||||
// Success: jade green with gold text — elegant
|
.result-icon { font-size: 0.9em; opacity: 0.8; }
|
||||||
|
.result-label { font-size: 1.15em; line-height: 1.2; }
|
||||||
|
.result-desc {
|
||||||
|
font-size: 0.65em;
|
||||||
|
letter-spacing: 0.12em;
|
||||||
|
margin-top: 1px;
|
||||||
|
opacity: 0.85;
|
||||||
|
text-transform: none;
|
||||||
|
font-style: italic;
|
||||||
|
font-family: var(--cel-font-body, serif);
|
||||||
|
}
|
||||||
|
|
||||||
&.success {
|
&.success {
|
||||||
background-color: var(--cel-green, #1b3828);
|
background-color: var(--cel-green, #1b3828);
|
||||||
background-image: url("../assets/ui/fond_cadrille.jpg");
|
background-image: url("../assets/ui/fond_cadrille.jpg");
|
||||||
@@ -178,16 +416,26 @@
|
|||||||
color: var(--cel-orange, #c49a1a);
|
color: var(--cel-orange, #c49a1a);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Failure: deep burgundy — dark and grave
|
&.critical-success {
|
||||||
|
background: linear-gradient(135deg, #1b3828, #2c5a3f);
|
||||||
|
background-image: url("../assets/ui/fond_cadrille.jpg");
|
||||||
|
background-blend-mode: soft-light;
|
||||||
|
color: var(--cel-orange-light, #ddb84a);
|
||||||
|
border-top: 2px solid var(--cel-orange, #c49a1a);
|
||||||
|
border-bottom: 2px solid var(--cel-orange, #c49a1a);
|
||||||
|
text-shadow: 0 0 8px rgba(196,154,26,0.5);
|
||||||
|
}
|
||||||
|
|
||||||
&.failure {
|
&.failure {
|
||||||
background: var(--cel-accent, #6b1e28);
|
background: var(--cel-accent, #6b1e28);
|
||||||
color: #e8c8c8;
|
color: #e8c8c8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.critical {
|
&.critical-failure {
|
||||||
display: block;
|
background: linear-gradient(135deg, #3d0f18, #6b1e28);
|
||||||
font-size: 0.75em;
|
color: #f0c8c8;
|
||||||
letter-spacing: 0.12em;
|
border-top: 2px solid #8b3e48;
|
||||||
|
border-bottom: 2px solid #8b3e48;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
"verified": "13"
|
"verified": "13"
|
||||||
},
|
},
|
||||||
"esmodules": ["fvtt-celestopol.mjs"],
|
"esmodules": ["fvtt-celestopol.mjs"],
|
||||||
"styles": ["css/fvtt-celestopol.css"],
|
"styles": ["css/fvtt-celestopol.css?v=1774698726"],
|
||||||
"languages": [
|
"languages": [
|
||||||
{
|
{
|
||||||
"lang": "fr",
|
"lang": "fr",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<div class="item-sheet anomaly">
|
<div class="item-sheet anomaly">
|
||||||
<header class="item-header">
|
<header class="item-header">
|
||||||
<div class="item-portrait" data-action="editImage" data-edit="img">
|
<div class="item-portrait" data-action="editImage">
|
||||||
<img src="{{item.img}}" alt="{{item.name}}">
|
<img src="{{item.img}}" alt="{{item.name}}">
|
||||||
</div>
|
</div>
|
||||||
<div class="item-header-fields">
|
<div class="item-header-fields">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<div class="item-sheet aspect">
|
<div class="item-sheet aspect">
|
||||||
<header class="item-header">
|
<header class="item-header">
|
||||||
<div class="item-portrait" data-action="editImage" data-edit="img">
|
<div class="item-portrait" data-action="editImage">
|
||||||
<img src="{{item.img}}" alt="{{item.name}}">
|
<img src="{{item.img}}" alt="{{item.name}}">
|
||||||
</div>
|
</div>
|
||||||
<div class="item-header-fields">
|
<div class="item-header-fields">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<div class="item-sheet attribute">
|
<div class="item-sheet attribute">
|
||||||
<header class="item-header">
|
<header class="item-header">
|
||||||
<div class="item-portrait" data-action="editImage" data-edit="img">
|
<div class="item-portrait" data-action="editImage">
|
||||||
<img src="{{item.img}}" alt="{{item.name}}">
|
<img src="{{item.img}}" alt="{{item.name}}">
|
||||||
</div>
|
</div>
|
||||||
<div class="item-header-fields">
|
<div class="item-header-fields">
|
||||||
|
|||||||
@@ -12,7 +12,10 @@
|
|||||||
<div class="track-box {{#if (lookup ../system.blessures key 'checked')}}checked{{/if}}">
|
<div class="track-box {{#if (lookup ../system.blessures key 'checked')}}checked{{/if}}">
|
||||||
<input type="checkbox" name="system.blessures.{{key}}.checked"
|
<input type="checkbox" name="system.blessures.{{key}}.checked"
|
||||||
{{#if (lookup ../system.blessures key 'checked')}}checked{{/if}}
|
{{#if (lookup ../system.blessures key 'checked')}}checked{{/if}}
|
||||||
{{#unless ../isEditable}}disabled{{/unless}}>
|
{{#unless ../isEditable}}disabled{{/unless}}
|
||||||
|
class="wound-checkbox"
|
||||||
|
data-track="blessures"
|
||||||
|
data-index="{{idx}}">
|
||||||
<label class="box-label">{{lookup ../system.blessures key 'malus'}}</label>
|
<label class="box-label">{{lookup ../system.blessures key 'malus'}}</label>
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
@@ -37,7 +40,10 @@
|
|||||||
<div class="track-box destiny {{#if (lookup ../system.destin key 'checked')}}checked{{/if}}">
|
<div class="track-box destiny {{#if (lookup ../system.destin key 'checked')}}checked{{/if}}">
|
||||||
<input type="checkbox" name="system.destin.{{key}}.checked"
|
<input type="checkbox" name="system.destin.{{key}}.checked"
|
||||||
{{#if (lookup ../system.destin key 'checked')}}checked{{/if}}
|
{{#if (lookup ../system.destin key 'checked')}}checked{{/if}}
|
||||||
{{#unless ../isEditable}}disabled{{/unless}}>
|
{{#unless ../isEditable}}disabled{{/unless}}
|
||||||
|
class="wound-checkbox"
|
||||||
|
data-track="destin"
|
||||||
|
data-index="{{@index}}">
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
@@ -61,7 +67,10 @@
|
|||||||
<div class="track-box spleen {{#if (lookup ../system.spleen key 'checked')}}checked{{/if}}">
|
<div class="track-box spleen {{#if (lookup ../system.spleen key 'checked')}}checked{{/if}}">
|
||||||
<input type="checkbox" name="system.spleen.{{key}}.checked"
|
<input type="checkbox" name="system.spleen.{{key}}.checked"
|
||||||
{{#if (lookup ../system.spleen key 'checked')}}checked{{/if}}
|
{{#if (lookup ../system.spleen key 'checked')}}checked{{/if}}
|
||||||
{{#unless ../isEditable}}disabled{{/unless}}>
|
{{#unless ../isEditable}}disabled{{/unless}}
|
||||||
|
class="wound-checkbox"
|
||||||
|
data-track="spleen"
|
||||||
|
data-index="{{@index}}">
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<div class="tab competences {{tab.cssClass}}" data-group="sheet" data-tab="competences">
|
<div class="tab competences {{tab.cssClass}}" data-group="sheet" data-tab="competences">
|
||||||
{{!-- Grille des 4 stats × 4 compétences --}}
|
{{!-- Grille des 4 stats × 4 domaines --}}
|
||||||
<div class="stats-grid">
|
<div class="stats-grid">
|
||||||
{{#each stats as |stat statId|}}
|
{{#each stats as |stat statId|}}
|
||||||
<div class="stat-block">
|
<div class="stat-block">
|
||||||
@@ -8,26 +8,50 @@
|
|||||||
<div class="stat-res">
|
<div class="stat-res">
|
||||||
<label>{{localize "CELESTOPOL.Stat.res"}}</label>
|
<label>{{localize "CELESTOPOL.Stat.res"}}</label>
|
||||||
{{#if ../isEditMode}}
|
{{#if ../isEditMode}}
|
||||||
<input type="number" name="system.stats.{{statId}}.res" value="{{lookup ../system.stats statId 'res'}}" min="0" max="8" class="stat-res-input">
|
<input type="number" name="system.stats.{{statId}}.res" value="{{lookup (lookup ../system.stats statId) 'res'}}" min="0" max="8" class="stat-res-input">
|
||||||
{{else}}
|
{{else}}
|
||||||
<span class="stat-res-value">{{lookup ../system.stats statId 'res'}}</span>
|
<span class="stat-res-value">{{lookup (lookup ../system.stats statId) 'res'}}</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="skills-list">
|
<div class="skills-list">
|
||||||
{{#each (lookup ../skills statId) as |skill skillId|}}
|
{{#each (lookup ../skills statId) as |skill skillId|}}
|
||||||
<div class="skill-row {{#unless ../isEditMode}}rollable{{/unless}}"
|
{{#if @root.isEditMode}}
|
||||||
data-stat-id="{{statId}}" data-skill-id="{{skillId}}"
|
<div class="skill-row edit-mode" data-stat-id="{{statId}}" data-skill-id="{{skillId}}">
|
||||||
title="{{#unless ../isEditMode}}{{localize 'CELESTOPOL.Roll.clickToRoll'}}{{/unless}}">
|
|
||||||
<span class="skill-name">{{localize skill.label}}</span>
|
<span class="skill-name">{{localize skill.label}}</span>
|
||||||
{{#if ../isEditMode}}
|
<div class="skill-checkboxes-container">
|
||||||
<input type="number" name="system.stats.{{statId}}.{{skillId}}.value"
|
<div class="skill-checkboxes">
|
||||||
value="{{lookup (lookup ../system.stats statId) skillId 'value'}}"
|
{{#each (array 1 2 3 4 5 6 7 8) as |level|}}
|
||||||
min="0" max="8" class="skill-value-input">
|
<label class="skill-checkbox-wrapper">
|
||||||
{{else}}
|
<input type="checkbox" name="system.stats.{{statId}}.{{skillId}}.level{{level}}"
|
||||||
<span class="skill-value">{{lookup (lookup ../system.stats statId) skillId 'value'}}</span>
|
{{#if (lookup (lookup (lookup @root.system.stats statId) skillId) (concat 'level' level))}}checked{{/if}}
|
||||||
{{/if}}
|
class="skill-level-checkbox">
|
||||||
|
</label>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input type="number" name="system.stats.{{statId}}.{{skillId}}.value"
|
||||||
|
value="{{lookup (lookup @root.system.stats statId) skillId 'value'}}"
|
||||||
|
min="0" max="8" class="skill-value-input">
|
||||||
</div>
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="skill-row rollable" data-stat-id="{{statId}}" data-skill-id="{{skillId}}"
|
||||||
|
title="{{localize 'CELESTOPOL.Roll.clickToRoll'}}">
|
||||||
|
<span class="skill-name">{{localize skill.label}}</span>
|
||||||
|
<div class="skill-checkboxes-container">
|
||||||
|
<div class="skill-checkboxes">
|
||||||
|
{{#each (array 1 2 3 4 5 6 7 8) as |level|}}
|
||||||
|
<label class="skill-checkbox-wrapper">
|
||||||
|
<input type="checkbox"
|
||||||
|
{{#if (lookup (lookup (lookup @root.system.stats statId) skillId) (concat 'level' level))}}checked{{/if}}
|
||||||
|
disabled class="skill-level-checkbox">
|
||||||
|
</label>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="skill-value">{{lookup (lookup @root.system.stats statId) skillId 'value'}}</span>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,21 +8,44 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{{#each factions as |faction factionId|}}
|
{{#each factions as |faction factionId|}}
|
||||||
<tr class="faction-row">
|
<tr class="faction-row" data-faction="{{factionId}}">
|
||||||
<td class="faction-name">{{localize faction.label}}</td>
|
<td class="faction-name">{{localize faction.label}}</td>
|
||||||
<td class="faction-value">
|
<td class="faction-value">
|
||||||
{{#if ../isEditMode}}
|
<div class="faction-checkboxes-container">
|
||||||
<input type="number" name="system.factions.{{factionId}}.value"
|
<div class="faction-checkboxes">
|
||||||
value="{{lookup ../system.factions factionId 'value'}}" min="0">
|
{{#each (array 1 2 3 4 5 6 7 8 9) as |level|}}
|
||||||
{{else}}
|
{{#if @root.isEditMode}}
|
||||||
<span>{{lookup ../system.factions factionId 'value'}}</span>
|
<label class="faction-checkbox-wrapper">
|
||||||
{{/if}}
|
<input type="checkbox" name="system.factions.{{factionId}}.level{{level}}"
|
||||||
|
{{#if (lookup (lookup @root.system.factions factionId) (concat 'level' level))}}checked{{/if}}
|
||||||
|
class="faction-checkbox"
|
||||||
|
data-faction="{{factionId}}"
|
||||||
|
data-level="{{level}}">
|
||||||
|
</label>
|
||||||
|
{{else}}
|
||||||
|
<label class="faction-checkbox-wrapper">
|
||||||
|
<input type="checkbox"
|
||||||
|
{{#if (lookup (lookup @root.system.factions factionId) (concat 'level' level))}}checked{{/if}}
|
||||||
|
disabled class="faction-checkbox">
|
||||||
|
</label>
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
<span class="faction-count">
|
||||||
|
{{#if ../isEditMode}}
|
||||||
|
<input type="number" name="system.factions.{{factionId}}.value"
|
||||||
|
value="{{lookup (lookup ../system.factions factionId) 'value'}}" min="0" max="9" class="faction-value-input">
|
||||||
|
{{else}}
|
||||||
|
{{lookup (lookup ../system.factions factionId) 'value'}}
|
||||||
|
{{/if}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
||||||
{{!-- Factions personnalisées --}}
|
{{!-- Factions personnalisées --}}
|
||||||
<tr class="faction-row custom">
|
<tr class="faction-row custom" data-faction="perso1">
|
||||||
<td>
|
<td>
|
||||||
{{#if isEditMode}}
|
{{#if isEditMode}}
|
||||||
<input type="text" name="system.factions.perso1.label"
|
<input type="text" name="system.factions.perso1.label"
|
||||||
@@ -33,14 +56,38 @@
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{#if isEditMode}}
|
<div class="faction-checkboxes-container">
|
||||||
<input type="number" name="system.factions.perso1.value" value="{{system.factions.perso1.value}}" min="0">
|
<div class="faction-checkboxes">
|
||||||
{{else}}
|
{{#each (array 1 2 3 4 5 6 7 8 9) as |level|}}
|
||||||
<span>{{system.factions.perso1.value}}</span>
|
{{#if ../isEditMode}}
|
||||||
{{/if}}
|
<label class="faction-checkbox-wrapper">
|
||||||
|
<input type="checkbox" name="system.factions.perso1.level{{level}}"
|
||||||
|
{{#if (lookup ../system.factions.perso1 (concat 'level' level))}}checked{{/if}}
|
||||||
|
class="faction-checkbox"
|
||||||
|
data-faction="perso1"
|
||||||
|
data-level="{{level}}">
|
||||||
|
</label>
|
||||||
|
{{else}}
|
||||||
|
<label class="faction-checkbox-wrapper">
|
||||||
|
<input type="checkbox"
|
||||||
|
{{#if (lookup ../system.factions.perso1 (concat 'level' level))}}checked{{/if}}
|
||||||
|
disabled class="faction-checkbox">
|
||||||
|
</label>
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
<span class="faction-count">
|
||||||
|
{{#if ../isEditMode}}
|
||||||
|
<input type="number" name="system.factions.perso1.value"
|
||||||
|
value="{{system.factions.perso1.value}}" min="0" max="9" class="faction-value-input">
|
||||||
|
{{else}}
|
||||||
|
{{system.factions.perso1.value}}
|
||||||
|
{{/if}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="faction-row custom">
|
<tr class="faction-row custom" data-faction="perso2">
|
||||||
<td>
|
<td>
|
||||||
{{#if isEditMode}}
|
{{#if isEditMode}}
|
||||||
<input type="text" name="system.factions.perso2.label"
|
<input type="text" name="system.factions.perso2.label"
|
||||||
@@ -51,11 +98,35 @@
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{#if isEditMode}}
|
<div class="faction-checkboxes-container">
|
||||||
<input type="number" name="system.factions.perso2.value" value="{{system.factions.perso2.value}}" min="0">
|
<div class="faction-checkboxes">
|
||||||
{{else}}
|
{{#each (array 1 2 3 4 5 6 7 8 9) as |level|}}
|
||||||
<span>{{system.factions.perso2.value}}</span>
|
{{#if ../isEditMode}}
|
||||||
{{/if}}
|
<label class="faction-checkbox-wrapper">
|
||||||
|
<input type="checkbox" name="system.factions.perso2.level{{level}}"
|
||||||
|
{{#if (lookup ../system.factions.perso2 (concat 'level' level))}}checked{{/if}}
|
||||||
|
class="faction-checkbox"
|
||||||
|
data-faction="perso2"
|
||||||
|
data-level="{{level}}">
|
||||||
|
</label>
|
||||||
|
{{else}}
|
||||||
|
<label class="faction-checkbox-wrapper">
|
||||||
|
<input type="checkbox"
|
||||||
|
{{#if (lookup ../system.factions.perso2 (concat 'level' level))}}checked{{/if}}
|
||||||
|
disabled class="faction-checkbox">
|
||||||
|
</label>
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
<span class="faction-count">
|
||||||
|
{{#if ../isEditMode}}
|
||||||
|
<input type="number" name="system.factions.perso2.value"
|
||||||
|
value="{{system.factions.perso2.value}}" min="0" max="9" class="faction-value-input">
|
||||||
|
{{else}}
|
||||||
|
{{system.factions.perso2.value}}
|
||||||
|
{{/if}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<div class="character-main sheet-part">
|
<div class="character-main sheet-part">
|
||||||
<header class="sheet-header">
|
<header class="sheet-header">
|
||||||
<div class="portrait" data-action="editImage" data-edit="img">
|
<div class="portrait" data-action="editImage">
|
||||||
<img src="{{actor.img}}" alt="{{actor.name}}" class="actor-portrait {{#unless isEditMode}}click-to-edit{{/unless}}">
|
<img src="{{actor.img}}" alt="{{actor.name}}" class="actor-portrait {{#unless isEditMode}}click-to-edit{{/unless}}">
|
||||||
</div>
|
</div>
|
||||||
<div class="header-fields">
|
<div class="header-fields">
|
||||||
|
|||||||
@@ -1,49 +1,88 @@
|
|||||||
<div class="celestopol chat-roll {{resultClass}}">
|
<div class="celestopol chat-roll {{resultClass}}">
|
||||||
|
|
||||||
|
{{!-- En-tête : acteur + domaine --}}
|
||||||
<div class="roll-header">
|
<div class="roll-header">
|
||||||
{{#if actorImg}}
|
{{#if actorImg}}
|
||||||
<img src="{{actorImg}}" class="actor-img" alt="{{actorName}}">
|
<img src="{{actorImg}}" class="actor-img" alt="{{actorName}}">
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<div class="roll-info">
|
<div class="roll-info">
|
||||||
<span class="actor-name">{{actorName}}</span>
|
<span class="actor-name">{{actorName}}</span>
|
||||||
<span class="skill-info">{{statLabel}} › {{skillLabel}}</span>
|
<span class="skill-info">
|
||||||
|
{{#if statLabel}}<span class="stat-lbl">{{statLabel}}</span><span class="sep"> › </span>{{/if}}
|
||||||
|
<span class="skill-lbl">{{skillLabel}}</span>
|
||||||
|
</span>
|
||||||
|
{{#if woundLabel}}
|
||||||
|
<span class="wound-info">⚠ {{woundLabel}}</span>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
<div class="moon-badge" title="{{moonPhaseLabel}}">
|
||||||
|
<span class="moon-sym">{{moonSymbol}}</span>
|
||||||
|
{{#if moonBonus}}<span class="moon-bon">+{{moonBonus}}</span>{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="roll-details">
|
{{!-- Dés --}}
|
||||||
<div class="dice-results">
|
<div class="dice-zone">
|
||||||
{{#each diceResults as |die|}}
|
{{#each diceResults as |die|}}
|
||||||
<span class="die d6">{{die}}</span>
|
<span class="die-face d6 {{#if (eq die 6)}}max{{/if}}{{#if (eq die 1)}}min{{/if}}">{{die}}</span>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{!-- Formule détaillée --}}
|
||||||
|
<div class="formula-line">
|
||||||
|
<span class="fl-ndice">{{nbDice}}d6</span>
|
||||||
|
<span class="fl-eq"> = </span>
|
||||||
|
<span class="fl-sum">{{diceSum}}</span>
|
||||||
{{#if moonBonus}}
|
{{#if moonBonus}}
|
||||||
<div class="bonus-line">
|
<span class="fl-op"> + </span>
|
||||||
<span class="bonus-label">{{localize "CELESTOPOL.Roll.moonBonus"}} ({{moonPhaseLabel}}) :</span>
|
<span class="fl-moon" title="{{moonPhaseLabel}}">{{moonSymbol}} {{moonBonus}}</span>
|
||||||
<span class="bonus-value">+{{moonBonus}}</span>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if modifier}}
|
{{#if modifier}}
|
||||||
<div class="bonus-line">
|
<span class="fl-op"> {{#if (gt modifier 0)}}+{{else}}−{{/if}} </span>
|
||||||
<span class="bonus-label">{{localize "CELESTOPOL.Roll.modifier"}} :</span>
|
<span class="fl-mod">{{abs modifier}}</span>
|
||||||
<span class="bonus-value">{{#if (gt modifier 0)}}+{{/if}}{{modifier}}</span>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{#if aspectMod}}
|
||||||
<div class="roll-total-line">
|
<span class="fl-op"> {{#if (gt aspectMod 0)}}+{{else}}−{{/if}} </span>
|
||||||
<span class="total-label">{{localize "CELESTOPOL.Roll.total"}}</span>
|
<span class="fl-asp" title="{{localize "CELESTOPOL.Roll.usedAspect"}}">✦ {{abs aspectMod}}</span>
|
||||||
<span class="total-value">{{total}}</span>
|
{{/if}}
|
||||||
<span class="vs-difficulty">vs {{difficultyLabel}}</span>
|
<span class="fl-sep"> = </span>
|
||||||
</div>
|
<span class="fl-total">{{total}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{!-- Seuil et marge --}}
|
||||||
|
<div class="threshold-line">
|
||||||
|
<span class="vs-label">vs</span>
|
||||||
|
<span class="diff-label">{{difficultyLabel}}</span>
|
||||||
|
<span class="diff-val">({{difficultyValue}})</span>
|
||||||
|
{{#if margin}}
|
||||||
|
<span class="margin-badge {{#if marginAbove}}above{{else}}below{{/if}}">
|
||||||
|
{{#if marginAbove}}+{{/if}}{{margin}}
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{!-- Infos bonus (Destin utilisé) --}}
|
||||||
|
{{#if useDestin}}
|
||||||
|
<div class="used-info">
|
||||||
|
<span class="used-destin">⭐ {{localize "CELESTOPOL.Roll.usedDestin"}}</span>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{!-- Bandeau résultat --}}
|
||||||
<div class="roll-result-banner {{resultClass}}">
|
<div class="roll-result-banner {{resultClass}}">
|
||||||
{{#if success}}
|
{{#if isCriticalSuccess}}
|
||||||
<span class="result-label success">{{localize "CELESTOPOL.Roll.success"}}</span>
|
<span class="result-icon">✦</span>
|
||||||
{{#if criticalSuccess}}<span class="critical">{{localize "CELESTOPOL.Roll.criticalSuccess"}}</span>{{/if}}
|
<span class="result-label">{{localize "CELESTOPOL.Roll.criticalSuccess"}}</span>
|
||||||
{{else}}
|
<span class="result-desc">{{localize "CELESTOPOL.Roll.criticalSuccessDesc"}}</span>
|
||||||
<span class="result-label failure">{{localize "CELESTOPOL.Roll.failure"}}</span>
|
{{else if isSuccess}}
|
||||||
{{#if criticalFailure}}<span class="critical">{{localize "CELESTOPOL.Roll.criticalFailure"}}</span>{{/if}}
|
<span class="result-label">{{localize "CELESTOPOL.Roll.success"}}</span>
|
||||||
|
{{else if isCriticalFailure}}
|
||||||
|
<span class="result-icon">✖</span>
|
||||||
|
<span class="result-label">{{localize "CELESTOPOL.Roll.criticalFailure"}}</span>
|
||||||
|
<span class="result-desc">{{localize "CELESTOPOL.Roll.criticalFailureDesc"}}</span>
|
||||||
|
{{else if isFailure}}
|
||||||
|
<span class="result-label">{{localize "CELESTOPOL.Roll.failure"}}</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<div class="item-sheet equipment">
|
<div class="item-sheet equipment">
|
||||||
<header class="item-header">
|
<header class="item-header">
|
||||||
<div class="item-portrait" data-action="editImage" data-edit="img">
|
<div class="item-portrait" data-action="editImage">
|
||||||
<img src="{{item.img}}" alt="{{item.name}}">
|
<img src="{{item.img}}" alt="{{item.name}}">
|
||||||
</div>
|
</div>
|
||||||
<div class="item-header-fields">
|
<div class="item-header-fields">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<div class="npc-main sheet-part">
|
<div class="npc-main sheet-part">
|
||||||
<header class="sheet-header">
|
<header class="sheet-header">
|
||||||
<div class="portrait" data-action="editImage" data-edit="img">
|
<div class="portrait" data-action="editImage">
|
||||||
<img src="{{actor.img}}" alt="{{actor.name}}" class="actor-portrait">
|
<img src="{{actor.img}}" alt="{{actor.name}}" class="actor-portrait">
|
||||||
</div>
|
</div>
|
||||||
<div class="header-fields">
|
<div class="header-fields">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{{!-- Template partagé pour les scores bonus/malus d'un item par compétence --}}
|
{{!-- Template partagé pour les scores bonus/malus d'un item par domaine --}}
|
||||||
<div class="scores-section">
|
<div class="scores-section">
|
||||||
<div class="scores-header">
|
<div class="scores-header">
|
||||||
<span>{{localize "CELESTOPOL.Item.scores"}}</span>
|
<span>{{localize "CELESTOPOL.Item.scores"}}</span>
|
||||||
|
|||||||
@@ -1,21 +1,48 @@
|
|||||||
<form class="roll-dialog celestopol">
|
<div class="roll-dialog-content">
|
||||||
<div class="roll-title">
|
|
||||||
{{#if statLabel}}<span class="stat-label">{{localize statLabel}}</span><span class="separator"> › </span>{{/if}}
|
{{!-- Info bloc : acteur + domaine --}}
|
||||||
<span class="skill-label">{{localize skillLabel}}</span>
|
<div class="roll-info-block">
|
||||||
|
<div class="roll-actor">{{actorName}}</div>
|
||||||
|
<div class="roll-skill-line">
|
||||||
|
{{#if statLabel}}<span class="stat-label">{{localize statLabel}}</span><span class="sep"> › </span>{{/if}}
|
||||||
|
<span class="skill-label">{{localize skillLabel}}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="roll-dice-summary">
|
||||||
|
<div class="dice-breakdown">
|
||||||
|
<span class="dval">{{skillValue}}</span>
|
||||||
|
<span class="dlabel">{{localize "CELESTOPOL.Roll.nbDiceBase"}}</span>
|
||||||
|
{{#if woundMalus}}
|
||||||
|
<span class="dminus"> − {{abs woundMalus}}</span>
|
||||||
|
<span class="dlabel">{{localize "CELESTOPOL.Roll.woundMalus"}}</span>
|
||||||
|
{{/if}}
|
||||||
|
<span class="deq"> = </span>
|
||||||
|
<span class="nb-dice" id="preview-ndice">{{nbDiceBase}}</span>
|
||||||
|
<span class="ddice">d6</span>
|
||||||
|
</div>
|
||||||
|
{{#if woundLabel}}
|
||||||
|
<div class="wound-info">⚠ {{woundLabel}}</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
{{!-- Sélecteur Phase de Lune --}}
|
||||||
<label for="moonPhase">{{localize "CELESTOPOL.Roll.moonPhase"}}</label>
|
<div class="moon-section">
|
||||||
<select id="moonPhase" name="moonPhase">
|
<div class="moon-section-label">{{localize "CELESTOPOL.Roll.moonPhase"}}</div>
|
||||||
|
<div class="moon-phases" id="moon-phases">
|
||||||
{{#each moonPhaseChoices as |phase key|}}
|
{{#each moonPhaseChoices as |phase key|}}
|
||||||
<option value="{{key}}" {{#if (eq key ../defaultMoonPhase)}}selected{{/if}}>
|
<label class="moon-option {{#if (eq key ../defaultMoonPhase)}}active{{/if}}" data-moon="{{key}}" title="{{localize phase.label}}{{#if phase.bonus}} (+{{phase.bonus}}){{else}} (+0){{/if}}">
|
||||||
{{localize phase.label}} {{#if phase.bonus}}(+{{phase.bonus}}){{else}}(+0){{/if}}
|
<input type="radio" name="moonPhase" value="{{key}}" {{#if (eq key ../defaultMoonPhase)}}checked{{/if}} hidden>
|
||||||
</option>
|
<span class="moon-symbol">{{phase.symbol}}</span>
|
||||||
|
<span class="moon-name">{{localize phase.label}}</span>
|
||||||
|
<span class="moon-bonus">{{#if phase.bonus}}+{{phase.bonus}}{{else}}+0{{/if}}</span>
|
||||||
|
</label>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</select>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
{{!-- Seuil de difficulté --}}
|
||||||
|
<div class="form-group form-row">
|
||||||
<label for="difficulty">{{localize "CELESTOPOL.Roll.difficulty"}}</label>
|
<label for="difficulty">{{localize "CELESTOPOL.Roll.difficulty"}}</label>
|
||||||
<select id="difficulty" name="difficulty">
|
<select id="difficulty" name="difficulty">
|
||||||
{{#each difficultyChoices as |diff key|}}
|
{{#each difficultyChoices as |diff key|}}
|
||||||
@@ -26,14 +53,77 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
{{!-- Modificateur & Aspect en ligne --}}
|
||||||
<label for="modifier">{{localize "CELESTOPOL.Roll.modifier"}}</label>
|
<div class="form-two-col">
|
||||||
<input type="number" id="modifier" name="modifier" value="0" min="-10" max="10">
|
<div class="form-group">
|
||||||
|
<label for="modifier">{{localize "CELESTOPOL.Roll.modifier"}}</label>
|
||||||
|
<input type="number" id="modifier" name="modifier" value="0" min="-10" max="10">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="aspectModifier">{{localize "CELESTOPOL.Roll.aspect"}}</label>
|
||||||
|
<input type="number" id="aspectModifier" name="aspectModifier" value="0" min="-4" max="4">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="dice-preview">
|
{{!-- Destin --}}
|
||||||
<span>{{localize "CELESTOPOL.Roll.nbDice"}}</span>
|
<div class="form-group form-destin">
|
||||||
<span class="nb-dice">{{nbDice}}</span>
|
<label class="destin-label" for="useDestin">
|
||||||
<span>d6</span>
|
<input type="checkbox" id="useDestin" name="useDestin">
|
||||||
|
<span class="destin-icon">⭐</span>
|
||||||
|
{{localize "CELESTOPOL.Roll.destin"}}
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
|
||||||
|
{{!-- Prévisualisation dynamique --}}
|
||||||
|
<div class="dice-preview" id="dice-preview">
|
||||||
|
<span class="preview-formula" id="preview-formula">{{nbDiceBase}}d6</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
const wrap = document.querySelector('.roll-dialog-content');
|
||||||
|
if (!wrap) return;
|
||||||
|
|
||||||
|
const skillVal = {{skillValue}};
|
||||||
|
const woundMalus = {{woundMalus}};
|
||||||
|
const base = Math.max(1, skillVal + woundMalus);
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
const moonBonus = parseInt(wrap.querySelector('input[name="moonPhase"]:checked')?.closest('[data-moon]')
|
||||||
|
?.dataset.moonBonus ?? 0);
|
||||||
|
const modifier = parseInt(wrap.querySelector('#modifier')?.value ?? 0) || 0;
|
||||||
|
const aspectMod = parseInt(wrap.querySelector('#aspectModifier')?.value ?? 0) || 0;
|
||||||
|
const useDestin = wrap.querySelector('#useDestin')?.checked ? 2 : 0;
|
||||||
|
const ndice = Math.max(1, base + useDestin);
|
||||||
|
const totalMod = moonBonus + modifier + aspectMod;
|
||||||
|
const formula = totalMod !== 0 ? `${ndice}d6 + ${totalMod}` : `${ndice}d6`;
|
||||||
|
|
||||||
|
const previewEl = wrap.querySelector('#preview-formula');
|
||||||
|
const ndiceEl = wrap.querySelector('#preview-ndice');
|
||||||
|
if (previewEl) previewEl.textContent = formula;
|
||||||
|
if (ndiceEl) ndiceEl.textContent = ndice;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mettre à jour les classes actives des lunes + moon-bonus
|
||||||
|
wrap.querySelectorAll('.moon-option').forEach(opt => {
|
||||||
|
const input = opt.querySelector('input[type="radio"]');
|
||||||
|
const key = opt.dataset.moon;
|
||||||
|
const bonusMap = { {{#each moonPhaseChoices}}"{{@key}}": {{#if bonus}}{{bonus}}{{else}}0{{/if}}, {{/each}} };
|
||||||
|
opt.dataset.moonBonus = bonusMap[key] ?? 0;
|
||||||
|
input.addEventListener('change', () => {
|
||||||
|
wrap.querySelectorAll('.moon-option').forEach(o => o.classList.remove('active'));
|
||||||
|
opt.classList.add('active');
|
||||||
|
update();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
wrap.querySelectorAll('#modifier, #aspectModifier, #useDestin').forEach(el => {
|
||||||
|
el.addEventListener('change', update);
|
||||||
|
el.addEventListener('input', update);
|
||||||
|
});
|
||||||
|
|
||||||
|
update();
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user