diff --git a/.gitea/workflows/release.yaml b/.gitea/workflows/release.yaml index 800418c..928c984 100644 --- a/.gitea/workflows/release.yaml +++ b/.gitea/workflows/release.yaml @@ -27,7 +27,7 @@ jobs: env: version: ${{steps.get_version.outputs.version-without-v}} url: https://www.uberwald.me/gitea/${{gitea.repository}} - manifest: https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/system.json + manifest: https://www.uberwald.me/gitea/public/${{gitea.repository}}/releases/download/latest/system.json download: https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/fvtt-cthulhu-eternal.zip # Create a zip file with all files required by the module to add to the release diff --git a/css/fvtt-cthulhu-eternal.css b/css/fvtt-cthulhu-eternal.css index 11ebcf3..3738a30 100644 --- a/css/fvtt-cthulhu-eternal.css +++ b/css/fvtt-cthulhu-eternal.css @@ -1983,6 +1983,11 @@ i.fvtt-cthulhu-eternal { font-family: var(--font-primary); font-size: calc(var(--font-size-standard) * 1); } +.dice-roll .intro-chat .intro-right ul .nudge-roll { + font-size: calc(var(--font-size-standard) * 1); + margin-left: 4rem; + display: none; +} .dice-roll .intro-chat .intro-right ul .result-success { color: var(--color-success); font-family: var(--font-title); diff --git a/cthulhu-eternal.mjs b/cthulhu-eternal.mjs index 46146fa..75e3e13 100644 --- a/cthulhu-eternal.mjs +++ b/cthulhu-eternal.mjs @@ -114,25 +114,14 @@ Hooks.once("ready", function () { }) Hooks.on("renderChatMessage", (message, html, data) => { - const typeMessage = data.message.flags.CthulhuEternal?.typeMessage - // Message de demande de jet de dés - if (typeMessage === "askRoll") { - // Affichage des boutons de jet de dés uniquement pour les joueurs - if (game.user.isGM) { - html.find(".ask-roll-dice").each((i, btn) => { - btn.style.display = "none" - }) - } else { - html.find(".ask-roll-dice").click((event) => { - const btn = $(event.currentTarget) - const type = btn.data("type") - const value = btn.data("value") - const avantage = btn.data("avantage") ?? "=" - const character = game.user.character - if (type === SYSTEM.ROLL_TYPE.RESOURCE) character.rollResource(value) - else if (type === SYSTEM.ROLL_TYPE.SAVE) character.rollSave(value, avantage) - }) - } + // Affichage des boutons de jet de dés uniquement pour les joueurs + if (message.author.id === game.user.id) { + html.find(".nudge-roll").each((i, btn) => { + btn.style.display = "inline" + }) + html.find(".nudge-roll").click((event) => { + CthulhuEternalUtils.nudgeRoll(message) + }) } }) diff --git a/lang/en.json b/lang/en.json index 01a67aa..859ac8a 100644 --- a/lang/en.json +++ b/lang/en.json @@ -325,6 +325,9 @@ "veryHarsh": "Very Harsh" }, "Label": { + "nudgedRoll": "Nudged Roll", + "selectNewValue": "Select the new value", + "wpCost": "WP Cost", "Hand": "Hand", "Stowed": "Stowed", "Storage": "Storage", @@ -437,7 +440,9 @@ }, "Roll": { "skill": "Skill", - "roll": "Roll" + "roll": "Roll", + "applyNudge": "Apply", + "cancel": "Cancel" }, "Tooltip": { "sanBP": ">5 SAN lost in one roll, temporary insanity. If SAN less reaches BP = a Disorder unconscious Breaking and AND reset BP.", @@ -445,10 +450,11 @@ }, "Chat": { }, - "Notitications": { + "Notifications": { "NoWeaponSkill": "No weapon skill found for this weapon. Check Weapon definition or available skills/era", "NoWeaponType": "No weapon type found for this weapon subtype. Check Weapon definition or available skills/era", - "skillAlreadyExists": "Skill already exists" + "skillAlreadyExists": "Skill already exists", + "WrongEra": "The era of the item does not match the ear of the system" } } } diff --git a/module/config/system.mjs b/module/config/system.mjs index 9d557d1..92dfc5e 100644 --- a/module/config/system.mjs +++ b/module/config/system.mjs @@ -140,6 +140,13 @@ export const WEAPON_SKILL_MAPPING = { "rangedfirearm": "CTHULHUETERNAL.Skill.Firearms", "unarmed": "CTHULHUETERNAL.Skill.UnarmedCombat" }, + postapo: { + "melee": "CTHULHUETERNAL.Skill.Melee", + "rangedprimitive": "CTHULHUETERNAL.Skill.Firearms", + "rangedthrown": "CTHULHUETERNAL.Skill.Athletics", + "rangedfirearm": "CTHULHUETERNAL.Skill.Firearms", + "unarmed": "CTHULHUETERNAL.Skill.UnarmedCombat" + }, jazz: { "melee": "CTHULHUETERNAL.Skill.Melee", "rangedprimitive": "CTHULHUETERNAL.Skill.Firearms", diff --git a/module/documents/roll.mjs b/module/documents/roll.mjs index 20e4803..ce7cef9 100644 --- a/module/documents/roll.mjs +++ b/module/documents/roll.mjs @@ -79,6 +79,14 @@ export default class CthulhuEternalRoll extends Roll { return this.options.isExhausted } + get isNudgedRoll() { + return this.options.isNudgedRoll + } + + get wpCost() { + return this.options.wpCost + } + static updateResourceDialog(options) { let rating = 0 if (options.rollItem.enableHand) { @@ -113,7 +121,8 @@ export default class CthulhuEternalRoll extends Roll { static async prompt(options = {}) { let formula = "1d100" let hasModifier = true - let hasMultiplier = false + let hasMultiplier = false + options.isNudge = true switch (options.rollType) { case "skill": @@ -123,6 +132,7 @@ export default class CthulhuEternalRoll extends Roll { case "san": case "char": options.initialScore = options.rollItem.targetScore + options.isNudge = (options.rollType !== "san") break case "resource": hasModifier = false @@ -133,6 +143,7 @@ export default class CthulhuEternalRoll extends Roll { options.rollItem.enableHand = true options.rollItem.enableStowed = true options.rollItem.enableStorage = true + options.isNudge = false break case "damage": let formula = options.rollItem.system.damage @@ -142,6 +153,7 @@ export default class CthulhuEternalRoll extends Roll { flavor: `${options.rollItem.name} - Damage Roll` }); let isLethal = false + options.isNudge = false if (options.rollItem.system.lethality > 0) { let lethalityRoll = new Roll("1d100") await lethalityRoll.evaluate() @@ -153,8 +165,14 @@ export default class CthulhuEternalRoll extends Roll { return case "weapon": let era = game.settings.get("fvtt-cthulhu-eternal", "settings-era") + if (era !== options.rollItem.system.settings) { + ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.WrongEra")) + console.log("WP Wrong Era", era, options.rollItem.system.weaponType) + return + } if (!SYSTEM.WEAPON_SKILL_MAPPING[era] || !SYSTEM.WEAPON_SKILL_MAPPING[era][options.rollItem.system.weaponType]) { ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.NoWeaponType")) + console.log("WP Not found", era, options.rollItem.system.weaponType) return } let skillName = game.i18n.localize(SYSTEM.WEAPON_SKILL_MAPPING[era][options.rollItem.system.weaponType]) @@ -247,7 +265,7 @@ export default class CthulhuEternalRoll extends Roll { render: (event, dialog) => { $(".roll-skill-multiplier").change(event => { options.multiplier = Number(event.target.value) - this.updateResourceDialog(options) + this.updateResourceDialog(options) }) } }) @@ -259,10 +277,10 @@ export default class CthulhuEternalRoll extends Roll { rollData.rollMode = rollContext.visibility // Update target score - console.log(rollData) - if (options.rollType === "resource" ) { + console.log("Rolldata", rollData, options) + if (options.rollType === "resource") { rollData.targetScore = options.initialScore * Number(rollContext.multiplier) - } else { + } else { rollData.targetScore = Math.min(Math.max(options.initialScore + Number(rollData.modifier), 0), 100) if (rollData.isLowWP || rollData.isExhausted) { rollData.targetScore -= 20 @@ -270,25 +288,31 @@ export default class CthulhuEternalRoll extends Roll { if (rollData.isZeroWP) { rollData.targetScore = 0 } - rollData.targetScore = Math.min(Math.max(rollData.targetScore, 0), 100) + rollData.targetScore = Math.min(Math.max(rollData.targetScore, 0), 100) } - /** - * A hook event that fires before the roll is made. - */ if (Hooks.call("fvtt-cthulhu-eternal.preRoll", options, rollData) === false) return const roll = new this(formula, options.data, rollData) await roll.evaluate() + roll.displayRollResult(roll, options, rollData) + + if (Hooks.call("fvtt-cthulhu-eternal.Roll", options, rollData, roll) === false) return + + return roll + } + + displayRollResult(formula, options, rollData) { + // Compute the result quality let resultType = "failure" - let dec = Math.floor(roll.total / 10) - let unit = roll.total - (dec * 10) - if (roll.total <= rollData.targetScore) { + let dec = Math.floor(this.total / 10) + let unit = this.total - (dec * 10) + if (this.total <= rollData.targetScore) { resultType = "success" // Detect if decimal == unit in the dire total result - if (dec === unit || roll.total === 1) { + if (dec === unit || this.total === 1) { resultType = "successCritical" } } else { @@ -298,20 +322,20 @@ export default class CthulhuEternalRoll extends Roll { } } - roll.options.resultType = resultType - roll.options.isSuccess = resultType === "success" || resultType === "successCritical" - roll.options.isFailure = resultType === "failure" || resultType === "failureCritical" - roll.options.isCritical = resultType === "successCritical" || resultType === "failureCritical" - roll.options.isLowWP = rollData.isLowWP - roll.options.isZeroWP = rollData.isZeroWP - roll.options.isExhausted = rollData.isExhausted - - /** - * A hook event that fires after the roll has been made. - */ - if (Hooks.call("fvtt-cthulhu-eternal.Roll", options, rollData, roll) === false) return - - return roll + this.options.resultType = resultType + if (this.options.isNudgedRoll) { + this.options.isSuccess = resultType === "success" || resultType === "successCritical" + this.options.isFailure = resultType === "failure" || resultType === "failureCritical" + this.options.isCritical = false + } else { + this.options.isSuccess = resultType === "success" || resultType === "successCritical" + this.options.isFailure = resultType === "failure" || resultType === "failureCritical" + this.options.isCritical = resultType === "successCritical" || resultType === "failureCritical" + } + this.options.isLowWP = rollData.isLowWP + this.options.isZeroWP = rollData.isZeroWP + this.options.isExhausted = rollData.isExhausted + this.options.rollData = foundry.utils.duplicate(rollData) } /** @@ -387,9 +411,8 @@ export default class CthulhuEternalRoll extends Roll { cardData.isLowWP = this.isLowWP cardData.isZeroWP = this.isZeroWP cardData.isExhausted = this.isExhausted - - - console.log(cardData) + cardData.isNudgedRoll = this.isNudgedRoll + cardData.wpCost = this.wpCost cardData.cssClass = cardData.css.join(" ") cardData.tooltip = isPrivate ? "" : await this.getTooltip() diff --git a/module/models/protagonist.mjs b/module/models/protagonist.mjs index bb18874..a900a64 100644 --- a/module/models/protagonist.mjs +++ b/module/models/protagonist.mjs @@ -178,6 +178,17 @@ export default class CthulhuEternalProtagonist extends foundry.abstract.TypeData return this.wp.exhausted } + modifyWP(value) { + let updates = {} + let wp = Math.max(Math.min(this.wp.value + value, this.wp.max), 0) + if ( this.wp.value !== wp) { + updates[`system.wp.value`] = wp + } + if (Object.keys(updates).length > 0) { + this.parent.update(updates) + } + } + setBP() { let updates = {} let bp = Math.max(this.san.value - this.characteristics.pow.value, 0) diff --git a/module/utils.mjs b/module/utils.mjs index bef0ea3..fc6e33f 100644 --- a/module/utils.mjs +++ b/module/utils.mjs @@ -1,4 +1,6 @@ +import CthulhuEternalRoll from "./documents/roll.mjs" + export default class CthulhuEternalUtils { static registerSettings() { @@ -177,6 +179,73 @@ export default class CthulhuEternalUtils { } + static async nudgeRoll(rollMessage) { + + let dialogContext = rollMessage.rolls[0]?.options + let actor = game.actors.get(dialogContext.actorId) + dialogContext.wpValue = actor.system.wp.value + dialogContext.rollResultIndex = rollMessage.rolls[0].total - 1 + dialogContext.minValue = Math.max(rollMessage.rolls[0].total - (dialogContext.wpValue * 5), 1) + dialogContext.maxValue = Math.min(rollMessage.rolls[0].total + (dialogContext.wpValue * 5), 100) + dialogContext.wpCost = 0 + + // Build options table for the select operator between minValue and maxValue + dialogContext.nudgeOptions = Array.from({ length: dialogContext.maxValue - dialogContext.minValue + 1 }, (_, i) => dialogContext.minValue + i) + console.log(dialogContext) + + const content = await renderTemplate("systems/fvtt-cthulhu-eternal/templates/nudge-dialog.hbs", dialogContext) + + const title = game.i18n.localize("CTHULHUETERNAL.Roll.nudgeRoll") + const rollContext = await foundry.applications.api.DialogV2.wait({ + window: { title: title }, + classes: ["fvtt-cthulhu-eternal"], + content, + buttons: [ + { + action: "apply", + label: game.i18n.localize("CTHULHUETERNAL.Roll.applyNudge"), + callback: (event, button, dialog) => { + const output = Array.from(button.form.elements).reduce((obj, input) => { + if (input.name) obj[input.name] = input.value + return obj + }, {}) + return output + }, + }, + { + action: "cancel", + label: game.i18n.localize("CTHULHUETERNAL.Roll.cancel"), + callback: (event, button, dialog) => { } + } + ], + actions: { + }, + rejectClose: false, // Click on Close button will not launch an error + render: (event, dialog) => { + $(".nudged-score-select").change(event => { + dialogContext.nudgedValue = Number(event.target.value)+1 + dialogContext.wpCost = Math.ceil(Math.abs(rollMessage.rolls[0].total - dialogContext.nudgedValue) / 5) + $("#nudged-wp-cost").val(dialogContext.wpCost) + }) + } + }) + + // If the user cancels the dialog, exit + if (rollContext === null || dialogContext.wpCost === 0) { + return + } + + const roll = new CthulhuEternalRoll(String(dialogContext.nudgedValue)) + await roll.evaluate() + roll.options = dialogContext + roll.options.isNudgedRoll = true + roll.options.isNudge = false + roll.displayRollResult(roll, dialogContext, dialogContext.rollData) + roll.toMessage() + + actor.system.modifyWP(-dialogContext.wpCost) + } + static setupCSSRootVariables() { const era = game.settings.get("fvtt-cthulhu-eternal", "settings-era") diff --git a/packs-system/skills/000094.log b/packs-system/skills/000094.log deleted file mode 100644 index e69de29..0000000 diff --git a/packs-system/skills/CURRENT b/packs-system/skills/CURRENT index 5b83d76..6274e55 100644 --- a/packs-system/skills/CURRENT +++ b/packs-system/skills/CURRENT @@ -1 +1 @@ -MANIFEST-000092 +MANIFEST-000096 diff --git a/packs-system/skills/LOG b/packs-system/skills/LOG index 2d6b47c..eb4a895 100644 --- a/packs-system/skills/LOG +++ b/packs-system/skills/LOG @@ -1,8 +1,8 @@ -2025/01/28-13:40:46.388286 7fc73affd6c0 Recovering log #90 -2025/01/28-13:40:46.486338 7fc73affd6c0 Delete type=3 #88 -2025/01/28-13:40:46.486413 7fc73affd6c0 Delete type=0 #90 -2025/01/28-14:21:23.579080 7fc738ff96c0 Level-0 table #95: started -2025/01/28-14:21:23.579113 7fc738ff96c0 Level-0 table #95: 0 bytes OK -2025/01/28-14:21:23.585685 7fc738ff96c0 Delete type=0 #93 -2025/01/28-14:21:23.595091 7fc738ff96c0 Manual compaction at level-0 from '!folders!DD8331Hda4rhvEf9' @ 72057594037927935 : 1 .. '!items!zplzTG30QXHURusr' @ 0 : 0; will stop at (end) -2025/01/28-14:21:23.607394 7fc738ff96c0 Manual compaction at level-1 from '!folders!DD8331Hda4rhvEf9' @ 72057594037927935 : 1 .. '!items!zplzTG30QXHURusr' @ 0 : 0; will stop at (end) +2025/02/01-13:04:37.403980 7fd1c9ffb6c0 Recovering log #94 +2025/02/01-13:04:37.414560 7fd1c9ffb6c0 Delete type=3 #92 +2025/02/01-13:04:37.414616 7fd1c9ffb6c0 Delete type=0 #94 +2025/02/01-21:09:42.547683 7fd1c93ff6c0 Level-0 table #99: started +2025/02/01-21:09:42.547729 7fd1c93ff6c0 Level-0 table #99: 0 bytes OK +2025/02/01-21:09:42.554673 7fd1c93ff6c0 Delete type=0 #97 +2025/02/01-21:09:42.570630 7fd1c93ff6c0 Manual compaction at level-0 from '!folders!DD8331Hda4rhvEf9' @ 72057594037927935 : 1 .. '!items!zplzTG30QXHURusr' @ 0 : 0; will stop at (end) +2025/02/01-21:09:42.570661 7fd1c93ff6c0 Manual compaction at level-1 from '!folders!DD8331Hda4rhvEf9' @ 72057594037927935 : 1 .. '!items!zplzTG30QXHURusr' @ 0 : 0; will stop at (end) diff --git a/packs-system/skills/LOG.old b/packs-system/skills/LOG.old index 667ca96..2d6b47c 100644 --- a/packs-system/skills/LOG.old +++ b/packs-system/skills/LOG.old @@ -1,8 +1,8 @@ -2025/01/28-11:42:23.420912 7fc7397fa6c0 Recovering log #86 -2025/01/28-11:42:23.492812 7fc7397fa6c0 Delete type=3 #84 -2025/01/28-11:42:23.492907 7fc7397fa6c0 Delete type=0 #86 -2025/01/28-11:56:09.681552 7fc738ff96c0 Level-0 table #91: started -2025/01/28-11:56:09.681584 7fc738ff96c0 Level-0 table #91: 0 bytes OK -2025/01/28-11:56:09.688279 7fc738ff96c0 Delete type=0 #89 -2025/01/28-11:56:09.688435 7fc738ff96c0 Manual compaction at level-0 from '!folders!DD8331Hda4rhvEf9' @ 72057594037927935 : 1 .. '!items!zplzTG30QXHURusr' @ 0 : 0; will stop at (end) -2025/01/28-11:56:09.699103 7fc738ff96c0 Manual compaction at level-1 from '!folders!DD8331Hda4rhvEf9' @ 72057594037927935 : 1 .. '!items!zplzTG30QXHURusr' @ 0 : 0; will stop at (end) +2025/01/28-13:40:46.388286 7fc73affd6c0 Recovering log #90 +2025/01/28-13:40:46.486338 7fc73affd6c0 Delete type=3 #88 +2025/01/28-13:40:46.486413 7fc73affd6c0 Delete type=0 #90 +2025/01/28-14:21:23.579080 7fc738ff96c0 Level-0 table #95: started +2025/01/28-14:21:23.579113 7fc738ff96c0 Level-0 table #95: 0 bytes OK +2025/01/28-14:21:23.585685 7fc738ff96c0 Delete type=0 #93 +2025/01/28-14:21:23.595091 7fc738ff96c0 Manual compaction at level-0 from '!folders!DD8331Hda4rhvEf9' @ 72057594037927935 : 1 .. '!items!zplzTG30QXHURusr' @ 0 : 0; will stop at (end) +2025/01/28-14:21:23.607394 7fc738ff96c0 Manual compaction at level-1 from '!folders!DD8331Hda4rhvEf9' @ 72057594037927935 : 1 .. '!items!zplzTG30QXHURusr' @ 0 : 0; will stop at (end) diff --git a/packs-system/skills/MANIFEST-000092 b/packs-system/skills/MANIFEST-000092 deleted file mode 100644 index c9b9e64..0000000 Binary files a/packs-system/skills/MANIFEST-000092 and /dev/null differ diff --git a/styles/roll.less b/styles/roll.less index 3269939..0df4727 100644 --- a/styles/roll.less +++ b/styles/roll.less @@ -93,6 +93,11 @@ font-family: var(--font-primary); font-size: calc(var(--font-size-standard) * 1.0); } + .nudge-roll { + font-size: calc(var(--font-size-standard) * 1.0); + margin-left: 4rem; + display: none; + } .result-success { color: var(--color-success); font-family: var(--font-title); diff --git a/system.json b/system.json index c1de2a2..7fd93c1 100644 --- a/system.json +++ b/system.json @@ -6,7 +6,7 @@ "download": "#{DOWNLOAD}#", "url": "https://www.uberwald.me/gitea/public/fvtt-cthulhu-eternal", "license": "LICENSE", - "version": "12.0.4", + "version": "12.0.5", "authors": [ { "name": "Uberwald", diff --git a/templates/chat-message.hbs b/templates/chat-message.hbs index a54b091..54a76d8 100644 --- a/templates/chat-message.hbs +++ b/templates/chat-message.hbs @@ -14,6 +14,10 @@
  • {{localize "CTHULHUETERNAL.Label.skillRoll"}}
  • {{/if}} + {{#if isNudgedRoll}} +
  • {{localize "CTHULHUETERNAL.Label.nudgedRoll"}} : {{wpCost}} WP spent
  • + {{/if}} + {{#if weapon}}
  • Weapon : {{weapon.name}}
  • {{/if}} @@ -47,14 +51,24 @@ {{#if isCritical}}
  • {{localize "CTHULHUETERNAL.Label.criticalSuccess"}}
  • {{else}} -
  • {{localize "CTHULHUETERNAL.Label.success"}}
  • +
  • + {{localize "CTHULHUETERNAL.Label.success"}} + {{#if isNudge}} + + {{/if}} +
  • {{/if}} {{/if}} {{#if isFailure}} {{#if isCritical}}
  • {{localize "CTHULHUETERNAL.Label.criticalFailure"}}
  • {{else}} -
  • {{localize "CTHULHUETERNAL.Label.failure"}}
  • +
  • + {{localize "CTHULHUETERNAL.Label.failure"}} + {{#if isNudge}} + + {{/if}} +
  • {{/if}} {{/if}} diff --git a/templates/roll-dialog.hbs b/templates/roll-dialog.hbs index 5a2074c..23b2430 100644 --- a/templates/roll-dialog.hbs +++ b/templates/roll-dialog.hbs @@ -1,4 +1,3 @@ -{{log "roll-dialog" this}}
    {{#if (eq rollType "skill")}}