Compare commits

..

7 Commits

12 changed files with 297 additions and 33 deletions

View File

@ -0,0 +1,135 @@
import {XmlImport} from "../xml-import/xml-import.mjs";
import {SpecialAbility} from "../documents/specialAbility.mjs";
const {
ApplicationV2,
HandlebarsApplicationMixin
} = foundry.applications.api
export class MeditatingDialog extends HandlebarsApplicationMixin(ApplicationV2) {
static DEFAULT_OPTIONS = {
classes: ['dsa41', 'dialog', 'meditating'],
tag: "form",
position: {
width: 320,
height: 433
},
window: {
resizable: false,
title: 'Meditieren'
},
form: {
submitOnChange: true,
closeOnSubmit: false,
},
}
static PARTS = {
form: {
template: 'systems/DSA_4-1/templates/dialog/meditating-dialog.hbs',
}
}
/**
* @type {Actor}
* @private
*/
_actor = null
constructor(actor) {
super();
this._actor = actor
}
static async #onSubmitForm(event, form, formData) {
event.preventDefault()
}
#hatSonderfertigkeit(name) {
return this._actor.itemTypes["SpecialAbility"]?.find(p => p.name === name) != null
}
async _prepareContext(options) {
const context = await super._prepareContext(options)
context.hasAstraleMeditation = this.#hatSonderfertigkeit("Astrale Meditation")
context.hasThonnys = false
context.rituale = {}
this._actor.itemTypes["SpecialAbility"]?.filter(p => p.name.startsWith("Ritualkenntnis")).forEach( p => {
context.rituale[p.name] = p.name.split("Ritualkenntnis:")[1].trim()
})
context.duration = "0"
context.costs = "0 + 1d3"
context.result = 0
return context
}
#update(context) {
// params
const aspregain = this.element.querySelector('[name="aspRegain"]').value
const thonnys = !!this.element.querySelector('[name="thonnys"]:checked')
const astralmeditation = !!this.element.querySelector('[name="astralmeditation"]:checked')
const talent = this.element.querySelector('[name="skill"]').value
const rituale = this._actor.itemTypes["SpecialAbility"]?.filter(p => p.name.startsWith("Ritualkenntnis"))
let wert = rituale.find( p => context.rituale[talent])?.system.value.split(talent + ": ")[1].trim() ?? null
let ritualWert = 0
if (!wert) {
console.log("Ritualkenntniswert nicht gefunden für", talent)
// maybe its actually a talent?
const actualTalent = this._actor.itemTypes["Skill"].find(p => p.name === talent)
if (actualTalent) {
wert = actualTalent.system.taw
ritualWert = wert + "/2"
}
} else {
ritualWert = Number(wert)
}
// outputs
const probe = this.element.querySelector("[name='talentprobe']")
const dauer = this.element.querySelector("[name='duration']")
const costs = this.element.querySelector("[name='costs']")
const result = this.element.querySelector("[name='result']")
let penalty
let bonus = " +"+ ritualWert
if (astralmeditation && thonnys) {
bonus = " +" + ritualWert + " +3"
}
if (astralmeditation && thonnys) {
penalty = ""
} else {
penalty = " +1d3"
}
probe.value = "IN/CH/KO " + bonus
costs.value = ""+aspregain + penalty + " LeP"
dauer.value = aspregain + " Spielrunden"
result.value = aspregain + " AsP"
}
_onRender(context, options) {
this.#update(context)
this.element.querySelectorAll('[name="thonnys"], [name="astralmeditation"], [name="skill"], [name="aspRegain"]').forEach(e => e.addEventListener('change', (event) => {
this.#update(context)
}))
}
}

View File

