Merge remote-tracking branch 'origin/main'

pull/43/head
Poppy 2025-09-28 15:57:11 +02:00
commit dba39e2c54
11 changed files with 778 additions and 49 deletions

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><?xml-stylesheet type="text/xsl" href="helden.xsl"?>
<helden Version="5.5.3">
<held key="1758727714759" name="Travin Walroder" stand="1758728185936">
<held key="1758727714759" name="Travin Walroder" stand="1758827726594">
<mods/>
<basis>
<geschlecht name="männlich"/>
@ -13,15 +13,17 @@
</settings>
<rasse name="helden.model.rasse.Mittellaender" string="Mittelländer">
<groesse gewicht="76" value="176"/>
<aussehen alter="19" augenfarbe="blau" aussehentext0="" aussehentext1="" aussehentext2=""
aussehentext3="" familietext0="" familietext1="" familietext2="" familietext3=""
familietext4="" familietext5="" gbjahr="1003" gbmonat="2" gbtag="11" gprest="26" gpstart="160"
haarfarbe="weißblond" kalender="Bosparans Fall" stand="" titel=""/>
<aussehen alter="19" augenfarbe="blau" aussehentext0="Sieht man"
aussehentext1="Kann man nicht verfehlen" aussehentext2="Ist vorhanden"
aussehentext3="Glitzert ein wenig" familietext0="Mama" familietext1="Papa"
familietext2="Schwester" familietext3="Bruder" familietext4="Oma" familietext5="Opa"
gbjahr="1003" gbmonat="13" gbtag="5" gprest="26" gpstart="160" haarfarbe="weißblond"
kalender="Bosparans Fall" stand="Graf" titel="von und zu"/>
</rasse>
<kultur name="helden.model.kultur.Garetien" string="Mittelländische Städte"/>
<ausbildungen>
<ausbildung art="Hauptprofession" name="helden.model.profession.Magier"
string="Akademie der Magischen Rüstung zu Gareth " tarnidentitaet="">
string="Akademie der Magischen Rüstung zu Gareth " tarnidentitaet="Depp vom Dienst">
<variante name="Akademie der Magischen Rüstung zu Gareth "/>
</ausbildung>
<ausbildung art="Spaetweihe" name="helden.model.profession.Geweihter"
@ -34,7 +36,7 @@
notiz7="" notiz8="" notiz9=""/>
<portraet value=""/>
<abenteuerpunkte value="15000"/>
<freieabenteuerpunkte value="6119"/>
<freieabenteuerpunkte value="5632"/>
<gilde name="weiß"/>
</basis>
<eigenschaften>
@ -73,6 +75,13 @@
<auswahl name="Hesinde"/>
</sonderfertigkeit>
<sonderfertigkeit name="Astrale Meditation"/>
<sonderfertigkeit name="Berufsgeheimnis">
<auswahl>
<wahl position="0" value="Ackerbau"/>
<wahl position="1" value="Abrichten"/>
<wahl position="2" value="Knüppel auf den Kopp"/>
</auswahl>
</sonderfertigkeit>
<sonderfertigkeit name="Große Meditation"/>
<sonderfertigkeit name="Kulturkunde">
<kultur name="Mittelreich"/>
@ -90,6 +99,13 @@
<sonderfertigkeit name="Ritualkenntnis: Gildenmagie"/>
<sonderfertigkeit name="Spätweihe Alveranische Gottheit"/>
<sonderfertigkeit name="Stabzauber: Bindung"/>
<sonderfertigkeit name="Wahrer Name: Dschinn">
<auswahl>
<wahl position="0" value="Luft"/>
<wahl position="1" value="1"/>
<wahl position="2" value="Rüdiger"/>
</auswahl>
</sonderfertigkeit>
</sf>
<ereignisse>
<ereignis obj="max GP für Helden: 160" text="EINSTELLUNG" time="1758727716048" version="HS 5.5.3"/>
@ -257,6 +273,59 @@
text="Sonderfertigkeit hinzugefügt" time="1758728180962" version="HS 5.5.3"/>
<ereignis Abenteuerpunkte="-250" Karmaenergie="-1" obj="Liturgie: Göttliche Strafe (V)"
text="Sonderfertigkeit hinzugefügt" time="1758728185936" version="HS 5.5.3"/>
<ereignis kommentar="[BROKENSIG, EDITOREINGEGEBEN]"
obj="Held wurde extern verändert. Änderungskontrolle wieder aktiviert." text="Änderungskontrolle"
time="1758822738691" version="HS 5.5.3"/>
<ereignis Abenteuerpunkte="-4" Alt="1" Info="Gegenseitiges Lehren" Neu="2" obj="Ackerbau"
text="Talent steigern" time="1758827653479" version="HS 5.5.3"/>
<ereignis Abenteuerpunkte="-6" Alt="2" Info="Gegenseitiges Lehren" Neu="3" obj="Ackerbau"
text="Talent steigern" time="1758827653657" version="HS 5.5.3"/>
<ereignis Abenteuerpunkte="-8" Alt="3" Info="Gegenseitiges Lehren" Neu="4" obj="Ackerbau"
text="Talent steigern" time="1758827653813" version="HS 5.5.3"/>
<ereignis Abenteuerpunkte="-11" Alt="4" Info="Gegenseitiges Lehren" Neu="5" obj="Ackerbau"
text="Talent steigern" time="1758827653954" version="HS 5.5.3"/>
<ereignis Abenteuerpunkte="-14" Alt="5" Info="Gegenseitiges Lehren" Neu="6" obj="Ackerbau"
text="Talent steigern" time="1758827654121" version="HS 5.5.3"/>
<ereignis Abenteuerpunkte="-17" Alt="6" Info="Gegenseitiges Lehren" Neu="7" obj="Ackerbau"
text="Talent steigern" time="1758827654690" version="HS 5.5.3"/>
<ereignis Abenteuerpunkte="-11" Alt="4" Info="Gegenseitiges Lehren" Neu="5" obj="Alchimie"
text="Talent steigern" time="1758827656137" version="HS 5.5.3"/>
<ereignis Abenteuerpunkte="-19" Alt="7" Info="Gegenseitiges Lehren" Neu="8" obj="Ackerbau"
text="Talent steigern" time="1758827657421" version="HS 5.5.3"/>
<ereignis Abenteuerpunkte="-22" Alt="8" Info="Gegenseitiges Lehren" Neu="9" obj="Ackerbau"
text="Talent steigern" time="1758827657884" version="HS 5.5.3"/>
<ereignis Abenteuerpunkte="-25" Alt="9" Info="Gegenseitiges Lehren" Neu="10" obj="Ackerbau"
text="Talent steigern" time="1758827658047" version="HS 5.5.3"/>
<ereignis Abenteuerpunkte="-28" Alt="10" Info="Gegenseitiges Lehren" Neu="11" obj="Ackerbau"
text="Talent steigern" time="1758827658217" version="HS 5.5.3"/>
<ereignis Abenteuerpunkte="-32" Alt="11" Info="Gegenseitiges Lehren" Neu="12" obj="Ackerbau"
text="Talent steigern" time="1758827658385" version="HS 5.5.3"/>
<ereignis Abenteuerpunkte="-35" Alt="12" Info="Gegenseitiges Lehren" Neu="13" obj="Ackerbau"
text="Talent steigern" time="1758827658554" version="HS 5.5.3"/>
<ereignis Abenteuerpunkte="-38" Alt="13" Info="Gegenseitiges Lehren" Neu="14" obj="Ackerbau"
text="Talent steigern" time="1758827658729" version="HS 5.5.3"/>
<ereignis Abenteuerpunkte="-41" Alt="14" Info="Gegenseitiges Lehren" Neu="15" obj="Ackerbau"
text="Talent steigern" time="1758827659093" version="HS 5.5.3"/>
<ereignis Abenteuerpunkte="-10" Neu="0" obj="Abrichten" text="Talent aktivieren" time="1758827670806"
version="HS 5.5.3"/>
<ereignis Abenteuerpunkte="-2" Alt="0" Info="Gegenseitiges Lehren" Neu="1" obj="Abrichten"
text="Talent steigern" time="1758827675626" version="HS 5.5.3"/>
<ereignis Abenteuerpunkte="-4" Alt="1" Info="Gegenseitiges Lehren" Neu="2" obj="Abrichten"
text="Talent steigern" time="1758827675769" version="HS 5.5.3"/>
<ereignis Abenteuerpunkte="-6" Alt="2" Info="Gegenseitiges Lehren" Neu="3" obj="Abrichten"
text="Talent steigern" time="1758827675936" version="HS 5.5.3"/>
<ereignis Abenteuerpunkte="-8" Alt="3" Info="Gegenseitiges Lehren" Neu="4" obj="Abrichten"
text="Talent steigern" time="1758827676096" version="HS 5.5.3"/>
<ereignis Abenteuerpunkte="-11" Alt="4" Info="Gegenseitiges Lehren" Neu="5" obj="Abrichten"
text="Talent steigern" time="1758827676247" version="HS 5.5.3"/>
<ereignis Abenteuerpunkte="-14" Alt="5" Info="Gegenseitiges Lehren" Neu="6" obj="Abrichten"
text="Talent steigern" time="1758827676400" version="HS 5.5.3"/>
<ereignis Abenteuerpunkte="-17" Alt="6" Info="Gegenseitiges Lehren" Neu="7" obj="Abrichten"
text="Talent steigern" time="1758827676561" version="HS 5.5.3"/>
<ereignis Abenteuerpunkte="-100" obj="Berufsgeheimnis (Ackerbau; Abrichten; Knüppel auf den Kopp)"
text="Sonderfertigkeit hinzugefügt" time="1758827690491" version="HS 5.5.3"/>
<ereignis Abenteuerpunkte="-4" obj="Wahrer Name: Dschinn (Luft Q1 Rüdiger)"
text="Sonderfertigkeit hinzugefügt" time="1758827726594" version="HS 5.5.3"/>
</ereignisse>
<talentliste>
<talent lernmethode="Gegenseitiges Lehren" name="Armbrust" probe=" (GE/FF/KK)" value="1"/>
@ -320,8 +389,9 @@
value="6"/>
<talent k="16" lernmethode="Gegenseitiges Lehren" name="Lesen/Schreiben Urtulamidya" probe=" (KL/KL/FF)"
value="6"/>
<talent lernmethode="Gegenseitiges Lehren" name="Ackerbau" probe=" (IN/FF/KO)" value="1"/>
<talent lernmethode="Gegenseitiges Lehren" name="Alchimie" probe=" (MU/KL/FF)" value="4"/>
<talent lernmethode="Gegenseitiges Lehren" name="Abrichten" probe=" (MU/IN/CH)" value="7"/>
<talent lernmethode="Gegenseitiges Lehren" name="Ackerbau" probe=" (IN/FF/KO)" value="15"/>
<talent lernmethode="Gegenseitiges Lehren" name="Alchimie" probe=" (MU/KL/FF)" value="5"/>
<talent lernmethode="Gegenseitiges Lehren" name="Heilkunde: Gift" probe=" (MU/KL/IN)" value="4"/>
<talent lernmethode="Gegenseitiges Lehren" name="Heilkunde: Wunden" probe=" (KL/CH/FF)" value="4"/>
<talent lernmethode="Gegenseitiges Lehren" name="Holzbearbeitung" probe=" (KL/FF/KK)" value="0"/>
@ -506,6 +576,7 @@
</gegenstände>
<BoniWaffenlos/>
<kommentare>
<kommentar key="Ortskenntnis (Stadtteil/Kleinstadt)" kommentar="Andergast"/>
<sfInfos dauer="" kosten="" probe="" sf="" sfname="Liturgie: Argelions bannende Hand" wirkung=""/>
<sfInfos dauer="" kosten="" probe="" sf="" sfname="Liturgie: Cereborns Handreichung (Handwerkssegen)"
wirkung=""/>
@ -542,10 +613,10 @@
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<DigestValue>D/lXw741ikWhJ+dIE/eCcUgT5vw=</DigestValue>
<DigestValue>a1RMsniSGUvFk5vUM6faRb5HF7M=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>Yo5RyWxO8N8Z0ReQlPhESaEjbpFUFTANob25mAXlFTH0eCBano63WQ==</SignatureValue>
<SignatureValue>IoH2tMVRNhVL5zF5VrhsiYRdosA0GopNsJMf4tFpYVi5yPW6RhGqNQ==</SignatureValue>
<KeyInfo>
<KeyValue>
<DSAKeyValue>

