implements roll mechanic for battle dice

pull/62/head
macniel 2025-10-29 15:14:22 +01:00
parent b50e4d3090
commit 196b99b52e
3 changed files with 189 additions and 11 deletions

View File

@ -0,0 +1,101 @@
export class Talent {
/**
* @typedef TalentEigenschaften
* @property Number mu
* @property Number kl
* @property Number in
* @property Number ch
* @property Number ff
* @property Number ge
* @property Number ko
* @property Number kk
*/
/**
* @typedef TalentData
* @property {String} name
* @property {Number} taw
* @property {TalentEigenschaften} eigenschaften
* @property {"mu","kl","in","ch","ff","ge","ko","kk"} eigenschaft1
* @property {"mu","kl","in","ch","ff","ge","ko","kk"} eigenschaft2
* @property {"mu","kl","in","ch","ff","ge","ko","kk"} eigenschaft3
*/
/**
*
* @param {TalentData} data
**/
constructor(data) {
this.data = data
}
/**
* @param {"publicroll", "gmroll", "privateroll"} rollMode
* @returns {Promise<{tap: any, meisterlich: boolean, patzer: boolean, evaluatedRoll: Roll<EmptyObject> & {_evaluated: true, _total: number, readonly total: number}}>}
*/
evaluate(rollMode) {
return this.#talentRoll(this.data, rollMode)
}
/**
*
* @param {TalentData} data
* @param {"publicroll", "gmroll", "privateroll"} rollMode
* @returns {Promise<{tap: any, meisterlich: boolean, patzer: boolean, evaluatedRoll: Roll<EmptyObject> & {_evaluated: true, _total: number, readonly total: number}}>}
*/
async #talentRoll(data, rollMode) {
let roll1 = new Roll("3d20");
let evaluated1 = (await roll1.evaluate())
const dsaDieRollEvaluated = this._evaluateRoll(evaluated1.terms[0].results, {
taw: data.taw,
werte: [data.eigenschaften[data.eigenschaft1], data.eigenschaften[data.eigenschaft2], data.eigenschaften[data.eigenschaft3]],
})
return {
...dsaDieRollEvaluated,
evaluatedRoll: evaluated1,
}
}
_evaluateRoll(rolledDice, {
taw,
lowerThreshold = 1,
upperThreshold = 20,
countToMeisterlich = 3,
countToPatzer = 3,
werte = []
}) {
let tap = taw;
let meisterlichCounter = 0;
let patzerCounter = 0;
let failCounter = 0;
rolledDice.forEach((rolledDie, index) => {
if (tap < 0 && rolledDie.result > werte[index]) {
tap -= rolledDie.result - werte[index];
if (tap < 0) { // konnte nicht vollständig ausgeglichen werden
failCounter++;
}
} else if (rolledDie.result > werte[index]) { // taw ist bereits aufgebraucht und wert kann nicht ausgeglichen werden
tap -= rolledDie.result - werte[index];
failCounter++;
}
if (rolledDie.result <= lowerThreshold) meisterlichCounter++;
if (rolledDie.result > upperThreshold) patzerCounter++;
})
return {
tap,
meisterlich: meisterlichCounter === countToMeisterlich,
patzer: patzerCounter === countToPatzer,
}
}
}

View File