@ -33,7 +33,7 @@ export class RestingDialog extends HandlebarsApplicationMixin(ApplicationV2) {
} }
/** /**
* @type {Actor} * @type {Character}
* @private * @private
*/ */
_actor = null _actor = null
@ -159,7 +159,7 @@ export class RestingDialog extends HandlebarsApplicationMixin(ApplicationV2) {
this.badWeather = -1 this.badWeather = -1
this.woundTreated = false this.woundTreated = false
this.woundRegenerationModifier = 0 this.woundRegenerationModifier = 0
this.wounds = 1 this.wounds = this._actor.getWoundModel().current
this.badCamp = false this.badCamp = false
this.watch = false this.watch = false
this.interrupted = false this.interrupted = false
@ -176,13 +176,14 @@ export class RestingDialog extends HandlebarsApplicationMixin(ApplicationV2) {
static async #regenerate() { static async #regenerate() {
const context = this.#updateData() const context = this.#updateData()
const rollData = this._actor.getRollData() const rollData = this._actor.system
// regenerate LeP // regenerate LeP
const targetBonusLep = rollData.attribute.ko.aktuell + context.koRoll const targetBonusLep = rollData.attribute.ko.aktuell + context.koRoll
const targetBonusAsp = rollData.attribute.in.aktuell + context.inRoll const targetBonusAsp = rollData.attribute.in.aktuell + context.inRoll
// TODO: needs to be situational (not every character has AsP for example) and not a compound roll statement
const regenerationRoll = await new Roll("1d20[Konstitution]+1d20[Intuition]+1d20[Konstitution] + 2d6").evaluate() const regenerationRoll = await new Roll("1d20[Konstitution]+1d20[Intuition]+1d20[Konstitution] + 2d6").evaluate()
const [koRoll, _, inRoll, __, woundRoll, ___, lepAndAsp] = regenerationRoll.terms // the 3d20s const [koRoll, _, inRoll, __, woundRoll, ___, lepAndAsp] = regenerationRoll.terms // the 3d20s
@ -191,6 +192,7 @@ export class RestingDialog extends HandlebarsApplicationMixin(ApplicationV2) {
const woundHealed = woundRoll.results[0].result <= rollData.attribute.ko.aktuell + (context.woundMod * -1) const woundHealed = woundRoll.results[0].result <= rollData.attribute.ko.aktuell + (context.woundMod * -1)
const [regLep, regAsp] = lepAndAsp.results // the 2d6s const [regLep, regAsp] = lepAndAsp.results // the 2d6s
// TODO: message needs to be better structured, and also include Buttons to heal LeP and restore AsP
await regenerationRoll.toMessage({ await regenerationRoll.toMessage({
speaker: ChatMessage.getSpeaker({actor: this.actor}), speaker: ChatMessage.getSpeaker({actor: this.actor}),
flavor: flavor:
@ -245,8 +247,6 @@ export class RestingDialog extends HandlebarsApplicationMixin(ApplicationV2) {
context.hasAsP = true context.hasAsP = true
context.wounds = this.wounds context.wounds = this.wounds
// TODO count wounds
let lepRestModifier = 0 let lepRestModifier = 0
let aspRestModifier = 0 let aspRestModifier = 0

View File

@ -621,4 +621,38 @@ export class Character extends Actor {
} }
await this.update({system: {lep: {aktuell: previousLeP - damage}}}) await this.update({system: {lep: {aktuell: previousLeP - damage}}})
} }
getWoundModel() {
if (this.type === "Character") {
const actorData = this;
const systemData = actorData.system;
let returnValue = {
max: 0,
current: 0,
ws: 0
}
returnValue.max = systemData.wunden.max;
console.log("spiel mit trefferzonen: ", game.settings.get("DSA_4-1", "optional_trefferzonen"));
if (game.settings.get("DSA_4-1", "optional_trefferzonen")) {
returnValue.current =
systemData.wunden.kopf +
systemData.wunden.brust +
systemData.wunden.bauch +
systemData.wunden.ruecken +
systemData.wunden.armlinks +
systemData.wunden.armrechts +
systemData.wunden.beinlinks +
systemData.wunden.beinrechts +
systemData.wunden.gesamt;
} else {
returnValue.current = systemData.wunden.aktuell;
}
returnValue.ws = systemData.ws;
return returnValue;
} else {
return null;
}
}
} }

View File

@ -26,6 +26,7 @@ import {SpellDialog} from "../dialog/spellDialog.mjs";
import {displayRoll} from "../globals/displayRoll.js"; import {displayRoll} from "../globals/displayRoll.js";
import {ResourceType} from "../data/character.mjs"; import {ResourceType} from "../data/character.mjs";
import {EditResourceDialog} from "../dialog/resourceDialog.mjs"; import {EditResourceDialog} from "../dialog/resourceDialog.mjs";
import {MeditatingDialog} from "../dialog/meditatingDialog.mjs";
const {HandlebarsApplicationMixin, DocumentSheetV2} = foundry.applications.api const {HandlebarsApplicationMixin, DocumentSheetV2} = foundry.applications.api
const {ActorSheetV2} = foundry.applications.sheets const {ActorSheetV2} = foundry.applications.sheets
@ -64,6 +65,7 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) {
cancelCooldown: CharacterSheet.#cancelCooldown, cancelCooldown: CharacterSheet.#cancelCooldown,
activateCooldown: CharacterSheet.#activateCooldown, activateCooldown: CharacterSheet.#activateCooldown,
rest: CharacterSheet.#startResting, rest: CharacterSheet.#startResting,
meditate: CharacterSheet.#startMeditating,
removeEffect: CharacterSheet.#removeEffect, removeEffect: CharacterSheet.#removeEffect,
rollDamage: CharacterSheet.#rollDamage, rollDamage: CharacterSheet.#rollDamage,
openItemBrowser: CharacterSheet.openItemBrowser, openItemBrowser: CharacterSheet.openItemBrowser,
@ -337,6 +339,11 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) {
dialog.render(true) dialog.render(true)
} }
static #startMeditating(event, target) {
const dialog = new MeditatingDialog(this.document)
dialog.render(true)
}
static async #removeEffect(event, target) { static async #removeEffect(event, target) {
const {actorId, effectId} = target.dataset const {actorId, effectId} = target.dataset

