dsa-4th-edition/src/main.mjs
2024-11-12 00:25:47 +01:00

815 lines
40 KiB
JavaScript

const { TypeDataModel } = foundry.abstract;
const { SchemaField, NumberField, StringField, BooleanField } = foundry.data.fields;
const { ActorSheetV2, ItemSheetV2 } = foundry.applications.sheets;
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
const { OperatorTerm, NumericTerm } = foundry.dice.terms;
Hooks.once("init", async function() {
CONFIG.Combat.initiative.formula = "1d6 + @computed.ini_basiswert[INI-Basiswert]";
CONFIG.Actor.dataModels.Player = DSA41_CharacterData;
CONFIG.Actor.documentClass = DSA41_CharacterDocument;
CONFIG.Item.dataModels.Gegenstand = DSA41_GegenstandData;
CONFIG.Item.dataModels.Ruestung = DSA41_RuestungData;
CONFIG.Item.dataModels.Bewaffnung = DSA41_BewaffnungData;
DocumentSheetConfig.unregisterSheet(Actor, "core", ActorSheet);
DocumentSheetConfig.registerSheet(Actor, "dsa41", DSA41_ActorSheet, {
makeDefault: true,
types: [
"Player",
]
});
DocumentSheetConfig.unregisterSheet(Item, "core", ItemSheet);
DocumentSheetConfig.registerSheet(Item, "dsa41", DSA41_ItemSheetV2, {
makeDefault: true,
types: [
"Gegenstand",
"Ruestung",
"Bewaffnung",
]
});
await loadTemplates({
"editable-input": "systems/dsa-4th-edition/src/EditableInput.hbs",
"attribute_tooltip": "systems/dsa-4th-edition/src/Tooltips/Attribute.hbs",
"attacke_tooltip": "systems/dsa-4th-edition/src/Tooltips/Attacke.hbs",
"parade_tooltip": "systems/dsa-4th-edition/src/Tooltips/Parade.hbs",
"trefferpunkte_tooltip": "systems/dsa-4th-edition/src/Tooltips/Trefferpunkte.hbs",
"fernkampf_attacke_tooltip": "systems/dsa-4th-edition/src/Tooltips/FernkampfAttacke.hbs",
"fernkampf_trefferpunkte_tooltip": "systems/dsa-4th-edition/src/Tooltips/FernkampfTrefferpunkte.hbs",
"trefferpunkte_dialog": "systems/dsa-4th-edition/src/Dialog/Trefferpunkte.hbs",
"fernkampf_angriff_dialog": "systems/dsa-4th-edition/src/Dialogs/FernkampfAngriff.hbs",
"fernkampf_trefferpunkte_dialog": "systems/dsa-4th-edition/src/Dialogs/FernkampfTrefferpunkte.hbs",
});
});
function get_minified_formula(formula, data) {
const terms = Roll.simplifyTerms(Roll.parse(formula, data));
let output = [];
let is_combinable = true;
for (let i = 0; i < terms.length; i++) {
const term = terms[i];
if (is_combinable && term instanceof NumericTerm) {
for (let j = i + 1; j < terms.length - 1; j += 2) {
let operator = terms[j];
let next = terms[j + 1];
if (!(operator instanceof OperatorTerm && next instanceof NumericTerm))
break;
switch (operator.operator) {
case "+": {
term.number += next.number;
i += 2;
} break;
case "-": {
term.number -= next.number;
i += 2;
} break;
default:
break;
}
}
is_combinable = false;
} else {
if (term instanceof OperatorTerm && (term.operator == "+" || term.operator == "-")) {
is_combinable = true;
} else {
is_combinable = false;
}
}
output.push(term);
}
output = Roll.simplifyTerms(output);
return Roll.getFormula(output);
}
class AttributeField extends SchemaField {
constructor() {
return super({
initial: new NumberField({ integer: true, initial: 8, min: 0 }),
advancement: new NumberField({ integer: true, initial: 0, min: 0 }),
modifier: new NumberField({ integer: true, initial: 0 }),
});
}
}
class CombatTalentField extends SchemaField {
constructor() {
return super({
talentwert: new NumberField({ integer: true, initial: 0 }),
attacke: new NumberField({ integer: true, initial: 0 }),
parade: new NumberField({ integer: true, initial: 0 }),
});
}
}
class DSA41_CharacterDocument extends Actor {
}
class DSA41_CharacterData extends TypeDataModel {
static defineSchema() {
return {
race: new StringField(),
culture: new StringField(),
profession: new StringField(),
attributes: new SchemaField({
courage: new AttributeField(),
cleverness: new AttributeField(),
intuition: new AttributeField(),
charisma: new AttributeField(),
dexterity: new AttributeField(),
agility: new AttributeField(),
constitution: new AttributeField(),
strength: new AttributeField(),
}),
kampf_talente: new SchemaField({
anderthalbhaender: new CombatTalentField(),
armbrust: new CombatTalentField(),
belagerungswaffen: new CombatTalentField(),
blasrohr: new CombatTalentField(),
bogen: new CombatTalentField(),
diskus: new CombatTalentField(),
dolche: new CombatTalentField(),
fechtwaffen: new CombatTalentField(),
hiebwaffen: new CombatTalentField(),
infanteriewaffen: new CombatTalentField(),
kettenstaebe: new CombatTalentField(),
kettenwaffen: new CombatTalentField(),
lanzenreiten: new CombatTalentField(),
peitsche: new CombatTalentField(),
raufen: new CombatTalentField(),
ringen: new CombatTalentField(),
saebel: new CombatTalentField(),
schleuder: new CombatTalentField(),
schwerter: new CombatTalentField(),
speere: new CombatTalentField(),
staebe: new CombatTalentField(),
wurfbeile: new CombatTalentField(),
wurfmesser: new CombatTalentField(),
wurfspeere: new CombatTalentField(),
zweihandflegel: new CombatTalentField(),
zweihand_hiebwaffen: new CombatTalentField(),
zweihandschwerter: new CombatTalentField(),
}),
talente: new SchemaField({
koerperliche: new SchemaField({
akrobatik: new NumberField({ integer: true, initial: 0 }),
athletik: new NumberField({ integer: true, initial: 0 }),
fliegen: new NumberField({ integer: true, initial: 0 }),
gaukeleien: new NumberField({ integer: true, initial: 0 }),
klettern: new NumberField({ integer: true, initial: 0 }),
koerperbeherrschung: new NumberField({ integer: true, initial: 0 }),
reiten: new NumberField({ integer: true, initial: 0 }),
schleichen: new NumberField({ integer: true, initial: 0 }),
schwimmen: new NumberField({ integer: true, initial: 0 }),
selbstbeherrschung: new NumberField({ integer: true, initial: 0 }),
sich_verstecken: new NumberField({ integer: true, initial: 0 }),
singen: new NumberField({ integer: true, initial: 0 }),
sinnenschärfe: new NumberField({ integer: true, initial: 0 }),
skifahren: new NumberField({ integer: true, initial: 0 }),
stimmen_imitieren: new NumberField({ integer: true, initial: 0 }),
tanzen: new NumberField({ integer: true, initial: 0 }),
taschendiebstahl: new NumberField({ integer: true, initial: 0 }),
zechen: new NumberField({ integer: true, initial: 0 }),
}),
gesellschaftliche: new SchemaField({
betoeren: new NumberField({ integer: true, initial: 0 }),
etikette: new NumberField({ integer: true, initial: 0 }),
gassenwissen: new NumberField({ integer: true, initial: 0 }),
lehren: new NumberField({ integer: true, initial: 0 }),
menschenkenntnis: new NumberField({ integer: true, initial: 0 }),
schauspielerei: new NumberField({ integer: true, initial: 0 }),
schriftlicher_ausdruck: new NumberField({ integer: true, initial: 0 }),
sich_verkleiden: new NumberField({ integer: true, initial: 0 }),
ueberreden: new NumberField({ integer: true, initial: 0 }),
ueberzeugen: new NumberField({ integer: true, initial: 0 }),
}),
natur: new SchemaField({
faehrtensuchen: new NumberField({ integer: true, initial: 0 }),
fallenstellen: new NumberField({ integer: true, initial: 0 }),
fesseln: new NumberField({ integer: true, initial: 0 }),
fischen: new NumberField({ integer: true, initial: 0 }),
orientierung: new NumberField({ integer: true, initial: 0 }),
wettervorhersage: new NumberField({ integer: true, initial: 0 }),
wildnisleben: new NumberField({ integer: true, initial: 0 }),
}),
wissens: new SchemaField({
anatomie: new NumberField({ integer: true, initial: 0 }),
baukunst: new NumberField({ integer: true, initial: 0 }),
brett_kartenspiel: new NumberField({ integer: true, initial: 0 }),
geographie: new NumberField({ integer: true, initial: 0 }),
geschichtswissen: new NumberField({ integer: true, initial: 0 }),
gesteinskunde: new NumberField({ integer: true, initial: 0 }),
goetter_kulte: new NumberField({ integer: true, initial: 0 }),
heraldik: new NumberField({ integer: true, initial: 0 }),
huettenkunde: new NumberField({ integer: true, initial: 0 }),
kriegskunst: new NumberField({ integer: true, initial: 0 }),
kryptographie: new NumberField({ integer: true, initial: 0 }),
magiekunde: new NumberField({ integer: true, initial: 0 }),
mechanik: new NumberField({ integer: true, initial: 0 }),
pflanzenkunde: new NumberField({ integer: true, initial: 0 }),
philosophie: new NumberField({ integer: true, initial: 0 }),
rechnen: new NumberField({ integer: true, initial: 0 }),
rechtskunde: new NumberField({ integer: true, initial: 0 }),
sagen_legenden: new NumberField({ integer: true, initial: 0 }),
schaetzen: new NumberField({ integer: true, initial: 0 }),
sprachenkunde: new NumberField({ integer: true, initial: 0 }),
staatskunst: new NumberField({ integer: true, initial: 0 }),
sternkunde: new NumberField({ integer: true, initial: 0 }),
tierkunde: new NumberField({ integer: true, initial: 0 }),
}),
sprachen: new SchemaField({
lesen_schreiben: new NumberField({ integer: true, initial: 0 }),
muttersprache: new NumberField({ integer: true, initial: 0 }),
fremdsprache: new NumberField({ integer: true, initial: 0 }),
}),
handwerks: new SchemaField({
abrichten: new NumberField({ integer: true, initial: 0 }),
ackerbau: new NumberField({ integer: true, initial: 0 }),
alchimie: new NumberField({ integer: true, initial: 0 }),
bergbau: new NumberField({ integer: true, initial: 0 }),
bogenbau: new NumberField({ integer: true, initial: 0 }),
boote_fahren: new NumberField({ integer: true, initial: 0 }),
brauer: new NumberField({ integer: true, initial: 0 }),
drucker: new NumberField({ integer: true, initial: 0 }),
fahrzeug_lenken: new NumberField({ integer: true, initial: 0 }),
falschspiel: new NumberField({ integer: true, initial: 0 }),
feinmechanik: new NumberField({ integer: true, initial: 0 }),
feuersteinbearbeitung: new NumberField({ integer: true, initial: 0 }),
fleischer: new NumberField({ integer: true, initial: 0 }),
gerber: new NumberField({ integer: true, initial: 0 }),
glaskunst: new NumberField({ integer: true, initial: 0 }),
grobschmied: new NumberField({ integer: true, initial: 0 }),
handel: new NumberField({ integer: true, initial: 0 }),
hauswirtschaft: new NumberField({ integer: true, initial: 0 }),
heilkunde_gift: new NumberField({ integer: true, initial: 0 }),
heilkunde_krankheiten: new NumberField({ integer: true, initial: 0 }),
heilkunde_seele: new NumberField({ integer: true, initial: 0 }),
heilkunde_wunden: new NumberField({ integer: true, initial: 0 }),
holzbearbeitung: new NumberField({ integer: true, initial: 0 }),
instrumentenbauer: new NumberField({ integer: true, initial: 0 }),
kartographie: new NumberField({ integer: true, initial: 0 }),
kochen: new NumberField({ integer: true, initial: 0 }),
kristallzucht: new NumberField({ integer: true, initial: 0 }),
lederarbeiten: new NumberField({ integer: true, initial: 0 }),
malen_zeichnen: new NumberField({ integer: true, initial: 0 }),
maurer: new NumberField({ integer: true, initial: 0 }),
metallguss: new NumberField({ integer: true, initial: 0 }),
musizieren: new NumberField({ integer: true, initial: 0 }),
schloesser_knacken: new NumberField({ integer: true, initial: 0 }),
schnapps_brennen: new NumberField({ integer: true, initial: 0 }),
schneidern: new NumberField({ integer: true, initial: 0 }),
seefahrt: new NumberField({ integer: true, initial: 0 }),
seiler: new NumberField({ integer: true, initial: 0 }),
steinmetz: new NumberField({ integer: true, initial: 0 }),
juwelier: new NumberField({ integer: true, initial: 0 }),
stellmacher: new NumberField({ integer: true, initial: 0 }),
stoffe_faerben: new NumberField({ integer: true, initial: 0 }),
taetowieren: new NumberField({ integer: true, initial: 0 }),
toepfern: new NumberField({ integer: true, initial: 0 }),
viehzucht: new NumberField({ integer: true, initial: 0 }),
webkunst: new NumberField({ integer: true, initial: 0 }),
winzer: new NumberField({ integer: true, initial: 0 }),
zimmermann: new NumberField({ integer: true, initial: 0 }),
}),
}),
}
}
async prepareDerivedData() {
super.prepareDerivedData();
this.computed = {
attributes: {},
attributes_without_modifiers: {},
kampf: {
talente: {},
waffen: {},
fernkampf_waffen: {},
ruestungen: {},
ruestungen_gesamt: {
kopf: 0,
brust: 0,
ruecken: 0,
bauch: 0,
linker_arm: 0,
rechter_arm: 0,
linkes_bein: 0,
rechtes_bein: 0,
gesamt_ruestungsschutz: 0.0,
gesamt_behinderung: 0.0,
},
},
};
for (const [attribute, values] of Object.entries(this.attributes)) {
this.computed.attributes[attribute] = values.initial + values.advancement + values.modifier;
this.computed.attributes_without_modifiers[attribute] = values.initial + values.advancement;
}
this.computed.max_lebenspunkte = Math.round((this.computed.attributes_without_modifiers.constitution + this.computed.attributes_without_modifiers.constitution + this.computed.attributes_without_modifiers.strength) / 2);
this.computed.max_ausdauer = Math.round((this.computed.attributes_without_modifiers.courage + this.computed.attributes_without_modifiers.constitution + this.computed.attributes_without_modifiers.agility) / 2);
this.computed.max_atralenergie = Math.round((this.computed.attributes_without_modifiers.courage + this.computed.attributes_without_modifiers.intuition + this.computed.attributes_without_modifiers.charisma) / 2);
this.computed.magieresistenz = Math.round((this.computed.attributes_without_modifiers.courage + this.computed.attributes_without_modifiers.cleverness + this.computed.attributes_without_modifiers.constitution) / 5);
this.computed.ini_basiswert = Math.round((this.computed.attributes_without_modifiers.courage + this.computed.attributes_without_modifiers.courage + this.computed.attributes_without_modifiers.intuition + this.computed.attributes_without_modifiers.agility) / 5);
this.computed.at_basiswert = Math.round((this.computed.attributes_without_modifiers.courage + this.computed.attributes_without_modifiers.agility + this.computed.attributes_without_modifiers.strength) / 5);;
this.computed.pa_basiswert = Math.round((this.computed.attributes_without_modifiers.intuition + this.computed.attributes_without_modifiers.agility + this.computed.attributes_without_modifiers.strength) / 5);;
this.computed.fk_basiswert = Math.round((this.computed.attributes_without_modifiers.intuition + this.computed.attributes_without_modifiers.dexterity + this.computed.attributes_without_modifiers.strength) / 5);;
const equipped_ruestungen = this.parent.items.filter((x) => x.type === "Ruestung");
for (const item of equipped_ruestungen) {
this.computed.kampf.ruestungen[item._id] = { item: item };
this.computed.kampf.ruestungen_gesamt.kopf += item.system.kopf;
this.computed.kampf.ruestungen_gesamt.brust += item.system.brust;
this.computed.kampf.ruestungen_gesamt.ruecken += item.system.ruecken;
this.computed.kampf.ruestungen_gesamt.bauch += item.system.bauch;
this.computed.kampf.ruestungen_gesamt.linker_arm += item.system.linker_arm;
this.computed.kampf.ruestungen_gesamt.rechter_arm += item.system.rechter_arm;
this.computed.kampf.ruestungen_gesamt.linkes_bein += item.system.linkes_bein;
this.computed.kampf.ruestungen_gesamt.rechtes_bein += item.system.rechtes_bein;
this.computed.kampf.ruestungen_gesamt.gesamt_ruestungsschutz += item.system.gesamt_ruestungsschutz;
this.computed.kampf.ruestungen_gesamt.gesamt_behinderung += item.system.gesamt_behinderung;
}
for (let [key, value] of Object.entries(this.computed.kampf.ruestungen_gesamt)) {
this.computed.kampf.ruestungen_gesamt[key] = Math.round(value);
}
for(const [name, values] of Object.entries(this.kampf_talente)) {
this.computed.kampf.talente[name] = {};
this.computed.kampf.talente[name].attacke = this.computed.at_basiswert + values.attacke;
this.computed.kampf.talente[name].parade = this.computed.pa_basiswert + values.parade;
}
const equipped_bewaffnung = this.parent.items.filter((x) => x.type === "Bewaffnung");
const equipped_nahkampfwaffen = equipped_bewaffnung.filter((x) => x.system.nahkampfwaffe.aktiv);
const equipped_parierwaffen = equipped_bewaffnung.filter((x) => x.system.parierwaffe.aktiv);
const equipped_schilde = equipped_bewaffnung.filter((x) => x.system.schild.aktiv);
const equipped_fernkampfwaffen = equipped_bewaffnung.filter((x) => x.system.fernkampfwaffe.aktiv);
for(const item of equipped_nahkampfwaffen) {
if (item.system.nahkampfwaffe.aktiv) {
let computed = this.computed.kampf.waffen[item._id] = {
item: item,
attacke: 0,
parade: 0,
trefferpunkte: "",
trefferpunkte_display: "",
basis_attacke: 0,
basis_parade: 0,
talent_attacke: 0,
talent_parade: 0,
modifikator_attacke: 0,
modifikator_parade: 0,
tp_kk: 0,
parierwaffe_attacke: 0,
parierwaffe_parade: 0,
schild_attacke: 0,
schild_parade: 0,
};
computed.basis_attacke = this.computed.at_basiswert;
computed.basis_parade = this.computed.pa_basiswert;
const talent = item.system.nahkampfwaffe.kampftalente;
computed.talent_attacke = this.kampf_talente[talent]?.attacke ?? 0;
computed.talent_parade = this.kampf_talente[talent]?.parade ?? 0;
computed.modifikator_attacke = item.system.nahkampfwaffe.modifikator_attacke;
computed.modifikator_parade = item.system.nahkampfwaffe.modifikator_parade;
if (item.system.nahkampfwaffe.schadensschritte != 0) {
computed.tp_kk = Math.trunc((this.computed.attributes.strength - item.system.nahkampfwaffe.schwellenwert) / item.system.nahkampfwaffe.schadensschritte);
}
for (const parierwaffe of equipped_parierwaffen) {
if (parierwaffe._id === item._id) continue;
computed.parierwaffe_attacke += parierwaffe.system.parierwaffe.modifikator_attacke;
computed.parierwaffe_parade += parierwaffe.system.parierwaffe.modifikator_parade;
}
for (const schild of equipped_schilde) {
if (schild._id === item._id) continue;
computed.schild_attacke += schild.system.schild.modifikator_attacke;
computed.schild_parade += schild.system.schild.modifikator_parade;
}
computed.attacke = computed.basis_attacke + computed.talent_attacke + computed.modifikator_attacke + computed.parierwaffe_attacke + computed.schild_attacke + Math.min(computed.tp_kk, 0);
computed.parade = computed.basis_parade + computed.talent_parade + computed.modifikator_parade + computed.parierwaffe_parade + computed.schild_parade + Math.min(computed.tp_kk, 0);
computed.trefferpunkte = get_minified_formula(item.system.nahkampfwaffe.basis + (computed.tp_kk != 0 ? " + " + computed.tp_kk : ""));
computed.trefferpunkte_display = computed.trefferpunkte.replace(/[\+\-]/, (op) => "<br>" + op);
}
}
for (const item of equipped_fernkampfwaffen) {
let computed = this.computed.kampf.fernkampf_waffen[item._id] = {
item: item,
attacke: 0,
trefferpunkte: "",
trefferpunkte_display: "",
basis_attacke: 0,
talent_attacke: 0,
};
computed.basis_attacke = this.computed.fk_basiswert;
const talent = item.system.nahkampfwaffe.kampftalente;
computed.talent_attacke = this.kampf_talente[talent]?.attacke ?? 0;
computed.attacke = computed.basis_attacke + computed.talent_attacke;
computed.trefferpunkte = get_minified_formula(item.system.fernkampfwaffe.basis);
computed.trefferpunkte_display = computed.trefferpunkte.replace(/[\+\-]/, (op) => "<br>" + op);
}
}
}
class DSA41_GegenstandData extends TypeDataModel {
static defineSchema() {
return {
gewicht: new NumberField({ integer: false, initial: 0, min: 0 }),
preis: new NumberField({ integer: true, initial: 0, min: 0 }),
};
}
}
class DSA41_RuestungData extends TypeDataModel {
static defineSchema() {
return {
gewicht: new NumberField({ integer: false, initial: 0, min: 0 }),
preis: new NumberField({ integer: true, initial: 0, min: 0 }),
kopf: new NumberField({ integer: true, initial: 0, min: 0 }),
brust: new NumberField({ integer: true, initial: 0, min: 0 }),
ruecken: new NumberField({ integer: true, initial: 0, min: 0 }),
bauch: new NumberField({ integer: true, initial: 0, min: 0 }),
linker_arm: new NumberField({ integer: true, initial: 0, min: 0 }),
rechter_arm: new NumberField({ integer: true, initial: 0, min: 0 }),
linkes_bein: new NumberField({ integer: true, initial: 0, min: 0 }),
rechtes_bein: new NumberField({ integer: true, initial: 0, min: 0 }),
gesamt_ruestungsschutz: new NumberField({ integer: false, initial: 0, min: 0 }),
gesamt_behinderung: new NumberField({ integer: false, initial: 0, min: 0 }),
};
}
}
class DSA41_BewaffnungData extends TypeDataModel {
static defineSchema() {
return {
gewicht: new NumberField({ integer: false, initial: 0, min: 0 }),
preis: new NumberField({ integer: true, initial: 0, min: 0 }),
angelegt: new BooleanField(),
nahkampfwaffe: new SchemaField({
aktiv: new BooleanField(),
basis: new StringField(),
schwellenwert: new NumberField({ integer: true, initial: 0, min: 0 }),
schadensschritte: new NumberField({ integer: true, initial: 0, min: 0 }),
modifikator_attacke: new NumberField({ integer: true, initial: 0 }),
modifikator_parade: new NumberField({ integer: true, initial: 0 }),
initiative: new NumberField({ integer: true, initial: 0 }),
bruchfaktor: new NumberField({ integer: true, initial: 0 }),
distanzklasse: new StringField(),
kampftalente: new StringField(),
laenge: new NumberField({ integer: true, initial: 0, min: 0 }),
zweihaendig: new BooleanField(),
werfbar: new BooleanField(),
improvisiert: new BooleanField(),
priviligiert: new BooleanField(),
}),
parierwaffe: new SchemaField({
aktiv: new BooleanField(),
modifikator_attacke: new NumberField({ integer: true, initial: 0 }),
modifikator_parade: new NumberField({ integer: true, initial: 0 }),
initiative: new NumberField({ integer: true, initial: 0 }),
bruchfaktor: new NumberField({ integer: true, initial: 0 }),
}),
schild: new SchemaField({
aktiv: new BooleanField(),
groesse: new StringField(),
modifikator_attacke: new NumberField({ integer: true, initial: 0 }),
modifikator_parade: new NumberField({ integer: true, initial: 0 }),
initiative: new NumberField({ integer: true, initial: 0 }),
bruchfaktor: new NumberField({ integer: true, initial: 0 }),
}),
fernkampfwaffe: new SchemaField({
aktiv: new BooleanField(),
basis: new StringField({ initial: "1d6 + 1" }),
laden: new NumberField({ integer: true, initial: 0 }),
reichweite1: new NumberField({ integer: true, initial: 0 }),
reichweite2: new NumberField({ integer: true, initial: 0 }),
reichweite3: new NumberField({ integer: true, initial: 0 }),
reichweite4: new NumberField({ integer: true, initial: 0 }),
reichweite5: new NumberField({ integer: true, initial: 0 }),
modifikator1: new NumberField({ integer: true, initial: 0, nullable: true }),
modifikator2: new NumberField({ integer: true, initial: 0, nullable: true }),
modifikator3: new NumberField({ integer: true, initial: 0, nullable: true }),
modifikator4: new NumberField({ integer: true, initial: 0, nullable: true }),
modifikator5: new NumberField({ integer: true, initial: 0, nullable: true }),
munitionskosten: new NumberField({ integer: true, initial: 0 }),
munitionsgewicht: new NumberField({ integer: true, initial: 0 }),
}),
};
}
}
function DSA41_ApplicationMixin(BaseApplication) {
class DSA41_Application extends HandlebarsApplicationMixin(BaseApplication) {
static DEFAULT_OPTIONS= {
classes: [ "DSA41" ],
window: { resizable: true },
form: { submitOnChange: true },
};
async _prepareContext(options) {
return this;
}
}
return DSA41_Application;
}
class DSA41_Dialog extends DSA41_ApplicationMixin(ApplicationV2) {
static PARTS = {
Parade: { template: "systems/dsa-4th-edition/src/Dialogs/Parade.hbs" },
Trefferpunkte: { template: "systems/dsa-4th-edition/src/Dialogs/Trefferpunkte.hbs" },
FernkampfAngriff: { template: "systems/dsa-4th-edition/src/Dialogs/FernkampfAngriff.hbs" },
FernkampfTrefferpunkte: { template: "systems/dsa-4th-edition/src/Dialogs/FernkampfTrefferpunkte.hbs" },
footer: { template: "templates/generic/form-footer.hbs" },
};
static DEFAULT_OPTIONS = {
classes: [ "DSA41", "Dialog" ],
window: {
title: "Dialog",
minimizable: false,
resizable: false
},
tag: "form",
form: {
closeOnSubmit: false,
submitOnChange: true,
}
};
get formData() {
const data = Object.fromEntries(
new FormData(this.element).entries().map(([key, value])=>{
if (typeof value != "string") return [key, value];
return isNaN(value) ? [key, value] : [key, Number(value)];
})
);
return data;
}
buttons = [{
type: "submit",
label: "Confirm"
}];
static async wait(dialog_type, options) {
return new Promise((resolve, reject) => {
const dialog = new this({
dialog_type: dialog_type,
form: {
handler: (event, form, formData) => {
if (event.type != "submit") {
dialog.render({ force: false });
return;
}
dialog.close();
resolve(dialog.formData);
}
},
...options,
});
dialog.render({ force: true });
});
}
_configureRenderOptions(options) {
super._configureRenderOptions(options);
options.parts = [ this.options.dialog_type, "footer" ];
}
_onFirstRender(context, options) {
this.render(options)
}
}
class DSA41_ActorSheet extends DSA41_ApplicationMixin(ActorSheetV2) {
static PARTS = {
ActorSheet: { template: "systems/dsa-4th-edition/src/ActorSheet.hbs" },
};
static DEFAULT_OPTIONS = {
position: { width: "800", height: "650" },
actions: {
"roll": async function(event, target) {
var roll_formula = event.target.closest("[data-roll]").dataset.roll;
const roll_type = event.target.closest("[data-roll-type]").dataset.rollType;
const success_value = event.target.closest("[data-success-value]")?.dataset.successValue;
const item_id = event.target.closest("[data-item-id]")?.dataset.itemId;
const item = this.document.items.get(item_id);
let flavor = game.i18n.localize("DSA41.roll_types." + roll_type);
if (typeof success_value !== 'undefined') {
flavor += " <= " + success_value;
}
if (roll_type == "parade") {
const item = this.document.system.computed.kampf.waffen[item_id];
const title = game.i18n.localize("DSA41.roll_types." + roll_type) + ": " + item.name;
const data = await DSA41_Dialog.wait("Parade", { window: { title: title }, item: item });
if (data.crit == "on") {
roll_formula = "round((" + roll_formula + ") / 2)";
}
let roll = new Roll(roll_formula + " + " + data.modifikator, this.document.system);
roll.toMessage({
speaker: ChatMessage.getSpeaker({ actor: this.document }),
flavor: flavor,
});
return;
}
if (roll_type == "trefferpunkte") {
const item = this.document.system.computed.kampf.waffen[item_id];
const title = game.i18n.localize("DSA41.roll_types." + roll_type) + ": " + item.name;
const data = await DSA41_Dialog.wait("Trefferpunkte", { window: { title: title }, item: item });
if (data.crit == "on") {
roll_formula = "2 * (" + item.item.system.nahkampfwaffe.basis + ")" + " + " + item.tp_kk;
}
let roll = new Roll(roll_formula + " + " + data.modifikator, this.document.system);
roll.toMessage({
speaker: ChatMessage.getSpeaker({ actor: this.document }),
flavor: flavor,
});
return;
}
if (roll_type == "fernkampf-attacke") {
const title = game.i18n.localize("DSA41.roll_types." + roll_type) + ": " + item.name;
const data = await DSA41_Dialog.wait("FernkampfAngriff", { window: { title: title }, item: this.document.system.computed.kampf.fernkampf_waffen[item_id] });
const groessen_modifikator = Math.max(data.ziel_groesse + data.deckung + data.ziel_bewegung, -2);
const andere_modifikator = data.entfernung + data.wind + data.modifikator;
const total_modifikator = groessen_modifikator + andere_modifikator;
let roll = new Roll(roll_formula + " + " + total_modifikator, this.document.system);
roll.toMessage({
speaker: ChatMessage.getSpeaker({ actor: this.document }),
flavor: game.i18n.localize("DSA41.roll_types." + roll_type),
});
return;
}
if (roll_type == "fernkampf-trefferpunkte") {
const title = game.i18n.localize("DSA41.roll_types." + roll_type) + ": " + item.name;
const data = await DSA41_Dialog.wait("FernkampfTrefferpunkte", { window: { title: title }, item: this.document.system.computed.kampf.fernkampf_waffen[item_id] });
const entfernung = item.system.fernkampfwaffe[data.entfernung];
if (data.crit == "on") {
roll_formula = "2 * (" + roll_formula + ")";
}
let roll = new Roll(roll_formula + " + " + (entfernung + data.modifikator), this.document.system);
roll.toMessage({
speaker: ChatMessage.getSpeaker({ actor: this.document }),
flavor: game.i18n.localize("DSA41.roll_types." + roll_type),
});
return;
}
let roll = new Roll(roll_formula, this.document.system);
roll.toMessage({
speaker: ChatMessage.getSpeaker({ actor: this.document }),
flavor: flavor,
});
},
"item-open": async function(event, target) {
const item_id = event.target.closest("[data-item-id]").dataset.itemId;
const item = this.document.items.get(item_id);
item.sheet.render(true)
},
"item-delete": async function(event, target) {
const item_id = event.target.closest("[data-item-id]").dataset.itemId;
const item = this.document.items.get(item_id);
item.delete();
},
},
};
_onFirstRender(context, options) {
super._onFirstRender(context, options);
const content = this.element.querySelector(".window-content");
content.addEventListener("drop", async (event) => {
event.preventDefault();
const data = TextEditor.getDragEventData(event);
switch (data.type) {
case "Item": {
const item = await Item.implementation.fromDropData(data);
await this.actor.createEmbeddedDocuments("Item", [item.toObject()]);
} break;
}
});
}
tabGroups = { primary: "tab1" };
}
class DSA41_ItemSheetV2 extends DSA41_ApplicationMixin(ItemSheetV2) {
static PARTS = {
Bewaffnung: { template: "systems/dsa-4th-edition/src/ItemSheets/Bewaffnung.hbs" },
Gegenstand: { template: "systems/dsa-4th-edition/src/ItemSheets/Gegenstand.hbs" },
Ruestung: { template: "systems/dsa-4th-edition/src/ItemSheets/Ruestung.hbs" },
};
static DEFAULT_OPTIONS = {
position: { width: "600", height: "auto" },
};
tabGroups = { primary: "tab1" };
_configureRenderOptions(options) {
super._configureRenderOptions(options);
options.parts = [ this.options.document.type ];
}
}