187 lines
5.8 KiB
JavaScript
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,
|
|
}
|
|
}
|
|
}
|