View File

@ -1,10 +1,10 @@
import {PlayerCharacterDataModel} from "./module/data/character.mjs";
import { SkillSheet } from "./module/sheets/skillSheet.mjs";
import { SpellSheet } from "./module/sheets/spellSheet.mjs";
import { CharacterSheet } from "./module/sheets/characterSheet.mjs";
import { SkillDataModel } from "./module/data/skill.mjs";
import { SpellDataModel } from "./module/data/spell.mjs";
import { Character } from "./module/documents/character.mjs";
import { CharacterSheet } from "./module/sheets/characterSheet.mjs";
Hooks.once("init", () => {

View File

@ -1,8 +1,5 @@
import {SkillDataModel} from "./skill.mjs";
import {SpellDataModel} from "./spell.mjs";
const {
SchemaField, NumberField, StringField, ArrayField, BooleanField, ForeignDocumentField
SchemaField, NumberField, StringField, ArrayField, ForeignDocumentField
} = foundry.data.fields;
export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel {
@ -13,7 +10,7 @@ export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel {
meta: new SchemaField({
spezies: new StringField(),
kultur: new StringField(),
profession: new StringField(),
professions: new ArrayField(new StringField()),
geschlecht: new StringField(),
haarfarbe: new StringField(),
groesse: new NumberField({ required: true, integer: false }),
@ -21,30 +18,120 @@ export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel {
geburtstag: new StringField(),
alter: new NumberField({ required: true, integer: true }),
gewicht: new NumberField({ required: true, integer: true }),
aussehen: new ArrayField(new StringField()),
familie: new ArrayField(new StringField()),
titel: new StringField(),
stand: new StringField(),
}),
attribute: new SchemaField({
mu: new NumberField({ required: true, integer: true }),
kl: new NumberField({ required: true, integer: true }),
in: new NumberField({ required: true, integer: true }),
ch: new NumberField({ required: true, integer: true }),
ff: new NumberField({ required: true, integer: true }),
ge: new NumberField({ required: true, integer: true }),
ko: new NumberField({ required: true, integer: true }),
kk: new NumberField({ required: true, integer: true }),
mr: new NumberField({ required: true, integer: true }),
lep: new NumberField({ required: true, integer: true }),
aup: new NumberField({ required: true, integer: true }),
asp: new NumberField({ required: false, integer: true }),
kap: new NumberField({ required: false, integer: true }),
at: new NumberField({ required: true, integer: true }),
pa: new NumberField({ required: true, integer: true }),
fk: new NumberField({ required: true, integer: true }),
ini: new NumberField({ required: true, integer: true }),
so: new NumberField({ required: true, integer: true }),
mu: new SchemaField({
start: new NumberField({ required: true, integer: true }),
aktuell: new NumberField({ required: true, integer: true }),
mod: new NumberField({ required: true, integer: true }),
}),
kl: new SchemaField({
start: new NumberField({ required: true, integer: true }),
aktuell: new NumberField({ required: true, integer: true }),
mod: new NumberField({ required: true, integer: true }),
}),
in: new SchemaField({
start: new NumberField({ required: true, integer: true }),
aktuell: new NumberField({ required: true, integer: true }),
mod: new NumberField({ required: true, integer: true }),
}),
ch: new SchemaField({
start: new NumberField({ required: true, integer: true }),
aktuell: new NumberField({ required: true, integer: true }),
mod: new NumberField({ required: true, integer: true }),
}),
ff: new SchemaField({
start: new NumberField({ required: true, integer: true }),
aktuell: new NumberField({ required: true, integer: true }),
mod: new NumberField({ required: true, integer: true }),
}),
ge: new SchemaField({
start: new NumberField({ required: true, integer: true }),
aktuell: new NumberField({ required: true, integer: true }),
mod: new NumberField({ required: true, integer: true }),
}),
ko: new SchemaField({
start: new NumberField({ required: true, integer: true }),
aktuell: new NumberField({ required: true, integer: true }),
mod: new NumberField({ required: true, integer: true }),
}),
kk: new SchemaField({
start: new NumberField({ required: true, integer: true }),
aktuell: new NumberField({ required: true, integer: true }),
mod: new NumberField({ required: true, integer: true }),
}),
mr: new SchemaField({
mod: new NumberField({ required: true, integer: true }),
}),
lep: new SchemaField({
mod: new NumberField({ required: true, integer: true }),
}),
aup: new SchemaField({
mod: new NumberField({ required: true, integer: true }),
}),
asp: new SchemaField({
mod: new NumberField({ required: true, integer: true }),
}),
kap: new SchemaField({
mod: new NumberField({ required: true, integer: true }),
}),
at: new SchemaField({
aktuell: new NumberField({ required: true, integer: true }),
mod: new NumberField({ required: true, integer: true }),
}),
pa: new SchemaField({
aktuell: new NumberField({ required: true, integer: true }),
mod: new NumberField({ required: true, integer: true }),
}),
fk: new SchemaField({
aktuell: new NumberField({ required: true, integer: true }),
mod: new NumberField({ required: true, integer: true }),
}),
ini: new SchemaField({
aktuell: new NumberField({ required: true, integer: true }),
mod: new NumberField({ required: true, integer: true }),
}),
so: new SchemaField({
start: new NumberField({ required: true, integer: true }),
aktuell: new NumberField({ required: true, integer: true }),
mod: new NumberField({ required: true, integer: true }),
}),
gilde: new StringField(),
}),
talente: new ArrayField ( new ForeignDocumentField(Item) ),
zauber: new ArrayField ( new ForeignDocumentField(Item) ),
vornachteile: new ArrayField(new SchemaField({
name: new StringField(),
wert: new NumberField({ required: false, integer: true }),
})),
sonderfertigkeiten: new ArrayField(new SchemaField({
name: new StringField(),
auswahlen: new ArrayField(new StringField()),
})),
talente: new ArrayField(new SchemaField({
talent: new ForeignDocumentField(Item),
taw: new NumberField({integer: true, required: true}),
})
),
zauber: new ArrayField(new SchemaField({
talent: new ForeignDocumentField(Item),
zfw: new NumberField({integer: true, required: true}),
})),
liturgien: new ArrayField(new SchemaField({
name: new StringField(),
})),
kampfwerte: new ArrayField(new SchemaField({
name: new StringField(),
at: new NumberField({ required: true, integer: true }),
pa: new NumberField({ required: true, integer: true }),
})),
notizen: new ArrayField(new SchemaField({
key: new StringField(),
notiz: new StringField(),
})),
}
}
@ -52,4 +139,49 @@ export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel {
super._initialize(options);
}
_onCreate(data, options, userId) {
// prepare base talents
const talentsByName = [
"Athletik", "Klettern", "Körperbeherrschung", "Schleichen", "Schwimmen", "Selbstbeherrschung", "Sich Verstecken", "Singen", "Sinnenschärfe", "Tanzen", "Zechen",
"Menschenkenntnis", "Überreden",
"Fährtensuchen", "Orientierung", "Wildnisleben",
"Götter/Kulte", "Rechnen", "Sagen/Legenden",
"Heilkunde: Wunden", "Holzbearbeitung", "Kochen", "Lederverarbeitung", "Malen/Zeichnen", "Schneidern"
]
const talente = []
talentsByName.forEach( talentName => {
const talent = game.items.getName(talentName);
console.log(talent);
if (talent) {
talente.push({
taw: 0,
talent
})
} else {
console.error(`${talentName} not found in items`)
}
})
// push base talents
game.actors.getName(data.name).update({system: {talente}})
const startEigenschaften = {
"mu": 10,
"kl": 10,
"in": 10,
"ch": 10,
"ff": 10,
"ge": 10,
"ko": 10,
"kk": 10,
}
game.actors.getName(data.name).update({system: {attribute: startEigenschaften}})
super._onCreate(data, options, userId);
}
}

View File

@ -1,4 +1,16 @@
import {importCharacter} from "../xml-import/xml-import.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()
}
/**
* Augment the basic Item data model with additional dynamic data.
*/
@ -7,4 +19,21 @@ export class Character extends Actor {
this.prepareEmbeddedDocuments();
}
getRollData() {
const data = super.getRollData();
if (this.type !== 'character') return;
// Copy the ability scores to the top level, so that rolls can use
// formulas like `@str.mod + 4`.
if (data.attribute) {
for (let [k, v] of Object.entries(data.attribute)) {
data[k] = foundry.utils.deepClone(v);
}
}
console.log(data);
return data;
}
}

