foundry-dsa41-game/src/module/data/skill.mjs

187 lines
5.8 KiB
JavaScript

import BaseItem from "./baseItem.mjs";
const {
DocumentIdField,
ArrayField,
NumberField,
SchemaField,
StringField,
HTMLField
} = foundry.data.fields;
export class SkillDataModel extends BaseItem {
static defineSchema() {
return {
name: new StringField({required: true}),
gruppe: new StringField({required: true}),
taw: new NumberField({integer: true, initial: 0}),
at: new NumberField({required: false, integer: true, initial: 0}),
pa: new NumberField({required: false, integer: true, nullable: true, initial: 0}),
probe: new ArrayField(new StringField(), {exact: 3}), // References one of the eight attributes by name
voraussetzung: new SchemaField({
talent: new DocumentIdField(),
wert: new NumberField(),
}, {required: false}), // Required skills at a given level
talent: new HTMLField({required: true}),
behinderung: new StringField({required: false}), // BE-X
komplexität: new NumberField({required: false}), // In case of languages
}
}
/**
* Augment the basic Item data model with additional dynamic data.
*/
prepareData() {
}
/**
* Prepare a data object which is passed to any Roll formulas which are created related to this Item
* @private
*/
getRollData() {
}
/**
* Determines the values to consult for the given type of roll (normal: talent roll, attack: AT, parry: PA
* @returns {{NORMAL: string, ATTACK: string, PARRY: string}}
* @constructor
*/
get SKILL_MODE() {
return {
NORMAL: "NORMAL",
ATTACK: "ATTACK",
PARRY: "PARRY",
}
}
/**
*
* @param rollMode {["publicroll","gmroll"] }
* @param mode
* @returns {Promise<void>}
*/
async roll(rollMode = null, mode = this.SKILL_MODE.NORMAL) {
rollMode = rollMode ?? game.settings.get('core', 'rollMode');
switch (mode) {
case this.SKILL_MODE.NORMAL:
return this.#talentRoll(rollMode)
case this.SKILL_MODE.ATTACK:
case this.SKILL_MODE.PARRY:
return this.#combatRoll(rollMode, mode)
}
}
async #combatRoll(rollMode, mode) {
const owner = this.parent.parent
const rollData = owner.getRollData()
let targetNumber = 0
if (mode === this.SKILL_MODE.ATTACK) {
targetNumber = this.at + owner.system.at.basis
} else {
targetNumber = this.pa + owner.system.pa.basis
}
let roll1 = new Roll(`1d20cs<${targetNumber}`, owner.getRollData());
let evaluated1 = (await roll1.evaluate())
const rolledValue = evaluated1.terms[0].results[0].result
if (rolledValue === 1 || rolledValue === 20) { // TODO: Modify this target
// fill with actual evaluation (targetNumber should be reduced by X and roll against that again)
}
let message = ""
if (mode === this.SKILL_MODE.ATTACK) {
if (rolledValue <= targetNumber) {
message = `Würde treffen [${rolledValue}]`
} else {
message = `Verfehlt [${rolledValue}]`
}
} else {
if (rolledValue <= targetNumber) {
message = `Würde parrieren [${rolledValue}]`
} else {
message = `Verfehlt die parade [${rolledValue}]`
}
}
evaluated1.toMessage({
speaker: ChatMessage.getSpeaker({actor: owner}),
flavor: message,
rollMode,
})
}
async #talentRoll(rollMode) {
const owner = this.parent.parent
let roll1 = new Roll("3d20", owner.getRollData());
let evaluated1 = (await roll1.evaluate())
const dsaDieRollEvaluated = this._evaluateRoll(evaluated1.terms[0].results, {
taw: this.taw,
werte: [this.probe[0], this.probe[1], this.probe[2]],
})
if (dsaDieRollEvaluated.tap >= 0) { // erfolg
evaluated1.toMessage({
speaker: ChatMessage.getSpeaker({actor: owner}),
flavor: ` ${dsaDieRollEvaluated.meisterlich ? 'Meisterlich geschafft' : 'Geschafft'} mit ${dsaDieRollEvaluated.tap} Punkten übrig`,
rollMode,
})
} else { // misserfolg
evaluated1.toMessage({
speaker: ChatMessage.getSpeaker({actor: owner}),
flavor: ` ${dsaDieRollEvaluated.meisterlich ? 'Gepatzt' : ''} mit ${Math.abs(dsaDieRollEvaluated.tap)} Punkten daneben`,
rollMode,
})
}
}
_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,
}
}
}