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}}