@ -1,4 +1,5 @@
import {ActionManager} from "../sheets/actions/action-manager.mjs";
import {Talent} from "../data/talent.mjs";
const {ApplicationV2, HandlebarsApplicationMixin} = foundry.applications.api
@ -76,19 +77,94 @@ export class BattleDialog extends HandlebarsApplicationMixin(ApplicationV2) {
this.render({parts: ["form"]})
}
static #doBattle(event, target) {
// TODO get test values from talents either from the object or from the fields
if (formData.object['saveOffenseData']) {
}
if (formData.object['saveDefenseData']) {
}
static async #doBattle(event, target) {
// TODO perform Dice Rolls but in secret mode so its up to the GM if they want to display the result or not
let offenseTalent = {}
if (this._offenseActor && this._offenseActor.items.get(this._offenseTalent)) {
const skill = this._offenseActor.items.get(this._offenseTalent)
offenseTalent.name = skill.name
offenseTalent.taw = skill.system.taw
offenseTalent.probe = skill.system.probe
} else {
offenseTalent.name = this.element.querySelector('input[name="offense.talent.name"]').value
offenseTalent.taw = this.element.querySelector('input[name="offense.talent.taw"]').value
offenseTalent.probe = [
this.element.querySelector('input[name="offense.talent.probe.0.name"]').value,
this.element.querySelector('input[name="offense.talent.probe.1.name"]').value,
this.element.querySelector('input[name="offense.talent.probe.2.name"]').value
]
}
offenseTalent.eigenschaften = {}
if (this._offenseActor && this._offenseActor.system.attribute) {
offenseTalent.eigenschaften = this._offenseActor.system.attribute
} else {
offenseTalent.eigenschaften = {
mu: this.element.querySelector('input[name="offenseAttributes.mu"]').value,
kl: this.element.querySelector('input[name="offenseAttributes.in"]').value,
in: this.element.querySelector('input[name="offenseAttributes.kl"]').value,
ch: this.element.querySelector('input[name="offenseAttributes.ch"]').value,
ff: this.element.querySelector('input[name="offenseAttributes.ff"]').value,
ge: this.element.querySelector('input[name="offenseAttributes.ge"]').value,
ko: this.element.querySelector('input[name="offenseAttributes.ko"]').value,
kk: this.element.querySelector('input[name="offenseAttributes.kk"]').value,
}
}
let defenseTalent = {}
if (this._defenseActor && this._defenseActor.items.get(this._defenseTalent)) {
const skill = this._defenseActor.items.get(this._defenseTalent)
defenseTalent.name = skill.name
defenseTalent.taw = skill.system.taw
defenseTalent.probe = skill.system.probe
} else {
defenseTalent.name = this.element.querySelector('input[name="defense.talent.name"]').value
defenseTalent.taw = this.element.querySelector('input[name="defense.talent.taw"]').value
defenseTalent.probe = [
this.element.querySelector('input[name="defense.talent.probe.0.name"]').value,
this.element.querySelector('input[name="defense.talent.probe.1.name"]').value,
this.element.querySelector('input[name="defense.talent.probe.2.name"]').value
]
}
defenseTalent.eigenschaften = {}
if (this._defenseActor && this._defenseActor.system.attribute) {
defenseTalent.eigenschaften = this._defenseActor.system.attribute
} else {
defenseTalent.eigenschaften = {
mu: this.element.querySelector('input[name="defenseAttributes.mu"]').value,
kl: this.element.querySelector('input[name="defenseAttributes.in"]').value,
in: this.element.querySelector('input[name="defenseAttributes.kl"]').value,
ch: this.element.querySelector('input[name="defenseAttributes.ch"]').value,
ff: this.element.querySelector('input[name="defenseAttributes.ff"]').value,
ge: this.element.querySelector('input[name="defenseAttributes.ge"]').value,
ko: this.element.querySelector('input[name="defenseAttributes.ko"]').value,
kk: this.element.querySelector('input[name="defenseAttributes.kk"]').value,
}
}
const offense = await (new Talent(offenseTalent)).evaluate("gmroll")
const defense = await (new Talent(defenseTalent)).evaluate("gmroll")
offense.evaluatedRoll.toMessage({
speaker: ChatMessage.getSpeaker({actor: this._offenseActor}),
flavor: `Talent: ${offenseTalent.name}<br/>TaP: ${offense.tap}<br/>${offense.meisterlich ? "Meisterlich" : ""}${offense.patzer ? "Petzer" : ""}`,
})
defense.evaluatedRoll.toMessage({
speaker: ChatMessage.getSpeaker({actor: this._defenseActor}),
flavor: `Talent: ${defenseTalent.name}<br/>TaP: ${defense.tap}<br/>${defense.meisterlich ? "Meisterlich" : ""}${defense.patzer ? "Petzer" : ""}`,
})
this.close()
}

View File

@ -2,6 +2,7 @@
<div class="presets">
<label><span>Voreinstellungen</span>
{{!-- TODO implement logic --}}
<select name="battle-presets">
<option value="" selected></option>
<option value="zechen-v-zechen">Vergleichend: Zechen</option>
@ -136,7 +137,7 @@
<div class="attribut">
<label><span>KO</span>
<output name="defenseAttributes.mu">{{offenseAttributes.ko}}</output>
<output name="offenseAttributes.mu">{{offenseAttributes.ko}}</output>
</label>
</div>
</div>