View File

@ -1,6 +1,5 @@
import { BaseItem } from "./base-item.mjs";
export class Skill extends BaseItem {
export class Skill extends Item {
/**
* Augment the basic Item data model with additional dynamic data.
*/

View File

@ -1,6 +1,4 @@
import { BaseItem } from "./base-item.mjs";
export class Spell extends BaseItem {
export class Spell extends Item {
/**
* Augment the basic Item data model with additional dynamic data.
*/

View File

@ -34,14 +34,63 @@ export class CharacterSheet extends ActorSheet {
// Add the actor's data to context.data for easier access, as well as flags.
context.system = actorData.system;
context.flags = actorData.flags;
context.attributes = [
{
eigenschaft: "mu",
name: "Mut",
wert: actorData.system.attribute.mu.aktuell ?? 0,
},
{
eigenschaft: "kl",
name: "Klugheit",
wert: actorData.system.attribute.kl.aktuell ?? 0,
},
{
eigenschaft: "in",
name: "Intuition",
wert: actorData.system.attribute.in.aktuell ?? 0,
},
{
eigenschaft: "ch",
name: "Charisma",
wert: actorData.system.attribute.ch.aktuell ?? 0,
},
{
eigenschaft: "ff",
name: "Fingerfertigkeit",
wert: actorData.system.attribute.ff.aktuell ?? 0,
},
{
eigenschaft: "ge",
name: "Geschicklichkeit",
wert: actorData.system.attribute.ge.aktuell ?? 0,
},
{
eigenschaft: "ko",
name: "Konstitution",
wert: actorData.system.attribute.ko.aktuell ?? 0,
},
{
eigenschaft: "kk",
name: "Körperkraft",
wert: actorData.system.attribute.kk.aktuell ?? 0,
},
];
context.skills = [];
if ( context.system.talente?.length >= 0) {
context.system.talente.forEach(talent => {
const tempTalent = talent();
console.log(tempTalent.system.probe);
console.log(talent);
const taw = talent.taw;
const talentObjekt = game.items.get(talent.talent);
const eigenschaften = Object.values(talentObjekt.system.probe);
context.skills.push({
talentName: tempTalent.name,
probe: `ROLLDATA(${Object.values(tempTalent.system.probe).join("/")})`
talentName: talentObjekt.name,
taw: taw,
rollEigenschaft1: this.prepareEigenschaftRoll(actorData, eigenschaften[0]),
rollEigenschaft2: this.prepareEigenschaftRoll(actorData, eigenschaften[1]),
rollEigenschaft3: this.prepareEigenschaftRoll(actorData, eigenschaften[2]),
probe: `(${eigenschaften.join("/")})`
});
})
@ -51,9 +100,97 @@ export class CharacterSheet extends ActorSheet {
return context;
}
prepareEigenschaftRoll(actorData, name) {
return actorData.system.attribute[name.toLowerCase()]
}
async _onTalentRoll(event) {
event.preventDefault();
const dataset = event.currentTarget.dataset;
console.log(dataset)
if (dataset.rolleigenschaft1) {
let roll1 = new Roll("3d20", this.actor.getRollData());
let evaluated1 = (await roll1.evaluate())
const dsaDieRollEvaluated = this._evaluateRoll(evaluated1.terms[0].results, {
taw: dataset.taw,
werte: [dataset.rolleigenschaft1, dataset.rolleigenschaft2, dataset.rolleigenschaft3],
})
if (dsaDieRollEvaluated.tap >= 0) { // erfolg
evaluated1.toMessage({
speaker: ChatMessage.getSpeaker({actor: this.actor}),
flavor: ` ${dsaDieRollEvaluated.meisterlich?'Meisterlich geschafft':'Geschafft'} mit ${dsaDieRollEvaluated.tap} Punkten übrig`,
rollMode: game.settings.get('core', 'rollMode'),
})
} else { // misserfolg
evaluated1.toMessage({
speaker: ChatMessage.getSpeaker({actor: this.actor}),
flavor: ` ${dsaDieRollEvaluated.meisterlich?'Gepatzt':''} mit ${Math.abs(dsaDieRollEvaluated.tap)} Punkten daneben`,
rollMode: game.settings.get('core', '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,
}
}
_onAttributeRoll(event) {
event.preventDefault();
const dataset = event.currentTarget.dataset;
if (dataset.roll) {
let label = dataset.label ? `[Attribut] ${dataset.label}` : '';
let roll = new Roll(dataset.roll, this.actor.getRollData());
roll.toMessage({
speaker: ChatMessage.getSpeaker({ actor: this.actor }),
flavor: label,
rollMode: game.settings.get('core', 'rollMode'),
});
return roll;
}
}
activateListeners(html) {
super.activateListeners(html);
html.on('click', '.attribut.rollable', (evt) => {
console.log(evt);
this._onAttributeRoll(evt);
});
html.on('click', '.talent.rollable', (evt) => {
console.log(evt);
this._onTalentRoll(evt);
});
// Everything below here is only needed if the sheet is editable
if (!this.isEditable) return;

View File

@ -0,0 +1,340 @@
let months = [
"Praios",
"Rondra",
"Efferd",
"Travia",
"Boron",
"Hesinde",
"Firun",
"Tsa",
"Phex",
"Peraine",
"Ingerimm",
"Rahja",
"Namenloser Tag"
]
/**
* Imports a character from a file created in the tool Helden-Software
* @param actorId the actor-id of the character
* @param file the file from which the character should be imported
*/
export async function importCharacter(actorId, file) {
let actor = game.actors.get(actorId)
let xmlString = await parseFileToString(file)
let domParser = new DOMParser()
let dom = domParser.parseFromString(xmlString, 'application/xml')
let rawJson = getJsonFromXML(dom)
let characterJson = mapRawJson(rawJson)
actor.update(characterJson)
}
/**
*
* @param dom the XML-Dom from which the json should be extracted
* @returns {{}} the json parsed from the xml
*/
function getJsonFromXML(dom) {
let children = [...dom.children];
// initializing object to be returned.
let jsonResult = {};
let attributes = dom.attributes ? dom.attributes : []
for (let attribute of attributes) {
jsonResult[attribute.name] = attribute.value
}
if (children.length) {
for (let child of children) {
// checking is child has siblings of same name.
let childIsArray = children.filter(eachChild => eachChild.nodeName === child.nodeName).length > 1;
// if child is array, save the values as array, else as strings.
if (childIsArray) {
if (jsonResult[child.nodeName] === undefined) {
jsonResult[child.nodeName] = [getJsonFromXML(child)];
} else {
jsonResult[child.nodeName].push(getJsonFromXML(child));
}
} else {
jsonResult[child.nodeName] = getJsonFromXML(child);
}
}
}
return jsonResult;
}
/**
* gets the text content of a file
* @param file the file with the desired content
* @returns {Promise<String>} a promise that returns the string contents of the file
*/
async function parseFileToString(file) {
return new Promise((resolve, reject) => {
let reader = new FileReader()
reader.readAsText(file, "utf-8")
reader.onload = event => {
resolve(event.target.result)
}
reader.onerror = event => {
reject(event)
}
})
}
/**
*Calculates a Birthdate String in the Calendar of Bosparans Fall
* @param json json with the day, the month and the year of the birthday
* @returns {string} a string in the format of "DD.MMMM.YYYY BF"
*/
function calculateBirthdate(json) {
let day = json.gbtag
let month = months[parseInt(json.gbmonat) - 1]
let year = json.gbjahr
return `${day}. ${month} ${year} BF`
}
/**
* parses a json into a fitting character-json
* @param rawJson the json parsed from the Helden-Software XML
* @returns {{}} a json representation of the character
*/
function mapRawJson(rawJson) {
let json = {}
let held = rawJson.helden.held;
json.name = held.name
json.meta = {}
json.meta.spezies = held.basis.rasse.string
json.meta.kultur = held.basis.kultur.string
let professions = []
for (let profession in held.basis.ausbildungen.ausbildung) {
profession = held.basis.ausbildungen.ausbildung[profession]
if (profession.tarnidentitaet) {
professions = [profession.tarnidentitaet]
break;
}
let professionString = profession.string
professions.push(professionString)
}
json.meta.professions = professions
json.meta.geschlecht = held.basis.geschlecht.name
json.meta.haarfarbe = held.basis.rasse.aussehen.haarfarbe
json.meta.groesse = held.basis.rasse.groesse.value
json.meta.augenfarbe = held.basis.rasse.aussehen.augenfarbe
json.meta.geburtstag = calculateBirthdate(held.basis.rasse.aussehen)
json.meta.alter = held.basis.rasse.aussehen.alter
json.meta.gewicht = held.basis.rasse.groesse.gewicht
json.meta.aussehen = [
held.basis.rasse.aussehen.aussehentext0,
held.basis.rasse.aussehen.aussehentext1,
held.basis.rasse.aussehen.aussehentext2,
held.basis.rasse.aussehen.aussehentext3,
]
json.meta.familie = [
held.basis.rasse.aussehen.familietext0,
held.basis.rasse.aussehen.familietext1,
held.basis.rasse.aussehen.familietext2,
held.basis.rasse.aussehen.familietext3,
held.basis.rasse.aussehen.familietext4,
held.basis.rasse.aussehen.familietext5,
]
json.meta.titel = held.basis.rasse.aussehen.titel
json.meta.stand = held.basis.rasse.aussehen.stand
let attributes = held.eigenschaften.eigenschaft
json.attribute = {}
if (held.basis.gilde) {
json.attribute.gilde = held.basis.gilde.name
}
json.attribute.mu = getAttributeJson(attributes, "Mut")
json.attribute.kl = getAttributeJson(attributes, "Klugheit")
json.attribute.in = getAttributeJson(attributes, "Intuition")
json.attribute.ch = getAttributeJson(attributes, "Charisma")
json.attribute.ff = getAttributeJson(attributes, "Fingerfertigkeit")
json.attribute.ge = getAttributeJson(attributes, "Gewandtheit")
json.attribute.ko = getAttributeJson(attributes, "Konstitution")
json.attribute.kk = getAttributeJson(attributes, "Körperkraft")
json.attribute.mr = {
mod: filterAttribute(attributes,"Magieresistenz").mod
}
json.attribute.lep = {
mod: filterAttribute(attributes,"Lebensenergie").mod
}
json.attribute.aup = {
mod: filterAttribute(attributes,"Ausdauer").mod
}
json.attribute.asp = {
mod: filterAttribute(attributes,"Astralenergie").mod
}
json.attribute.kap = {
mod: filterAttribute(attributes,"Karmaenergie").mod
}
let attribute = filterAttribute(attributes,"Karmaenergie")
json.attribute.at = {
mod: attribute.mod,
aktuell: attribute.value
}
attribute = filterAttribute(attributes,"at")
json.attribute.pa = {
mod: attribute.mod,
aktuell: attribute.value
}
attribute = filterAttribute(attributes,"pa")
json.attribute.at = {
mod: attribute.mod,
aktuell: attribute.value
}
attribute = filterAttribute(attributes,"fk")
json.attribute.fk = {
mod: attribute.mod,
aktuell: attribute.value
}
attribute = filterAttribute(attributes,"ini")
json.attribute.ini = {
mod: attribute.mod,
aktuell: attribute.value
}
json.attribute.so = getAttributeJson(attributes, "Sozialstatus")
let benefits = []
for (let benefit in held.vt.vorteil) {
benefit = held.vt.vorteil[benefit]
let benefitJson = {
name: benefit.name,
}
if (benefit.value !== undefined) {
benefitJson.wert = benefit.value
}
benefits.push(benefitJson)
}
json.vornachteile = benefits
let specialAbilities = []
let liturgies = []
for (let specialAbility in held.sf.sonderfertigkeit) {
specialAbility = held.sf.sonderfertigkeit[specialAbility]
if (specialAbility.name.startsWith("Liturgie:")) {
liturgies.push({
name: specialAbility.name.replace("Liturgie:", "").trim(),
})
} else {
let specialAbilityJson= {
name: specialAbility.name,
auswahlen: []
}
let fields = Object.keys(specialAbility)
if (fields.length > 1) {
for (let field in fields) {
field = fields[field]
if (field !== "name") {
let choices = specialAbility[field]
if (choices.hasOwnProperty("name")) {
specialAbilityJson.auswahlen.push(choices.name)
} else {
for (let choice in choices.wahl) {
choice = choices.wahl[choice]
specialAbilityJson.auswahlen.push(choice.value)
}
}
}
}
}
specialAbilities.push(specialAbilityJson)
}
}
json.sonderfertigkeiten = specialAbilities
json.liturgien = liturgies
let talents = []
for (let talent in held.talentliste.talent) {
talent = held.talentliste.talent[talent]
let talentItem = game.items.getName(talent.name)
if (talentItem) {
let talentJson = {
talent: talentItem,
taw: talent.value,
}
talents.push(talentJson)
}
}
json.talente = talents
let spells = []
/*for (let spell in held.zauberliste.zauber) {
spell = held.zauberliste.zauber[spell]
spells.push({
name: spell.name,
rep: spell.repraesentation,
hauszauber: spell.hauszauber === "true",
zfw: spell.value,
anmerkungen: spell.zauberkommentar,
komplexitaet: spell.k,
})
}*/
json.zauber = spells
let combatValues = []
for (let combatValue in held.kampf.kampfwerte) {
combatValue = held.kampf.kampfwerte[combatValue]
combatValues.push({
name: combatValue.name,
at: combatValue.attacke.value,
pa: combatValue.parade.value,
})
}
json.kampfwerte = combatValues
let notes = []
for (let note in held.kommentare) {
note = held.kommentare[note]
if (note.hasOwnProperty("key")) {
notes.push({
key: note.key,
notiz: note.kommentar,
})
}
}
json.notizen = notes
return {
name: held.name,
system: json,
}
}
/**
*
* @param attributes an array with the attributes
* @param name the name of the chosen attribute
* @returns {{}} a representation of the chosen Attribute
*/
function getAttributeJson(attributes, name) {
let attribute = filterAttribute(attributes, name)
return {
start: parseInt(attribute.startwert),
aktuell: parseInt(attribute.value),
mod: parseInt(attribute.mod),
}
}
/**
* filters a given attribute array based on the name of an attribute
* @param attributes the attribute array
* @param name the name of the desired attribute
* @returns {{}} the json of the desired attribute
*/
function filterAttribute(attributes, name) {
return attributes.filter(attribute => attribute.name === name)[0]
}
Hooks.on("getActorContextOptions", (application, menuItems) => {
menuItems.push({
name: "Import from XML",
icon: '<i class="fas fa-file"></i>',
callback: (li) => {
const actorId = li.getAttribute("data-entry-id")
const actor = game.actors.get(actorId)
actor.import()
}
})
})

View File

@ -2,7 +2,7 @@
"_id": "o1nYjhmMP0Zzlcw6",
"_key": "!items!o1nYjhmMP0Zzlcw6",
"type": "Skill",
"name": "Sich verstecken",
"name": "Sich Verstecken",
"system": {
"gruppe": "Körperlich",
"probe": [

View File

@ -6,6 +6,18 @@
<img class="profile-img" src="{{actor.img}}" data-edit="img" title="{{actor.name}}" height="100" width="100"/>
<div class="header-fields">
<h1 class="charname"><input name="name" type="text" value="{{actor.name}}" placeholder="Name"/></h1>
<div class="attribute">
{{#each attributes}}
<button class="attribut rollable" data-label="{{this.name}}" data-roll="1d20cs<=@{{this.eigenschaft}}">
<span class="attribut-wert">
{{this.wert}}
</span>
<span class="attribut-name">
{{this.name}}
</span>
</button>
{{/each}}
</div>
</div>
</header>
@ -34,9 +46,10 @@
<div class="tab skills" data-group="primary" data-tab="skills">
<ul>
{{#each skills}}
<li><div>
<li><div class="talent rollable" data-taw="{{this.taw}}" data-rollEigenschaft1="{{this.rollEigenschaft1}}" data-rollEigenschaft2="{{this.rollEigenschaft2}}" data-rollEigenschaft3="{{this.rollEigenschaft3}}">
<b>{{this.talentName}}</b>
{{this.probe}}
TAW: {{this.taw}}
</div></li>
{{/each}}
</ul>

View File

@ -0,0 +1,10 @@
<form class="flexcol" autocomplete="off">
<header class="sheet-header">
<div class="header-fields">
<h1 class="charname">{{actor.name}}</h1>
</div>
</header>
<section class="sheet-body">
<textarea readonly>{{formatCharacter actor}}</textarea>
</section>
</form>