329 lines
13 KiB
JavaScript
329 lines
13 KiB
JavaScript
import {LiturgyData} from "../data/miracle/liturgydata.mjs";
|
|
import {Zonenruestung, Zonenwunde, Wunde} from "../data/Trefferzone.js";
|
|
import {PlayerCharacterDataModel} from "../data/character.mjs";
|
|
|
|
export class Character extends Actor {
|
|
|
|
/**
|
|
import() {
|
|
let input = document.createElement('input')
|
|
input.type = 'file'
|
|
input.accept = '.xml'
|
|
input.onchange = e => {
|
|
importCharacter(this.id, e.target.files[0])
|
|
}
|
|
input.click()
|
|
}*/
|
|
|
|
/**
|
|
* @override
|
|
* Augment the actor source data with additional dynamic data. Typically,
|
|
* you'll want to handle most of your calculated/derived data in this step.
|
|
* Data calculated in this step should generally not exist in template.json
|
|
* (such as ability modifiers rather than ability scores) and should be
|
|
* available both inside and outside of character sheets (such as if an actor
|
|
* is queried and has a roll executed directly from it).
|
|
*/
|
|
prepareDerivedData() {
|
|
|
|
if (this.type === "character") {
|
|
const actorData = this;
|
|
const systemData = actorData.system;
|
|
|
|
systemData.attribute.mu.aktuell = systemData.attribute.mu.start + systemData.attribute.mu.mod;
|
|
systemData.attribute.kl.aktuell = systemData.attribute.kl.start + systemData.attribute.kl.mod;
|
|
systemData.attribute.in.aktuell = systemData.attribute.in.start + systemData.attribute.in.mod;
|
|
systemData.attribute.ch.aktuell = systemData.attribute.ch.start + systemData.attribute.ch.mod;
|
|
|
|
systemData.attribute.ff.aktuell = systemData.attribute.ff.start + systemData.attribute.ff.mod;
|
|
systemData.attribute.ge.aktuell = systemData.attribute.ge.start + systemData.attribute.ge.mod;
|
|
systemData.attribute.ko.aktuell = systemData.attribute.ko.start + systemData.attribute.ko.mod;
|
|
systemData.attribute.kk.aktuell = systemData.attribute.kk.start + systemData.attribute.kk.mod;
|
|
|
|
const mu = systemData.attribute.mu.aktuell;
|
|
const kl = systemData.attribute.kl.aktuell;
|
|
const _in = systemData.attribute.in.aktuell;
|
|
const ch = systemData.attribute.ch.aktuell;
|
|
const ff = systemData.attribute.ff.aktuell;
|
|
const ge = systemData.attribute.ge.aktuell;
|
|
const ko = systemData.attribute.ko.aktuell;
|
|
const kk = systemData.attribute.kk.aktuell;
|
|
|
|
systemData.lep.max = Math.round((ko + ko + kk) / 2) + systemData.lep.mod;
|
|
systemData.aup.max = Math.round((mu + ko + ge) / 2) + systemData.aup.mod;
|
|
systemData.asp.max = Math.round((mu + _in + ch) / 2) + systemData.asp.mod;
|
|
|
|
|
|
systemData.at = systemData.at ?? {links: {}, rechts: {}}
|
|
systemData.at.links = systemData.at.links ?? {
|
|
aktuell: 0,
|
|
mods: 0
|
|
}
|
|
systemData.at.rechts = systemData.at.rechts ?? {
|
|
aktuell: 0,
|
|
mods: 0
|
|
}
|
|
systemData.at.basis = Math.round((mu + ge + kk) / 5)
|
|
systemData.at.aktuell = systemData.at.basis + (systemData.at.mod ?? 0);
|
|
systemData.at.links.aktuell = systemData.at.basis + (systemData.at.links.mod ?? 0);
|
|
systemData.at.rechts.aktuell = systemData.at.basis + (systemData.at.rechts.mod ?? 0);
|
|
systemData.pa = systemData.pa ?? {links: {}, rechts: {}}
|
|
systemData.pa.links = systemData.pa.links ?? {
|
|
aktuell: 0,
|
|
mods: 0
|
|
}
|
|
systemData.pa.rechts = systemData.pa.rechts ?? {
|
|
aktuell: 0,
|
|
mods: 0
|
|
}
|
|
systemData.pa.basis = Math.round((_in + ge + kk) / 5);
|
|
systemData.pa.aktuell = systemData.pa.basis + (systemData.pa.mod ?? 0);
|
|
systemData.pa.links.aktuell = systemData.pa.basis + (systemData.pa.links.mod ?? 0);
|
|
systemData.pa.rechts.aktuell = systemData.pa.basis + (systemData.pa.links.mod ?? 0);
|
|
systemData.fk = systemData.fk ?? {
|
|
aktuell: 0,
|
|
mods: 0
|
|
}
|
|
systemData.fk.basis = Math.round((_in + ff + kk) / 5);
|
|
systemData.fk.aktuell = systemData.fk.basis + (systemData.fk.mod ?? 0);
|
|
|
|
systemData.ini.basis = Math.round((mu + mu + _in + ge) / 5)
|
|
systemData.ini.aktuell = systemData.ini.basis + (systemData.ini.mod ?? 0);
|
|
systemData.mr.basis = Math.round((mu + kl + ko) / 5)
|
|
systemData.mr.aktuell = systemData.mr.basis + (systemData.mr.mod ?? 0);
|
|
systemData.gs.basis = 6;
|
|
systemData.gs.aktuell = systemData.gs.basis + (systemData.gs.mod ?? 0); // TOOD: get GS from spezien
|
|
|
|
|
|
if (game.settings.get("DSA_4-1", "optional_ruestungzonen")) {
|
|
systemData.rs = {
|
|
brust: 0,
|
|
bauch: 0,
|
|
armlinks: 0,
|
|
armrechts: 0,
|
|
beinlinks: 0,
|
|
beinrechts: 0,
|
|
kopf: 0,
|
|
}; // only with DSA_4-1.optional_trefferzonen = true
|
|
} else {
|
|
systemData.rs = 0; // only with DSA_4-1.optional_trefferzonen = false
|
|
}
|
|
systemData.be = 0;
|
|
|
|
|
|
// half KO is the maximum a character can sustain wounds before collapsing
|
|
systemData.wunden.max = ko / 2;
|
|
if (game.settings.get("DSA_4-1", "optional_trefferzonen")) {
|
|
systemData.wunden.kopf = 0;
|
|
systemData.wunden.brust = 0;
|
|
systemData.wunden.bauch = 0;
|
|
systemData.wunden.ruecken = 0;
|
|
systemData.wunden.armlinks = 0;
|
|
systemData.wunden.armrechts = 0;
|
|
systemData.wunden.beinlinks = 0;
|
|
systemData.wunden.beinrechts = 0;
|
|
}
|
|
|
|
systemData.ws = ko / 2;
|
|
|
|
// map current set to RS and BE
|
|
|
|
const ausruestung = systemData.heldenausruestung[systemData.setEquipped];
|
|
const zonesToCheck = [
|
|
"brust",
|
|
"bauch",
|
|
"ruecken",
|
|
"kopf",
|
|
"armlinks",
|
|
"armrechts",
|
|
"beinlinks",
|
|
"beinrechts"]
|
|
|
|
if (game.settings.get("DSA_4-1", "optional_ruestungzonen")) {
|
|
systemData.rs.gesamt = 0
|
|
systemData.rs.brust = 0
|
|
systemData.rs.bauch = 0
|
|
systemData.rs.ruecken = 0
|
|
systemData.rs.kopf = 0
|
|
systemData.rs.armlinks = 0
|
|
systemData.rs.armrechts = 0
|
|
systemData.rs.beinlinks = 0
|
|
systemData.rs.beinrechts = 0
|
|
} else {
|
|
systemData.rs = 0
|
|
}
|
|
|
|
zonesToCheck.forEach((zone) => {
|
|
systemData.be += systemData.parent.items.get(ausruestung[zone])?.system.armorHandicap ?? 0
|
|
if (game.settings.get("DSA_4-1", "optional_ruestungzonen")) {
|
|
zonesToCheck.forEach((itemZone) => {
|
|
systemData.rs[itemZone] += systemData.parent.items.get(ausruestung[zone])?.system.armorValue[itemZone] ?? 0
|
|
})
|
|
} else {
|
|
systemData.rs += systemData.parent.items.get(ausruestung[zone])?.system.armorValue.total ?? 0
|
|
}
|
|
})
|
|
|
|
systemData.kap.max = 0;
|
|
|
|
// evaluate deities for KaP
|
|
|
|
const deities = systemData.parent.items.filter(p => p.type === "Blessing")
|
|
deities?.forEach((deity) => {
|
|
if (LiturgyData.alverans.includes(deity.system.gottheit)) {
|
|
systemData.kap.max = 24;
|
|
} else if (systemData.kap.max === 0) {
|
|
systemData.kap.max += 12;
|
|
}
|
|
}, 0)
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
/**
|
|
* Augment the basic Item data model with additional dynamic data.
|
|
*/
|
|
prepareData() {
|
|
super.prepareData();
|
|
this.prepareEmbeddedDocuments();
|
|
}
|
|
|
|
getRollData() {
|
|
const data = super.getRollData();
|
|
this.prepareDerivedData()
|
|
|
|
if (this.type !== 'character' && this.type !== 'creature') return;
|
|
|
|
|
|
// move sonderfertigkeiten into data, if it isn't in data the actor doesn't have that sonderfertigkeit
|
|
|
|
data.sf = {}
|
|
if (data.sonderfertigkeiten) {
|
|
data.sonderfertigkeiten.forEach(sf => {
|
|
data.sf[sf.name] = sf.auswahl;
|
|
})
|
|
delete data.sonderfertigkeiten;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
|
|
findEquipmentOnSlot(slot, setNumber) {
|
|
return this.items.get(this.system.heldenausruestung[setNumber ?? this.system.setEquipped]?.[slot])
|
|
}
|
|
|
|
getEquipmentSetUpdateObject() {
|
|
const updateObject = {}
|
|
Array.from(this.system.heldenausruestung).forEach((equipmentSet, index) => {
|
|
updateObject[`system.heldenausruestung.${index}.links`] = equipmentSet.links;
|
|
updateObject[`system.heldenausruestung.${index}.rechts`] = equipmentSet.rechts;
|
|
updateObject[`system.heldenausruestung.${index}.brust`] = equipmentSet.brust;
|
|
updateObject[`system.heldenausruestung.${index}.bauch`] = equipmentSet.bauch;
|
|
updateObject[`system.heldenausruestung.${index}.ruecken`] = equipmentSet.ruecken;
|
|
updateObject[`system.heldenausruestung.${index}.kopf`] = equipmentSet.kopf;
|
|
updateObject[`system.heldenausruestung.${index}.fernkampf`] = equipmentSet.fernkampf;
|
|
updateObject[`system.heldenausruestung.${index}.munition`] = equipmentSet.munition;
|
|
updateObject[`system.heldenausruestung.${index}.armlinks`] = equipmentSet.armlinks;
|
|
updateObject[`system.heldenausruestung.${index}.armrechts`] = equipmentSet.armrechts;
|
|
updateObject[`system.heldenausruestung.${index}.beinlinks`] = equipmentSet.beinlinks;
|
|
updateObject[`system.heldenausruestung.${index}.beinrechts`] = equipmentSet.beinrechts;
|
|
|
|
})
|
|
return updateObject;
|
|
}
|
|
|
|
|
|
isWorn(itemId) {
|
|
|
|
const slots = PlayerCharacterDataModel.getSlots()
|
|
const set = this.system.heldenausruestung[this.system.setEquipped]
|
|
if (set) {
|
|
for (const slot of slots) {
|
|
const equipmentSlotId = set[slot]
|
|
if (equipmentSlotId === itemId) {
|
|
return slot
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
async rollAttack(data) {
|
|
const maneuver = data.manuever
|
|
const weapon = this.itemTypes["Equipment"].find(p => p._id === data.weapon)
|
|
const skill = this.itemTypes["Skill"].find(p => p._id === data.skill)
|
|
const target = game.actors.get(game.scenes.current.tokens.find(p => p._id === data.target).actorId)
|
|
|
|
const roll = new Roll("1d20cs<" + data.targetNumber)
|
|
const evaluated1 = (await roll.evaluate())
|
|
|
|
await evaluated1.toMessage({
|
|
speaker: ChatMessage.getSpeaker({actor: this}),
|
|
flavor: `Attackiert ${target.name} mit ${weapon.name} (${skill.name})<br/>${data.modDescription}`,
|
|
rollMode: "publicroll",
|
|
})
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param amount
|
|
* @param zone either null or one of
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async takeDamage(amount = null, zone = null) {
|
|
|
|
this.prepareDerivedData()
|
|
|
|
const playWithZoneArmor = game.settings.get("DSA_4-1", "optional_ruestungzonen")
|
|
const playWithWoundZones = game.settings.get("DSA_4-1", "optional_trefferzonen")
|
|
const previousLeP = this.system.lep.aktuell;
|
|
|
|
if (amount == null) {
|
|
// TODO show Dialog for entering damage amount (TP)
|
|
}
|
|
|
|
let armorReduction = 0
|
|
let setEquipped = this.system.setEquipped
|
|
let woundThreshold = this.system.ws
|
|
|
|
if (playWithZoneArmor) {
|
|
const armorId = this.system.heldenausruestung[setEquipped][Zonenruestung[zone]]
|
|
const zoneArmor = await this.items.find(p => p._id === armorId)
|
|
if (!zoneArmor) {
|
|
return console.error(`zone "${zone}" is not a valid value`)
|
|
}
|
|
armorReduction = zoneArmor.system.armorValue ?? 0
|
|
} else {
|
|
armorReduction = this.system.rs
|
|
}
|
|
|
|
let damage = amount - armorReduction
|
|
let wounds = damage / woundThreshold
|
|
|
|
let wound = null
|
|
|
|
if (playWithWoundZones) {
|
|
wound = await game.packs.get("DSA_4-1.Wounds").index.find(p => p.name === Zonenwunde[zone])
|
|
if (!wound) {
|
|
return console.error(`Wunden Dokument zu "${zone}" konnten nicht gefunden werden`)
|
|
}
|
|
} else {
|
|
wound = await game.packs.get("DSA_4-1.Wounds").index.find(p => p.name === Wunde)
|
|
if (!wound) {
|
|
return console.error(`Wunden Dokument zu "${Wunde}" konnten nicht gefunden werden`)
|
|
}
|
|
}
|
|
|
|
// TODO this doesnt work yet, wound documents wont get expanded
|
|
|
|
for (let i = 0; i < wounds; i++) {
|
|
await this.createEmbeddedDocuments('Item', [wound])
|
|
}
|
|
await this.update({system: {lep: {aktuell: previousLeP - damage}}})
|
|
}
|
|
}
|