diff --git a/src/main.mjs b/src/main.mjs index fbe1361d..64bb6f40 100644 --- a/src/main.mjs +++ b/src/main.mjs @@ -30,6 +30,8 @@ import {ProfessionSheet} from "./module/sheets/ProfessionSheet.mjs"; import {XmlImportDialog} from "./module/dialog/xmlImportDialog.mjs"; import {MerchantDataModel} from "./module/data/merchant.mjs"; import {MerchantSheet} from "./module/sheets/merchantSheet.mjs"; +import {RestingDialog} from "./module/dialog/restingDialog.mjs"; + async function preloadHandlebarsTemplates() { return foundry.applications.handlebars.loadTemplates([ // ui partials. @@ -55,7 +57,8 @@ Hooks.once("init", () => { Zonenruestung, Zonenwunde, Trefferzone, - Wunde + Wunde, + RestingDialog } // Configure custom Document implementations. @@ -306,6 +309,26 @@ Hooks.once("init", () => { }) + Handlebars.registerHelper("fieldTooltip", (...args) => { + const [fieldName, actorId] = args + + const actor = game.actors.find(p => p._id === actorId) + + let tooltip = "" + + if (actor) { + Object.entries(actor.getModificationsOn(fieldName)).forEach(([key, value]) => { + tooltip += `${key}: ${value}
` + + }) + } else { + tooltip = `${fieldName} not found` + } + + + return new Handlebars.SafeString(tooltip) + }) + Handlebars.registerHelper("currency", (data) => { diff --git a/src/module/dialog/restingDialog.mjs b/src/module/dialog/restingDialog.mjs new file mode 100644 index 00000000..d73e4234 --- /dev/null +++ b/src/module/dialog/restingDialog.mjs @@ -0,0 +1,306 @@ +import {XmlImport} from "../xml-import/xml-import.mjs"; + +const {ApplicationV2, HandlebarsApplicationMixin} = foundry.applications.api + +export class RestingDialog extends HandlebarsApplicationMixin(ApplicationV2) { + + static DEFAULT_OPTIONS = { + classes: ['dsa41', 'dialog', 'resting'], + tag: "form", + position: { + width: 480, + height: 800 + }, + window: { + resizable: false, + title: "Rasten und Regenerieren" + }, + form: { + submitOnChange: true, + closeOnSubmit: false, + handler: RestingDialog.#onSubmitForm + }, + actions: {} + } + + static PARTS = { + form: { + template: 'systems/DSA_4-1/templates/dialog/resting-dialog.hbs', + } + } + + /** + * @type {Actor} + * @private + */ + _actor = null + + /** + * @typedef RestingCircumstance + * @property {String} name Displaytext of this circumstance modifier + * @property {Number|[Number]} aspMod modifier to AsP regeneration + * @property {Number|[Number]} lepMod modifier to LeP regeneration + * @property {Boolean|Number} active indicates if this modifier is active and or which index of aspMod/lepMod to be used + */ + + /** + * @typedef RestingOptions + * @property {Number} length the time of this rest, will be used for reducing exhaustion and overexertion + * @property {Number} wounds the amount of wounds the actor has suffered + * @property {Number|Boolean} treated if the wounds have been treated with the Skill "Heilkunde: Wunden" the Value is the Modificator for the wound regeneration + * @property {RestingCircumstance} circumstances the circumstances of this Rest + */ + + #type = { + DRAUßEN: "draußen", + SCHLAFSAAL: "schlafsaal", + EINZELZIMMER: "einzelzimmer", + SUITE: "suite", + } + + /** + * @type {[RestingCircumstance]} + * @private + */ + #circumstances = [ + { + name: "Ruhestörung", + display: "boolean", + lepMod: -1, + aspMod: -1, + group: "interrupted", + active: false, + value: "on" + }, + { + name: "Wache gehalten", + display: "boolean", + lepMod: -1, + aspMod: -1, + group: "watch", + active: false, + value: "on" + }, + { + name: "Unter freiem Himmel", + display: "radio", + aspMod: 0, + lepMod: 0, + active: false, + value: this.#type.DRAUßEN, + group: "type" + }, + { + name: "Schlechtes Wetter", + aspMod: [-1, -2, -3, -4, -5], + lepMod: [-1, -2, -3, -4, -5], + display: "range", + noLabel: "Gutes Wetter", + labels: [ + "Schlechtes Wetter I", + "Schlechtes Wetter II", + "Schlechtes Wetter III", + "Schlechtes Wetter IV", + "Schlechtes Wetter V", + ], + group: "bad_weather", + active: -1, + }, + { + name: "Schlechter Lagerplatz", + aspMod: -1, + lepMod: -1, + display: "boolean", + group: "bad_camp", + active: false, + value: "on" + }, + { + name: "Magere Lagerstätte (Schlafsaal, Heuboden)", + aspMod: 0, + lepMod: 0, + display: "radio", + group: "type", + value: this.#type.SCHLAFSAAL, + active: false, + }, + { + name: "Komfortable Schlafstätte", + aspMod: 1, + lepMod: 1, + display: "radio", + group: "type", + value: this.#type.EINZELZIMMER, + active: false, + }, + { + name: "Luxuriöse Schlafstätte (Suite)", + aspMod: 2, + lepMod: 2, + display: "radio", + group: "type", + value: this.#type.SUITE, + active: false, + } + + ] + + + constructor(actor) { + super(); + this._actor = actor; + this.restDuration = 6 + this.restingType = this.#circumstances.find(p => p.value === this.#type.DRAUßEN) + this.badWeather = -1 + this.woundTreated = false + this.woundRegenerationModifier = 0 + this.wounds = 1 + this.badCamp = false + this.watch = false + this.interrupted = false + + } + + static async #onSubmitForm(event, form, formData) { + event.preventDefault() + console.log(formData) + this.restDuration = formData.object.length + this.restingType = formData.object.type + this.badWeather = formData.object.bad_weather + this.woundTreated = formData.object.wound_treated + this.woundRegenerationModifier = formData.object.wound_treat_modifier + this.wounds = formData.object.wounds + this.badCamp = formData.object.bad_camp === "on" + this.watch = formData.object.watch === "on" + this.interrupted = formData.object.interrupted === "on" + + + const elementLepMod = this.element.querySelector('output[name="lepMod"]') + const elementKoMod = this.element.querySelector('output[name="koMod"]') + const elementAspMod = this.element.querySelector('output[name="aspMod"]') + const elementInMod = this.element.querySelector('output[name="inMod"]') + const elementWoundMod = this.element.querySelector('output[name="woundMod"]') + + const context = this.#updateData() + elementLepMod.value = context.lepMod + elementKoMod.value = context.koRoll + elementAspMod.value = context.aspMod + elementInMod.value = context.inRoll + elementWoundMod.value = context.woundMod + } + + #updateData(context = {}) { + context.circumstances = this.#circumstances + context.actorId = this._actor._id + context.hasWounds = true + context.hasAsP = true + context.wounds = this.wounds + + // TODO count wounds + + let lepRestModifier = 0 + let aspRestModifier = 0 + + if (this.restingType) { + const circ = this.#circumstances.find(p => p.value === this.restingType) + if (circ) { + lepRestModifier += Number(circ.lepMod) + aspRestModifier += Number(circ.aspMod) + } + } + + if (this.restingType === this.#type.DRAUßEN && this.badWeather !== -1) { + const circ = this.#circumstances.find(p => p.group === "bad_weather") + if (circ) { + + if (circ["lepMod"][this.badWeather] !== 0) { + lepRestModifier += circ["lepMod"][this.badWeather] + } + + if (circ["aspMod"][this.badWeather] !== 0) { + aspRestModifier += circ["aspMod"][this.badWeather] + } + + } + } + + if (this.woundTreated) { + context.woundMod = `1w20-${this.woundRegenerationModifier ?? 0}` + } else { + if (context.hasWounds) { + context.woundMod = `1w20+${context.wounds * 3}` + } + } + + if (this.restingType === this.#type.DRAUßEN && this.badCamp) { + const circ = this.#circumstances.find(p => p.group === "bad_camp") + if (circ) { + lepRestModifier += Number(circ.lepMod) + aspRestModifier += Number(circ.aspMod) + } + } + + if (this.watch) { + const circ = this.#circumstances.find(p => p.group === "watch") + if (circ) { + lepRestModifier += Number(circ.lepMod) + aspRestModifier += Number(circ.aspMod) + } + } + + if (this.interrupted) { + const circ = this.#circumstances.find(p => p.group === "interrupted") + if (circ) { + lepRestModifier += Number(circ.lepMod) + aspRestModifier += Number(circ.aspMod) + } + } + + const [lepDieAmount, lepModifier] = this._actor.system.regeneration.lep.split("d6") + const [aspDieAmount, aspModifier] = this._actor.system.regeneration.asp.split("d6") + + const lepMod = (Number(lepModifier) + (lepRestModifier ?? "")) + const aspMod = (Number(aspModifier) + (aspRestModifier ?? "")) + + if (lepMod == 0) { + context.lepMod = lepDieAmount + "d6" + } else if (lepMod > 0) { + context.lepMod = lepDieAmount + "d6+" + lepMod + } else { + context.lepMod = lepDieAmount + "d6" + lepMod + } + + if (aspMod == 0) { + context.aspMod = aspDieAmount + "d6" + } else if (lepMod > 0) { + context.aspMod = aspDieAmount + "d6+" + aspMod + } else { + context.aspMod = aspDieAmount + "d6" + aspMod + } + + if (this._actor.system.regeneration.ko < 0) { + context.koRoll = `1w20${this._actor.system.regeneration.ko}` + } else { + context.koRoll = `1w20+${this._actor.system.regeneration.ko}` + } + + if (this._actor.system.regeneration.in < 0) { + context.inRoll = `1w20${this._actor.system.regeneration.in}` + } else { + context.inRoll = `1w20+${this._actor.system.regeneration.in}` + } + + console.log(this, context) + + return context + } + + async _prepareContext(options) { + const context = await super._prepareContext(options) + return this.#updateData(context) + } + + _onRender(context, options) { + + } +} \ No newline at end of file diff --git a/src/module/sheets/characterSheet.mjs b/src/module/sheets/characterSheet.mjs index 2d46c838..da8df1df 100644 --- a/src/module/sheets/characterSheet.mjs +++ b/src/module/sheets/characterSheet.mjs @@ -10,6 +10,7 @@ import Spells from "./character/spells.mjs" import {CombatActionDialog} from "../dialog/combatAction.mjs"; import {ActionManager} from "./actions/action-manager.mjs"; import {DefenseActionDialog} from "../dialog/defenseAction.mjs"; +import {RestingDialog} from "../dialog/restingDialog.mjs"; const {HandlebarsApplicationMixin, DocumentSheetV2} = foundry.applications.api const {ActorSheetV2} = foundry.applications.sheets @@ -43,6 +44,7 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { progressCooldown: CharacterSheet.#progressCooldown, cancelCooldown: CharacterSheet.#cancelCooldown, activateCooldown: CharacterSheet.#activateCooldown, + rest: CharacterSheet.#startResting, } } @@ -207,6 +209,12 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { } } + static #startResting(event, target) { + const dialog = new RestingDialog(this.document) + + dialog.render(true) + } + /** * Handle form submission * @this {AdvantageSheet} @@ -242,6 +250,7 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { const actorData = context.document context.system = actorData.system + context.actorId = actorData._id context.isOwner = actorData.isOwner context.flags = actorData.flags context.derived = context.document.system @@ -309,7 +318,6 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { context.keper = Math.min((actorData.system.kap.aktuell / actorData.system.kap.max) * 100, 100) context.aspper = Math.min((actorData.system.asp.aktuell / actorData.system.asp.max) * 100, 100) context.lepcurrent = actorData.system.lep.aktuell ?? 0 - context.aupcurrent = actorData.system.aup.aktuell ?? 0 const fernkampf = actorData.findEquipmentOnSlot("fernkampf", actorData.system.setEquipped, actorData) @@ -391,6 +399,9 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { cooldown.tooltip = `${cooldown.data.maneuver.name}
Waffe:${weapon.name}
Ziel: ${target.name}
Wurfziel: ${cooldown.data.targetNumber}
Aktionen verbleibend: ${cooldown.current}` }) + context.hasSpells = actorData.itemTypes["Spell"].length > 0 + context.hasLiturgies = actorData.itemTypes["Liturgy"].length > 0 + context.attributes = [ { eigenschaft: "mu", diff --git a/src/style/organisms/_resting-dialog.scss b/src/style/organisms/_resting-dialog.scss new file mode 100644 index 00000000..f2b212b6 --- /dev/null +++ b/src/style/organisms/_resting-dialog.scss @@ -0,0 +1,80 @@ +.application.dsa41.dialog.resting { + + section { + + display: flex; + flex-direction: column; + height: 100%; + gap: 16px; + + .options { + flex: 1; + + div.setting { + margin-left: 32px; + padding-bottom: 16px; + + &.range { + padding-bottom: 0; + margin-left: 34px; + } + + &.combined { + input[type="checkbox"], input[type="radio"] { + margin-left: -32px; + padding-right: 8px; + } + + span { + height: 32px; + line-height: 32px; + vertical-align: 2px; + } + } + + &.radio, &.boolean { + + padding-bottom: 0; + + input[type="checkbox"], input[type="radio"] { + margin-left: -32px; + padding-right: 8px; + } + + span { + height: 32px; + line-height: 32px; + vertical-align: 2px; + } + + } + + } + + } + + fieldset { + flex: 0; + + .results { + + display: grid; + grid-template-columns: 120px 1fr; + + span { + + + } + + } + + } + + .actions { + flex: 0; + width: 100%; + } + + } + +} \ No newline at end of file diff --git a/src/style/styles.scss b/src/style/styles.scss index d2a1795f..b811eb52 100644 --- a/src/style/styles.scss +++ b/src/style/styles.scss @@ -30,3 +30,4 @@ @use "organisms/xml-import-dialog"; @use "organisms/combat-action-dialog"; @use "organisms/merchant-sheet"; +@use "organisms/resting-dialog"; \ No newline at end of file diff --git a/src/templates/actor/character/main-sheet.hbs b/src/templates/actor/character/main-sheet.hbs index 58b1bcc3..dccc8ead 100644 --- a/src/templates/actor/character/main-sheet.hbs +++ b/src/templates/actor/character/main-sheet.hbs @@ -40,7 +40,7 @@ {{#if this.hasLiturgies}} {{/if}} @@ -50,6 +50,10 @@ {{/if}} + + {{#each attacks}}

{{this.using}} ({{this.name}})

diff --git a/src/templates/actor/character/tab-combat.hbs b/src/templates/actor/character/tab-combat.hbs index f8e35f13..652a4e89 100644 --- a/src/templates/actor/character/tab-combat.hbs +++ b/src/templates/actor/character/tab-combat.hbs @@ -4,20 +4,20 @@
-
+
w6
-
+
von
{{#if ausdauer}} -
+
von diff --git a/src/templates/dialog/resting-dialog.hbs b/src/templates/dialog/resting-dialog.hbs new file mode 100644 index 00000000..4cfdc476 --- /dev/null +++ b/src/templates/dialog/resting-dialog.hbs @@ -0,0 +1,75 @@ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ + {{#each circumstances}} +
+ +
+ {{/each}} +
+ +
+ Modifikatoren +
+ LeP-Regeneration + {{lepMod}} + KO Wurf + {{koRoll}} + + {{#if hasAsP}} + AsP-Regeneration + {{aspMod}} + IN Wurf + {{inRoll}} + + {{/if}} + {{#if hasWounds}} + Wunden-Heilung + {{woundMod}} + {{/if}} +
+ +
+ +
\ No newline at end of file