View File

@ -302,7 +302,7 @@ export class SpecialAbilitySheet extends HandlebarsApplicationMixin(DocumentShee
_onRender(context, options) { _onRender(context, options) {
if (this._selectedVariant == null) { if (this._selectedVariant == null) {
this._selectedVariant = this.document.system.auswahl[0].name this._selectedVariant = this.document.system.auswahl[0]?.name ?? ""
this._currentSelectedVariant = this.document.system.auswahl?.find(p => p.name === this._selectedVariant) this._currentSelectedVariant = this.document.system.auswahl?.find(p => p.name === this._selectedVariant)
this._currentSelectedVariantIndex = this.document.system.auswahl?.findIndex(p => p.name === this._selectedVariant) this._currentSelectedVariantIndex = this.document.system.auswahl?.findIndex(p => p.name === this._selectedVariant)
} }

View File

@ -16,6 +16,8 @@ export class XmlImport {
#unknownSpecialAbilities = [] #unknownSpecialAbilities = []
#unknownAdvantage = [] #unknownAdvantage = []
#ritualkenntnisse = {}
#months = [ #months = [
"Praios", "Praios",
"Rondra", "Rondra",
@ -238,19 +240,24 @@ export class XmlImport {
json.attribute.ko = this.#getAttributeJson(attributes, "Konstitution") json.attribute.ko = this.#getAttributeJson(attributes, "Konstitution")
json.attribute.kk = this.#getAttributeJson(attributes, "Körperkraft") json.attribute.kk = this.#getAttributeJson(attributes, "Körperkraft")
json.mr = { json.mr = {
mod: this.#filterAttribute(attributes, "Magieresistenz").mod mod: Number(this.#filterAttribute(attributes, "Magieresistenz").mod) +
Number(this.#filterAttribute(attributes, "Magieresistenz").value) // bought points
} }
json.lep = { json.lep = {
mod: this.#filterAttribute(attributes, "Lebensenergie").mod mod: Number(this.#filterAttribute(attributes, "Lebensenergie").mod) +
Number(this.#filterAttribute(attributes, "Lebensenergie").value) // bought points
} }
json.aup = { json.aup = {
mod: this.#filterAttribute(attributes, "Ausdauer").mod mod: Number(this.#filterAttribute(attributes, "Ausdauer").mod) +
Number(this.#filterAttribute(attributes, "Ausdauer").value) // bought points
} }
json.asp = { json.asp = {
mod: this.#filterAttribute(attributes, "Astralenergie").mod mod: Number(this.#filterAttribute(attributes, "Astralenergie").mod) +
Number(this.#filterAttribute(attributes, "Astralenergie").value) // bought points
} }
json.kap = { json.kap = {
mod: this.#filterAttribute(attributes, "Karmaenergie").mod mod: Number(this.#filterAttribute(attributes, "Karmaenergie").mod) +
Number(this.#filterAttribute(attributes, "Karmaenergie").value) // bought points
} }
let attribute = this.#filterAttribute(attributes, "ini") let attribute = this.#filterAttribute(attributes, "ini")
json.ini = { json.ini = {
@ -297,7 +304,6 @@ export class XmlImport {
} }
} }
} }
this.#mapSpecialAbilities(actor, specialAbilities)
json.liturgien = liturgies json.liturgien = liturgies
let combatValues = [] let combatValues = []
@ -315,7 +321,7 @@ export class XmlImport {
if (!options.skipSpells) this.#mapSpells(actor, held) if (!options.skipSpells) this.#mapSpells(actor, held)
if (!options.skipLiturgies) this.#mapLiturgies(actor, liturgies) if (!options.skipLiturgies) this.#mapLiturgies(actor, liturgies)
if (!options.skipEquipment) this.#mapEquipment(actor, held) if (!options.skipEquipment) this.#mapEquipment(actor, held)
if (!options.skipSpecialAbilities) this.#mapSpecialAbilities(actor, specialAbilities)
let notes = [] let notes = []
for (let note in held.kommentare) { for (let note in held.kommentare) {
note = held.kommentare[note] note = held.kommentare[note]
@ -465,8 +471,10 @@ export class XmlImport {
for (let talent in held.talentliste.talent) { for (let talent in held.talentliste.talent) {
talent = held.talentliste.talent[talent] talent = held.talentliste.talent[talent]
// hook liturgy
if (talent.name.startsWith("Liturgiekenntnis")) { if (talent.name.startsWith("Ritualkenntnis")) { // hook Ritualkenntnisse
this.#ritualkenntnisse[talent.name] = talent.value;
} else if (talent.name.startsWith("Liturgiekenntnis")) { // hook liturgy
actor.createEmbeddedDocuments('Item', [ actor.createEmbeddedDocuments('Item', [
new Blessing({ new Blessing({
@ -627,15 +635,32 @@ export class XmlImport {
}) })
}) })
} else { } else {
if (specialAbility.name.startsWith("Ritualkenntnis")) {
actor.createEmbeddedDocuments('Item', [ actor.createEmbeddedDocuments('Item', [
new SpecialAbility({ new SpecialAbility({
name: specialAbility.name, name: specialAbility.name,
type: "SpecialAbility", type: "SpecialAbility",
system: { system: {
name: specialAbility.name,
value: specialAbility.name + "(" + this.#ritualkenntnisse[specialAbility.name] + ")",
description: specialAbility.auswahl?.wahl?.join("\n"), description: specialAbility.auswahl?.wahl?.join("\n"),
} }
}) })
]) ])
} else {
actor.createEmbeddedDocuments('Item', [
new SpecialAbility({
name: specialAbility.name,
type: "SpecialAbility",
system: {
name: specialAbility.name,
description: specialAbility.auswahl?.wahl?.join("\n"),
}
})
])
}
this.#unknownSpecialAbilities.push(specialAbility.name) this.#unknownSpecialAbilities.push(specialAbility.name)
} }

View File

@ -95,7 +95,8 @@
.button { .button {
margin-bottom: 8px; margin-bottom: 8px;
display: flex;
justify-content: space-between;
} }
.attack { .attack {

View File

@ -1,4 +1,5 @@
.application.dsa41.dialog.resting { .application.dsa41.dialog.resting,
.application.dsa41.dialog.meditating {
section { section {
@ -13,6 +14,7 @@
div.setting { div.setting {
margin-left: 32px; margin-left: 32px;
padding-bottom: 16px; padding-bottom: 16px;
position: relative;
&.setting-nest-1 { &.setting-nest-1 {
margin-left: 64px; margin-left: 64px;
@ -23,6 +25,14 @@
margin-left: 34px; margin-left: 34px;
} }
input[type="checkbox"] {
margin-left: -32px;
&+span {
margin-left: 6px;
}
}
&.combined { &.combined {
input[type="checkbox"], input[type="radio"] { input[type="checkbox"], input[type="radio"] {
margin-left: -32px; margin-left: -32px;
@ -43,6 +53,8 @@
input[type="checkbox"], input[type="radio"] { input[type="checkbox"], input[type="radio"] {
margin-left: -32px; margin-left: -32px;
padding-right: 8px; padding-right: 8px;
position: absolute;
left: 0;
} }
span { span {

View File

@ -38,8 +38,7 @@
{{#if ausdauer}} {{#if ausdauer}}
<div class="sidebar-element resource-bar" data-action="editAup"> <div class="sidebar-element resource-bar" data-action="editAup">
<label>AuP:</label> <input type="number" name="system.aup.aktuell" value="{{this.system.aup.aktuell}}><span class="resource-fill aup" <label>AuP:</label> <input type="number" name="system.aup.aktuell" value="{{this.system.aup.aktuell}}"><span class="resource-fill aup" style="width: {{this.aupper}}%"></span>
style="width: {{this.aupper}}%"></span>
</div> </div>
{{/if}} {{/if}}
@ -57,6 +56,9 @@
<div class="sidebar-element button"> <div class="sidebar-element button">
<button type="button" data-action="rest"><i class="fa-solid fa-bed"></i> Rasten</button> <button type="button" data-action="rest"><i class="fa-solid fa-bed"></i> Rasten</button>
{{#if this.hasSpells}}
<button type="button" data-action="meditate"><i class="fa-solid fa-person-praying"></i> Meditieren</button>
{{/if}}
</div> </div>
{{#each attacks}} {{#each attacks}}

View File

@ -0,0 +1,46 @@
<section>
<div class="options">
<label>
<span>Ritual der Meditation</span>
<select name="skill">
{{selectOptions rituale}}
<option value="Musizieren">Elfisch</option>
<option value="Selbstbeherrschung">Schelmisch</option>
</select>
</label>
</div>
<div class="options">
<label>
<input type="checkbox" name="astralmeditation" {{checked this.hasAstraleMeditation}}/>
<span>Astrale Meditation erlernt</span>
</label>
</div>
<div class="options">
<label>
<input type="checkbox" name="thonnys" {{checked this.hasThonnys}}/>
<span>Thonnys verfügbar</span>
</label>
</div>
<div class="options">
<label>
<span>Lebenspunkte Umwandeln</span>
<input name="aspRegain" type="number"/>
</label>
</div>
<fieldset>
<legend>Modifikatoren</legend>
<div class="results">
<span id="header_skill">Probe</span>
<output name="talentprobe">{{talentprobe}}</output>
<span>Dauer</span>
<output name="duration">{{duration}}</output>
<span>Kosten</span>
<output name="costs">{{costs}}</output>
<span>Bei Erfolg</span>
<output name="result">{{result}}</output>
</div>
</fieldset>
<button type="button" class="actions" data-action="regenerate"><i class="fa-solid fa-person-praying"></i> Meditieren</button>
</section>

View File

@ -1,24 +1,25 @@
<section> <section>
{{!-- TODO: Refactor dialog and explain more what each and every knob and input does --}}
<div class="options"> <div class="options">
<div class="setting"> <div class="setting">
<label> <label>
<span>Dauer der Rast</span> <span>Dauer der Rast</span>
<input type="number" name="length" placeholder="6" value="{{length}}"/> <input type="number" name="length" placeholder="6" value="{{length}}"/><span>Stunden</span>
</label> </label>
</div> </div>
<div class="setting"> <div class="setting">
<label> <label>
<span>Wunden</span> <span>Anzahl Wunden</span>
<input type="number" name="wounds" placeholder="0" value="{{wounds}}"/> <input type="number" name="wounds" placeholder="0" value="{{wounds}}"/>
</label> </label>
</div> </div>
<div class="setting combined"> <div class="setting">
<label> <label>
<input type="checkbox" name="wound_treated"/><span>Wunde behandelt</span> <input type="checkbox" name="wound_treated"/>
<span>Wunde behandelt (Modifikator)</span>
<input type="number" name="wound_treat_modifier" placeholder="0" value="{{wound_treat_modifier}}"/> <input type="number" name="wound_treat_modifier" placeholder="0" value="{{wound_treat_modifier}}"/>
</label> </label>
</div> </div>

1
systems/DSA_4-1 120000
View File

@ -0,0 +1 @@
../src