Full SAN management

This commit is contained in:
2025-06-15 23:02:36 +02:00
parent 9e4d76298c
commit 5ba88a1ae5
27 changed files with 433 additions and 41 deletions

View File

@ -170,6 +170,7 @@ i.fvtt-cthulhu-eternal {
background-position: 0%; background-position: 0%;
background-size: 100% 100%; background-size: 100% 100%;
} }
.chat-san-request ul,
.chat-lethal-damage ul { .chat-lethal-damage ul {
list-style-type: none; list-style-type: none;
padding: 0; padding: 0;
@ -177,14 +178,36 @@ i.fvtt-cthulhu-eternal {
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
.chat-san-request ul .result-lethal,
.chat-lethal-damage ul .result-lethal { .chat-lethal-damage ul .result-lethal {
color: var(--color-critical-failure); color: var(--color-critical-failure);
font-family: var(--font-title); font-family: var(--font-title);
} }
.chat-san-request ul .san-loose-buttons,
.chat-lethal-damage ul .san-loose-buttons {
display: flex;
justify-content: center;
align-items: center;
margin: 10px 0;
}
.chat-san-request ul .san-loose-buttons button,
.chat-lethal-damage ul .san-loose-buttons button {
margin: 0 2px;
font-family: var(--font-primary);
font-size: calc(var(--font-size-standard) * 1.1);
border: none;
padding: 2px 2px;
cursor: pointer;
transition: background-color 0.3s;
min-width: 3rem;
max-width: 3rem;
}
.chat-san-request ul .result-non-lethal,
.chat-lethal-damage ul .result-non-lethal { .chat-lethal-damage ul .result-non-lethal {
color: var(--color-failure); color: var(--color-failure);
font-family: var(--font-title); font-family: var(--font-title);
} }
.chat-san-request ul li,
.chat-lethal-damage ul li { .chat-lethal-damage ul li {
margin: 0 10px; margin: 0 10px;
font-family: var(--font-primary); font-family: var(--font-primary);
@ -454,6 +477,14 @@ i.fvtt-cthulhu-eternal {
min-width: 6rem; min-width: 6rem;
flex-grow: 1; flex-grow: 1;
} }
.fvtt-cthulhu-eternal .protagonist-main .protagonist-pc .protagonist-right .san .san-helplessness,
.fvtt-cthulhu-eternal .protagonist-main .protagonist-pc .protagonist-right .san .san-violence {
display: flex;
flex-grow: 1;
}
.fvtt-cthulhu-eternal .protagonist-main .protagonist-pc .protagonist-right .san .label-san-type {
margin-right: 0.5rem;
}
.fvtt-cthulhu-eternal .protagonist-main .protagonist-pc .protagonist-right .san .label-bp { .fvtt-cthulhu-eternal .protagonist-main .protagonist-pc .protagonist-right .san .label-bp {
flex-grow: 1; flex-grow: 1;
max-width: 3rem; max-width: 3rem;

View File

@ -156,12 +156,13 @@ Hooks.on("renderChatMessageHTML", (message, html, data) => {
$(html).find(".healing-roll").click((event) => { $(html).find(".healing-roll").click((event) => {
CthulhuEternalUtils.healingRoll(message) CthulhuEternalUtils.healingRoll(message)
}) })
} $(html).find(".san-loose").click((event) => {
CthulhuEternalUtils.applySANLoss(message, event)
}) })
$(html).find(".san-type").click((event) => {
// Dice-so-nice Ready CthulhuEternalUtils.applySANType(message, event)
Hooks.once("diceSoNiceReady", (dice3d) => { })
//configureDiceSoNice(dice3d) }
}) })
/** /**

View File

@ -516,6 +516,22 @@
} }
}, },
"Label": { "Label": {
"sanLoss5": "You suffered a SAN loss of 5+ ({value}) : you suffer a temporary insanity (Flee, Submit, Struggle or understanding the Unnatural).",
"sanViolenceReset": "The violence SAN loss count has been reset.",
"sanHelplessnessReset": "The helplessness SAN loss count has been reset.",
"sanLoss": "You suffer a SAN loss",
"selectSANType": "Select the type of SAN loss with the buttons below.",
"Violence" : "Violence",
"Helplessness": "Helplessness",
"Unnatural": "Unnatural",
"sanLossViolence": "You suffered a SAN loss due to violence : violence progress path has evolved.",
"sanLossHelplessness": "You suffered a SAN loss due to helplessness : helplessness progress path has evolved.",
"adaptedToViolence": "You are now : Adapted to Violence.",
"adaptedToViolenceShort": "Adapted to Violence",
"adaptedToHelplessness": "You are now : Adapted to Helplessness.",
"adaptedToHelplessnessShort": "Adapted to Helplessness",
"SANTest": "You just rolled a SAN test : please select the SAN loss with the buttons below.",
"breakingPointReached": "Your SAN has reached your Breaking Point : you suffer a disorder (to be discussed with the GM). Reset the BP to its new value.",
"rollNudge": "Roll Nudge", "rollNudge": "Roll Nudge",
"rollDamage": "Roll Damage", "rollDamage": "Roll Damage",
"rollHealing": "Roll Healing", "rollHealing": "Roll Healing",
@ -693,7 +709,10 @@
"skillAlreadyExists": "Skill already exists", "skillAlreadyExists": "Skill already exists",
"WrongEra": "The era of the item does not match the ear of the system", "WrongEra": "The era of the item does not match the ear of the system",
"NoSelectiveFireChoices": "Not enough ammo fo Selective Fire choices for this weapon.", "NoSelectiveFireChoices": "Not enough ammo fo Selective Fire choices for this weapon.",
"NoAmmo": "No more ammo for this weapon. " "NoAmmo": "No more ammo for this weapon. ",
"noRollDataFound": "No roll data found",
"noActorFound": "No actor found for this item.",
"noSanLossFound": "No SAN loss value found."
} }
} }
} }

View File

@ -516,6 +516,24 @@
} }
}, },
"Label": { "Label": {
"sanLoss5": "Perte de SAN de 5+ ({value}) : vous souffrez d'une folie temporaire (Fuite, Soumission, Lutte ou compréhension de l'Inconcevable).",
"sanViolenceReset": "Le décompte des pertes de SAN de violence a été réinitialisé.",
"sanHelplessnessReset": "Le décompte des pertes de SAN d'impuissance a été réinitialisé.",
"sanLoss": "Perte de SAN",
"noSanLoss": "Aucune perte de SAN",
"sanLossInfo": "Sélectionnez la perte de SAN à l'aide des boutons ci-dessous.",
"selectSANType": "Sélectionnez le type de perte de SAN à l'aide des boutons ci-dessous.",
"sanLossViolence": "Vous avez subi une perte de SAN due à la violence : le chemin de progression de la violence a évolué.",
"sanLossHelplessness": "Vous avez subi une perte de SAN due à l'impuissance : le chemin de progression de l'impuissance a évolué.",
"adaptedToViolence": "Vous êtes maintenant : Habitué à la violence",
"adaptedToViolenceShort": "Habitué à la violence",
"adaptedToHelplessness": "Vous êtes maintenant : Habitué à l'impuissance",
"adaptedToHelplessnessShort": "Habitué à l'impuissance",
"SANTest": "Vous venez de faire un jet de SAN : selectionnez la perte de SAN à l'aide des boutons ci-dessous.",
"breakingPointReached": "Vous avez atteint votre Point de Rupture (PR) : vous souffrez d'un trouble mental. Vous devez re-initialiser votre PR à l'aide du bouton disponible dans la section SAN de la fiche de PJ.",
"Violence" : "Violence",
"Helplessness": "Impuissance",
"Unnatural": "Inconcevable",
"rollNudge": "Modifier le jet", "rollNudge": "Modifier le jet",
"rollDamage": "Jet de dégâts", "rollDamage": "Jet de dégâts",
"rollHealing": "Jet de soin", "rollHealing": "Jet de soin",
@ -693,7 +711,10 @@
"skillAlreadyExists": "La compétence existe déja", "skillAlreadyExists": "La compétence existe déja",
"WrongEra": "L'époque de l'item ne correspond pas à celle du jeu en cours.", "WrongEra": "L'époque de l'item ne correspond pas à celle du jeu en cours.",
"NoSelectiveFireChoices": "Aucune option de tir sélectif n'est disponible pour cette arme : pas assez de munitions.", "NoSelectiveFireChoices": "Aucune option de tir sélectif n'est disponible pour cette arme : pas assez de munitions.",
"NoAmmo": "Aucune munition disponible pour cette arme." "NoAmmo": "Aucune munition disponible pour cette arme.",
"noRollDataFound": "Aucune donnée de jet trouvée.",
"noActorFound": "Aucun protagoniste trouvé.",
"noSanLossFound": "Aucune valeur de perte de SAN trouvée."
} }
} }
} }

View File

@ -78,6 +78,7 @@ export default class CthulhuEternalProtagonistSheet extends CthulhuEternalActorS
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description, { async: true }) context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description, { async: true })
context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.notes, { async: true }) context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.notes, { async: true })
context.isGM = game.user.isGM
context.tooltipsCharacteristic = { context.tooltipsCharacteristic = {
str: game.i18n.localize("CTHULHUETERNAL.Characteristic.Str"), str: game.i18n.localize("CTHULHUETERNAL.Characteristic.Str"),

View File

@ -568,7 +568,7 @@ export default class CthulhuEternalRoll extends Roll {
{ rollMode: rollMode }, { rollMode: rollMode },
) )
console.log("Roll to message", this.options, this.options.rollData, this.options.rollItem) // Manage the skill evolution if the roll is a failure
let rollData = this.options.rollData || this.options let rollData = this.options.rollData || this.options
let rollItem = this.options.rollItem let rollItem = this.options.rollItem
if (rollData.resultType.includes("failure") && rollItem.type === "skill") { if (rollData.resultType.includes("failure") && rollItem.type === "skill") {
@ -594,6 +594,20 @@ export default class CthulhuEternalRoll extends Roll {
} }
} }
// If the roll is a SAN roll, we propose to select the SAN loss
if (rollData.rollType === "san") {
let msgData = {
rollItem: rollItem,
rollData: rollData
}
let msg = await foundry.applications.handlebars.renderTemplate("systems/fvtt-cthulhu-eternal/templates/chat-san-request.hbs", msgData)
let chatMsg = await ChatMessage.create({
user: game.user.id,
content: msg,
speaker: ChatMessage.getSpeaker({ actor: rollData.actor })
}, { rollMode: rollData.rollMode, create: true })
await chatMsg.setFlag("fvtt-cthulhu-eternal", "rollData", rollData)
}
} }
} }

View File

@ -14,7 +14,8 @@ export default class CthulhuEternalProtagonist extends foundry.abstract.TypeData
const characteristicField = (label) => { const characteristicField = (label) => {
const schema = { const schema = {
value: new fields.NumberField({ ...requiredInteger, initial: 3, min: 0 }), value: new fields.NumberField({ ...requiredInteger, initial: 3, min: 0 }),
feature: new fields.StringField({ required: true, nullable: false, initial: "" }) feature: new fields.StringField({ required: true, nullable: false, initial: "" }),
max: new fields.NumberField({ ...requiredInteger, initial: 3, min: 0 })
} }
return new fields.SchemaField(schema, { label }) return new fields.SchemaField(schema, { label })
} }
@ -47,6 +48,7 @@ export default class CthulhuEternalProtagonist extends foundry.abstract.TypeData
violence: new fields.ArrayField(new fields.BooleanField(), { required: true, initial: [false, false, false], min: 3, max: 3 }), violence: new fields.ArrayField(new fields.BooleanField(), { required: true, initial: [false, false, false], min: 3, max: 3 }),
helplessness: new fields.ArrayField(new fields.BooleanField(), { required: true, initial: [false, false, false], min: 3, max: 3 }), helplessness: new fields.ArrayField(new fields.BooleanField(), { required: true, initial: [false, false, false], min: 3, max: 3 }),
breakingPoint: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), breakingPoint: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
breakingPointReached: new fields.BooleanField({ required: true, initial: false }),
insanity: new fields.StringField({ required: true, nullable: false, initial: "none", choices: SYSTEM.INSANITY }), insanity: new fields.StringField({ required: true, nullable: false, initial: "none", choices: SYSTEM.INSANITY }),
}) })
@ -132,6 +134,15 @@ export default class CthulhuEternalProtagonist extends foundry.abstract.TypeData
updates[`system.damageBonus`] = dmgBonus updates[`system.damageBonus`] = dmgBonus
} }
// BP (Breaking Point) management
if (!this.san.breakingPointReached && this.san.value <= this.san.breakingPoint) {
updates[`system.san.breakingPointReached`] = true
ChatMessage.create({
content: `<p>${game.i18n.format("CTHULHUETERNAL.Label.breakingPointReached", { bp: this.san.breakingPoint, san: this.san.value })}</p>`,
speaker: ChatMessage.getSpeaker({ actor: this.parent })
})
}
// Unconsciousness management // Unconsciousness management
if (!this.hp.unconscious && this.hp.value <= 2) { if (!this.hp.unconscious && this.hp.value <= 2) {
updates[`system.hp.unconscious`] = true updates[`system.hp.unconscious`] = true
@ -183,6 +194,108 @@ export default class CthulhuEternalProtagonist extends foundry.abstract.TypeData
} }
} }
async applySANConsequences(rollData) {
// If sanType is "non", do nothing
if (rollData.sanType === "none") {
return
}
let msgData = {
sanType: rollData.sanType,
sanLoss: rollData.sanLoss,
actorId: this.parent.id,
actorName: this.parent.name,
adaptedToHelplessness: this.biodata.adaptedToHelplessness,
adaptedToViolence: this.biodata.adaptedToViolence,
... rollData
}
let updates = {}
let template = ""
// Manage temporary insanity
if (rollData.sanLoss >= 5) {
rollData.resetMsg = false
if (rollData.sanType === "violence" && !this.biodata.adaptedToViolence) {
updates[`system.san.violence`] = [false, false, false]
rollData.resetMsg = "CTHULHUETERNAL.Label.sanViolenceReset"
}
if (rollData.sanType === "helplessness" && !this.biodata.adaptedToHelplessness) {
updates[`system.san.helplessness`] = [false, false, false]
rollData.resetMsg = "CTHULHUETERNAL.Label.sanHelplessnessReset"
}
template = "systems/fvtt-cthulhu-eternal/templates/chat-san-temp-insanity.hbs"
} else if (rollData.sanLoss === 0) { // Manage if sanLoss is 0
rollData.resetMsg = false
if (rollData.sanType === "violence" && !this.biodata.adaptedToViolence) {
updates[`system.san.violence`] = [false, false, false]
rollData.resetMsg = "CTHULHUETERNAL.Label.sanViolenceReset"
}
if (rollData.sanType === "helplessness" && !this.biodata.adaptedToHelplessness) {
updates[`system.san.helplessness`] = [false, false, false]
rollData.resetMsg = "CTHULHUETERNAL.Label.sanHelplessnessReset"
}
template = "systems/fvtt-cthulhu-eternal/templates/chat-san-loss-0.hbs"
} else if (rollData.sanType === "violence" ) {
// Set the first false element of the violence array to true
let violence = this.san.violence.slice()
let index = violence.findIndex(v => !v)
if (index !== -1) {
violence[index] = true
updates[`system.san.violence`] = violence
}
template = "systems/fvtt-cthulhu-eternal/templates/chat-san-loss-1-4.hbs"
// Check if all violence elements are true, if so, set adaptedToViolence to true
if (violence.every(v => v)) {
updates[`system.biodata.adaptedToViolence`] = true
updates[`system.san.violence`] = [false, false, false]
msgData.adaptedToViolence = true
}
} else if (rollData.sanType === "helplessness" ) {
// If sanType is "helplessness" and adapted to helplessness, set the first false element of the helplessness array to true
let helplessness = this.san.helplessness.slice()
let index = helplessness.findIndex(h => !h)
if (index !== -1) {
helplessness[index] = true
updates[`system.san.helplessness`] = helplessness
}
template = "systems/fvtt-cthulhu-eternal/templates/chat-san-loss-1-4.hbs"
// Check if all helplessness elements are true, if so, set adaptedToHelplessness to true
if (helplessness.every(h => h)) {
updates[`system.biodata.adaptedToHelplessness`] = true
updates[`system.san.helplessness`] = [false, false, false]
msgData.adaptedToHelplessness = true
}
}
let content = await foundry.applications.handlebars.renderTemplate(template, msgData)
let msg = await ChatMessage.create({
content: content,
speaker: ChatMessage.getSpeaker({ actor: this.parent })
})
msg.setFlag("fvtt-cthulhu-eternal", "rollData", msgData)
if (Object.keys(updates).length > 0) {
this.parent.update(updates)
}
}
async modifySAN(rollData) {
let updates = {}
let san = Math.max(Math.min(this.san.value + rollData.sanLoss, this.san.max), 0)
if (this.san.value !== san) {
updates[`system.san.value`] = san
const content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-cthulhu-eternal/templates/chat-san-type-request.hbs", rollData)
let msg = await ChatMessage.create({
content: content,
speaker: ChatMessage.getSpeaker({ actor: this.parent })
})
msg.setFlag("fvtt-cthulhu-eternal", "rollData", rollData)
}
if (Object.keys(updates).length > 0) {
this.parent.update(updates)
}
}
isStunned() { isStunned() {
return this.hp.stunned return this.hp.stunned
} }
@ -215,6 +328,7 @@ export default class CthulhuEternalProtagonist extends foundry.abstract.TypeData
let bp = Math.max(this.san.value - this.characteristics.pow.value, 0) let bp = Math.max(this.san.value - this.characteristics.pow.value, 0)
if (this.san.breakingPoint !== bp) { if (this.san.breakingPoint !== bp) {
updates[`system.san.breakingPoint`] = bp updates[`system.san.breakingPoint`] = bp
updates[`system.san.breakingPointReached`] = false // Reset breaking point reached
} }
if (Object.keys(updates).length > 0) { if (Object.keys(updates).length > 0) {
this.parent.update(updates) this.parent.update(updates)

View File

@ -180,6 +180,55 @@ export default class CthulhuEternalUtils {
}); });
} }
static async applySANType(rollMessage, event) {
let rollData = rollMessage.getFlag("fvtt-cthulhu-eternal", "rollData")
if (!rollData) {
ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.noRollDataFound"))
return
}
let actor = game.actors.get(rollData.actorId)
if (!actor) {
ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.noActorFound"))
return
}
let sanType = event.currentTarget.dataset.sanType;
if (!sanType) {
ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.noSanTypeFound"))
return
}
// If the sanType is "none", we don't apply any SAN processing
if (sanType === "none") {
ui.notifications.info(game.i18n.localize("CTHULHUETERNAL.Notifications.noSanLossApplied"))
return
}
rollData.sanType = sanType
await actor.system.applySANConsequences(rollData)
// Delete the roll message
await rollMessage.delete()
}
static async applySANLoss(rollMessage, event) {
let rollData = rollMessage.getFlag("fvtt-cthulhu-eternal", "rollData")
if (!rollData) {
ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.noRollDataFound"))
return
}
let actor = game.actors.get(rollData.actorId)
if (!actor) {
ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.noActorFound"))
return
}
// Get the san loss from the event : data-san-value
let sanLoss = event.currentTarget.dataset.sanValue
let r = new Roll(sanLoss.toString())
await r.evaluate()
rollData.sanLoss = -r.total
await actor.system.modifySAN(rollData)
// Delete the roll message
await rollMessage.delete()
}
static async healingRoll(rollMessage) { static async healingRoll(rollMessage) {
let rollData = rollMessage.rolls[0]?.options?.rollData let rollData = rollMessage.rolls[0]?.options?.rollData
let healingFormula = rollData.rollItem.system.healingFormula let healingFormula = rollData.rollItem.system.healingFormula

View File

@ -1 +1 @@
MANIFEST-000131 MANIFEST-000143

View File

@ -1,3 +1,7 @@
2025/06/14-23:45:33.918294 7f78d8df86c0 Recovering log #129 2025/06/15-20:50:32.407889 7f78d9dfa6c0 Recovering log #141
2025/06/14-23:45:34.040033 7f78d8df86c0 Delete type=3 #127 2025/06/15-20:50:32.419111 7f78d9dfa6c0 Delete type=3 #139
2025/06/14-23:45:34.040084 7f78d8df86c0 Delete type=0 #129 2025/06/15-20:50:32.419180 7f78d9dfa6c0 Delete type=0 #141
2025/06/15-23:02:15.291797 7f78d37fe6c0 Level-0 table #146: started
2025/06/15-23:02:15.291843 7f78d37fe6c0 Level-0 table #146: 0 bytes OK
2025/06/15-23:02:15.346613 7f78d37fe6c0 Delete type=0 #144
2025/06/15-23:02:15.442962 7f78d37fe6c0 Manual compaction at level-0 from '!items!4oyPRBWPBWAChrJP' @ 72057594037927935 : 1 .. '!items!zVFfp3o0G0Zg3Ia4' @ 0 : 0; will stop at (end)

View File

@ -1,7 +1,7 @@
2025/06/14-13:49:17.890351 7f78d8df86c0 Recovering log #125 2025/06/15-20:45:20.732548 7f78d95f96c0 Recovering log #137
2025/06/14-13:49:17.899990 7f78d8df86c0 Delete type=3 #123 2025/06/15-20:45:20.795953 7f78d95f96c0 Delete type=3 #135
2025/06/14-13:49:17.900040 7f78d8df86c0 Delete type=0 #125 2025/06/15-20:45:20.796016 7f78d95f96c0 Delete type=0 #137
2025/06/14-16:21:28.823932 7f78d37fe6c0 Level-0 table #130: started 2025/06/15-20:50:27.656562 7f78d37fe6c0 Level-0 table #142: started
2025/06/14-16:21:28.823960 7f78d37fe6c0 Level-0 table #130: 0 bytes OK 2025/06/15-20:50:27.656587 7f78d37fe6c0 Level-0 table #142: 0 bytes OK
2025/06/14-16:21:28.831212 7f78d37fe6c0 Delete type=0 #128 2025/06/15-20:50:27.663159 7f78d37fe6c0 Delete type=0 #140
2025/06/14-16:21:28.848927 7f78d37fe6c0 Manual compaction at level-0 from '!items!4oyPRBWPBWAChrJP' @ 72057594037927935 : 1 .. '!items!zVFfp3o0G0Zg3Ia4' @ 0 : 0; will stop at (end) 2025/06/15-20:50:27.663295 7f78d37fe6c0 Manual compaction at level-0 from '!items!4oyPRBWPBWAChrJP' @ 72057594037927935 : 1 .. '!items!zVFfp3o0G0Zg3Ia4' @ 0 : 0; will stop at (end)

View File

@ -1 +1 @@
MANIFEST-000300 MANIFEST-000312

View File

@ -1,3 +1,7 @@
2025/06/14-23:45:33.811724 7f78d9dfa6c0 Recovering log #298 2025/06/15-20:50:32.394511 7f78d95f96c0 Recovering log #310
2025/06/14-23:45:33.914052 7f78d9dfa6c0 Delete type=3 #296 2025/06/15-20:50:32.404291 7f78d95f96c0 Delete type=3 #308
2025/06/14-23:45:33.914134 7f78d9dfa6c0 Delete type=0 #298 2025/06/15-20:50:32.404388 7f78d95f96c0 Delete type=0 #310
2025/06/15-23:02:15.346748 7f78d37fe6c0 Level-0 table #315: started
2025/06/15-23:02:15.346777 7f78d37fe6c0 Level-0 table #315: 0 bytes OK
2025/06/15-23:02:15.368088 7f78d37fe6c0 Delete type=0 #313
2025/06/15-23:02:15.442974 7f78d37fe6c0 Manual compaction at level-0 from '!folders!5PrT9QmN1cFPzDFP' @ 72057594037927935 : 1 .. '!items!zvoUByzWSWZ87fxA' @ 0 : 0; will stop at (end)

View File

@ -1,7 +1,7 @@
2025/06/14-13:49:17.875545 7f78d95f96c0 Recovering log #294 2025/06/15-20:45:20.673899 7f78d9dfa6c0 Recovering log #306
2025/06/14-13:49:17.886122 7f78d95f96c0 Delete type=3 #292 2025/06/15-20:45:20.728664 7f78d9dfa6c0 Delete type=3 #304
2025/06/14-13:49:17.886173 7f78d95f96c0 Delete type=0 #294 2025/06/15-20:45:20.728753 7f78d9dfa6c0 Delete type=0 #306
2025/06/14-16:21:28.817422 7f78d37fe6c0 Level-0 table #299: started 2025/06/15-20:50:27.650512 7f78d37fe6c0 Level-0 table #311: started
2025/06/14-16:21:28.817477 7f78d37fe6c0 Level-0 table #299: 0 bytes OK 2025/06/15-20:50:27.650541 7f78d37fe6c0 Level-0 table #311: 0 bytes OK
2025/06/14-16:21:28.823801 7f78d37fe6c0 Delete type=0 #297 2025/06/15-20:50:27.656452 7f78d37fe6c0 Delete type=0 #309
2025/06/14-16:21:28.848912 7f78d37fe6c0 Manual compaction at level-0 from '!folders!5PrT9QmN1cFPzDFP' @ 72057594037927935 : 1 .. '!items!zvoUByzWSWZ87fxA' @ 0 : 0; will stop at (end) 2025/06/15-20:50:27.663286 7f78d37fe6c0 Manual compaction at level-0 from '!folders!5PrT9QmN1cFPzDFP' @ 72057594037927935 : 1 .. '!items!zvoUByzWSWZ87fxA' @ 0 : 0; will stop at (end)

View File

@ -86,6 +86,7 @@ i.fvtt-cthulhu-eternal {
background-size: 100% 100%; background-size: 100% 100%;
} }
.chat-san-request,
.chat-lethal-damage { .chat-lethal-damage {
ul { ul {
list-style-type: none; list-style-type: none;
@ -97,6 +98,23 @@ i.fvtt-cthulhu-eternal {
color: var(--color-critical-failure); color: var(--color-critical-failure);
font-family: var(--font-title); font-family: var(--font-title);
} }
.san-loose-buttons {
display: flex;
justify-content: center;
align-items: center;
margin: 10px 0;
button {
margin: 0 2px;
font-family: var(--font-primary);
font-size: calc(var(--font-size-standard) * 1.1);
border: none;
padding: 2px 2px;
cursor: pointer;
transition: background-color 0.3s;
min-width: 3.0rem;
max-width: 3.0rem;
}
}
.result-non-lethal { .result-non-lethal {
color: var(--color-failure); color: var(--color-failure);
font-family: var(--font-title); font-family: var(--font-title);

View File

@ -123,6 +123,14 @@
min-width: 6rem; min-width: 6rem;
flex-grow: 1; flex-grow: 1;
} }
.san-helplessness,
.san-violence {
display: flex;
flex-grow: 1;
}
.label-san-type {
margin-right: 0.5rem;
}
.label-bp { .label-bp {
flex-grow: 1; flex-grow: 1;
max-width: 3rem; max-width: 3rem;

View File

@ -0,0 +1,11 @@
<div class="{{cssClass}}">
<div class="chat-san-request">
<ul>
<li><strong>{{localize "CTHULHUETERNAL.Label.noSanLoss"}}</strong></li>
{{#if resetMsg}}
<li><strong>{{localize resetMsg}}</strong></li>
{{/if}}
</ul>
</div>
</div>

View File

@ -0,0 +1,24 @@
<div class="{{cssClass}}">
<div class="chat-san-request">
<ul>
<li><strong>{{localize "CTHULHUETERNAL.Label.sanLoss"}}</strong></li>
{{#if (eq sanType "violence")}}
{{#if adaptedToViolence}}
<li class="orange-warning">{{localize "CTHULHUETERNAL.Label.adaptedToViolence"}}</li>
{{else}}
<li>{{localize "CTHULHUETERNAL.Label.sanLossViolence"}}</li>
{{/if}}
{{/if}}
{{#if (eq sanType "helplessness")}}
{{#if adaptedToHelplessness}}
<li class="orange-warning">{{localize "CTHULHUETERNAL.Label.adaptedToHelplessness"}}</li>
{{else}}
<li>{{localize "CTHULHUETERNAL.Label.sanLossHelplessness"}}</li>
{{/if}}
{{/if}}
</ul>
</div>
</div>

View File

@ -0,0 +1,25 @@
<div class="{{cssClass}}">
<div class="chat-san-request">
<ul>
<li><strong>{{localize "CTHULHUETERNAL.Label.SANTest"}}</strong></li>
<li class="san-loose-buttons">
<button class="san-loose" data-san-value="0">0</button>
<button class="san-loose" data-san-value="1">1</button>
<button class="san-loose" data-san-value="2">2</button>
<button class="san-loose" data-san-value="3">3</button>
<button class="san-loose" data-san-value="4">4</button>
</li>
<li class="san-loose-buttons">
<button class="san-loose" data-san-value="1d4">1d4</button>
<button class="san-loose" data-san-value="1d6">1d6</button>
<button class="san-loose" data-san-value="1d8">1d8</button>
<button class="san-loose" data-san-value="1d10">1d10</button>
<button class="san-loose" data-san-value="1d12">1d12</button>
</li>
</ul>
</div>
</div>

View File

@ -0,0 +1,12 @@
<div class="{{cssClass}}">
<div class="chat-san-request">
<ul>
<li><strong>{{localize "CTHULHUETERNAL.Label.sanLoss5"}}</strong></li>
{{#if resetMsg}}
<li class="orange-warning">{{localize resetMsg}}</li>
{{/if}}
</ul>
</div>
</div>

View File

@ -0,0 +1,19 @@
<div class="{{cssClass}}">
<div class="chat-san-request">
<ul>
<li><strong>{{localize "CTHULHUETERNAL.Label.sanLoss"}} : {{sanLoss}}</strong></li>
<li><strong>{{localize "CTHULHUETERNAL.Label.selectSANType"}}</strong></li>
<li class="san-type-buttons">
<button class="san-type" data-san-value="{{sanLoss}}" data-san-type="violence">{{localize "CTHULHUETERNAL.Label.Violence"}}</button>
<button class="san-type" data-san-value="{{sanLoss}}" data-san-type="helplessness">{{localize "CTHULHUETERNAL.Label.Helplessness"}}</button>
</li>
<li class="san-type-buttons">
<button class="san-type" data-san-value="{{sanLoss}}" data-san-type="unnatural">{{localize "CTHULHUETERNAL.Label.Unnatural"}}</button>
<button class="san-type" data-san-value="{{sanLoss}}" data-san-type="none">{{localize "CTHULHUETERNAL.Label.None"}}</button>
</li>
</ul>
</div>
</div>

View File

@ -21,8 +21,13 @@
<fieldset> <fieldset>
<legend>{{localize "CTHULHUETERNAL.Label.biodata"}}</legend> <legend>{{localize "CTHULHUETERNAL.Label.biodata"}}</legend>
<div class="adapted"> <div class="adapted">
{{#if isGM}}
{{formField systemFields.biodata.fields.adaptedToViolence value=system.biodata.adaptedToViolence name="system.biodata.adaptedToViolence" localize=true}} {{formField systemFields.biodata.fields.adaptedToViolence value=system.biodata.adaptedToViolence name="system.biodata.adaptedToViolence" localize=true}}
{{formField systemFields.biodata.fields.adaptedToHelplessness value=system.biodata.adaptedToHelplessness name="system.biodata.adaptedToHelplessness" localize=true}} {{formField systemFields.biodata.fields.adaptedToHelplessness value=system.biodata.adaptedToHelplessness name="system.biodata.adaptedToHelplessness" localize=true}}
{{else}}
{{formField systemFields.biodata.fields.adaptedToViolence value=system.biodata.adaptedToViolence name="system.biodata.adaptedToViolence" localize=true disabled=true}}
{{formField systemFields.biodata.fields.adaptedToHelplessness value=system.biodata.adaptedToHelplessness name="system.biodata.adaptedToHelplessness" localize=true disabled=true}}
{{/if}}
</div> </div>
<div class="biodata"> <div class="biodata">
{{formField systemFields.biodata.fields.harshness value=system.biodata.harshness name="system.biodata.harshness" localize=true}} {{formField systemFields.biodata.fields.harshness value=system.biodata.harshness name="system.biodata.harshness" localize=true}}

View File

@ -76,17 +76,29 @@
</div> </div>
<div class="flexrow"> <div class="flexrow">
<span class="label-field">{{localize "CTHULHUETERNAL.Label.violence"}}</span> <div class="san-violence">
{{#if system.biodata.adaptedToViolence}}
<span class="">{{localize "CTHULHUETERNAL.Label.adaptedToViolenceShort"}}</span>
{{else}}
<span class="label-field label-san-type">{{localize "CTHULHUETERNAL.Label.violence"}}</span>
{{#each system.san.violence as |violence idx|}} {{#each system.san.violence as |violence idx|}}
<input class="san-checkbox" type="checkbox" data-action="updateCheckboxArray" data-index="{{@index}}" <input class="san-checkbox" type="checkbox" data-action="updateCheckboxArray" data-index="{{@index}}"
data-name="violence" {{#if violence}} checked {{/if}}> data-name="violence" {{#if violence}} checked {{/if}}>
{{/each}} {{/each}}
{{/if}}
</div>
<span class="label-field">{{localize "CTHULHUETERNAL.Label.helplessness"}}</span> <div class="san-helplessness">
{{#if system.biodata.adaptedToHelplessness}}
<span class="">{{localize "CTHULHUETERNAL.Label.adaptedToHelplessnessShort"}}</span>
{{else}}
<span class="label-field label-san-type">{{localize "CTHULHUETERNAL.Label.helplessness"}}</span>
{{#each system.san.helplessness as |helplessness idx|}} {{#each system.san.helplessness as |helplessness idx|}}
<input class="san-checkbox" type="checkbox" data-action="updateCheckboxArray" data-index="{{@index}}" <input class="san-checkbox" type="checkbox" data-action="updateCheckboxArray" data-index="{{@index}}"
data-name="helplessness" {{#if helplessness}} checked {{/if}}> data-name="helplessness" {{#if helplessness}} checked {{/if}}>
{{/each}} {{/each}}
{{/if}}
</div>
</div> </div>