From 905b0eb405231245966440050c6760671355e95e Mon Sep 17 00:00:00 2001 From: macniel Date: Tue, 21 Oct 2025 01:17:41 +0200 Subject: [PATCH] prepares xml-import to no longer append items, but instead cleans them out before adding. This will be controllable via dialog --- gulpfile.mjs | 5 + src/main.mjs | 40 ++-- src/module/data/culture.mjs | 15 ++ src/module/data/species.mjs | 15 ++ src/module/documents/character.mjs | 2 +- src/module/documents/culture.mjs | 9 + src/module/documents/species.mjs | 9 + src/module/sheets/CultureSheet.mjs | 54 +++++ src/module/sheets/ProfessionSheet.mjs | 55 +++++ src/module/sheets/SpeciesSheet.mjs | 55 +++++ src/module/sheets/actions/action-manager.mjs | 5 - src/module/sheets/characterSheet.mjs | 33 ++- src/module/xml-import/xml-import.mjs | 214 ++++++++++++++---- .../helden.model.kultur.Mittelreich.json | 6 + .../helden.model.rasse.Mittellaender.json | 7 + .../_source/zauber/MemorabiaFalsafir.json | 2 +- src/packs/_source/zauber/Windstille.json | 14 +- .../zauber/XenographusSchriftenkunde.json | 2 +- src/style/organisms/_culture-sheet.scss | 26 +++ src/style/organisms/_profession-sheet.scss | 26 +++ src/style/organisms/_species-sheet.scss | 26 +++ src/style/styles.scss | 4 + src/system.json | 37 +++ src/templates/actor/character/main-sheet.hbs | 8 +- src/templates/item/culture-sheet.hbs | 21 ++ src/templates/item/profession-sheet.hbs | 21 ++ src/templates/item/species-sheet.hbs | 25 ++ 27 files changed, 658 insertions(+), 78 deletions(-) create mode 100644 src/module/data/culture.mjs create mode 100644 src/module/data/species.mjs create mode 100644 src/module/documents/culture.mjs create mode 100644 src/module/documents/species.mjs create mode 100644 src/module/sheets/CultureSheet.mjs create mode 100644 src/module/sheets/ProfessionSheet.mjs create mode 100644 src/module/sheets/SpeciesSheet.mjs create mode 100644 src/packs/_source/kulturen/helden.model.kultur.Mittelreich.json create mode 100644 src/packs/_source/spezien/helden.model.rasse.Mittellaender.json create mode 100644 src/style/organisms/_culture-sheet.scss create mode 100644 src/style/organisms/_profession-sheet.scss create mode 100644 src/style/organisms/_species-sheet.scss create mode 100644 src/templates/item/culture-sheet.hbs create mode 100644 src/templates/item/profession-sheet.hbs create mode 100644 src/templates/item/species-sheet.hbs diff --git a/gulpfile.mjs b/gulpfile.mjs index 0c1c432d..d7277532 100644 --- a/gulpfile.mjs +++ b/gulpfile.mjs @@ -43,6 +43,7 @@ const convert = function (from, to, ofType) { mkdirSync(DEST) readdirSync(SOURCE).forEach(file => { + console.log(file) let originalSource = JSON.parse(readFileSync(join(SOURCE, file), {encoding: "utf8"})); let id = randomID(); @@ -104,6 +105,10 @@ async function prepareDB() { convert("./src/packs/_source/liturgien-und-segnungen", "./src/packs/__source/liturgien", "Liturgy"); convert("./src/packs/_source/wunden", "./src/packs/__source/wunden", "ActiveEffect"); + convert("./src/packs/_source/kulturen", "./src/packs/__source/kulturen", "Culture"); + convert("./src/packs/_source/spezien", "./src/packs/__source/spezien", "Species"); + convert("./src/packs/_source/professionen", "./src/packs/__source/professionen", "Profession"); + } catch (err) { console.error(err); } diff --git a/src/main.mjs b/src/main.mjs index d4b0b9d7..9776ffe3 100644 --- a/src/main.mjs +++ b/src/main.mjs @@ -22,6 +22,11 @@ import {ActiveEffectSheet} from "./module/sheets/ActiveEffectSheet.mjs"; import {ActiveEffectDataModel} from "./module/data/activeeffect.mjs"; import {Trefferzone, Wunde, Zonenruestung, Zonenwunde} from "./module/data/Trefferzone.js"; import {ProfessionDataModel} from "./module/data/profession.mjs"; +import {SpeciesDataModel} from "./module/data/species.mjs"; +import {CultureDataModel} from "./module/data/culture.mjs"; +import {CultureSheet} from "./module/sheets/CultureSheet.mjs"; +import {SpeciesSheet} from "./module/sheets/SpeciesSheet.mjs"; +import {ProfessionSheet} from "./module/sheets/ProfessionSheet.mjs"; async function preloadHandlebarsTemplates() { return foundry.applications.handlebars.loadTemplates([ @@ -71,6 +76,8 @@ Hooks.once("init", () => { SpecialAbility: SpecialAbilityDataModel, ActiveEffect: ActiveEffectDataModel, Profession: ProfessionDataModel, + Spezies: SpeciesDataModel, + Kultur: CultureDataModel, } CONFIG.Combat.initiative = { @@ -95,8 +102,6 @@ Hooks.once("init", () => { makeDefault: true, label: 'DSA41.GroupLabel.Item' }) - - // Register sheet application classes foundry.documents.collections.Items.registerSheet('dsa41.skill', SkillSheet, { types: ["Skill"], makeDefault: true, @@ -132,6 +137,21 @@ Hooks.once("init", () => { makeDefault: true, label: 'DSA41.ActiveEffectLabels.ActiveEffect' }) + foundry.documents.collections.Items.registerSheet('dsa41.culture', CultureSheet, { + types: ['Culture'], + makeDefault: true, + label: 'DSA41.CultureLabels.Culture' + }) + foundry.documents.collections.Items.registerSheet('dsa41.spezien', SpeciesSheet, { + types: ['Species'], + makeDefault: true, + label: 'DSA41.SpeciesLabels.Species' + }) + foundry.documents.collections.Items.registerSheet('dsa41.profession', ProfessionSheet, { + types: ['Profession'], + makeDefault: true, + label: 'DSA41.ProfessionLabels.Profession' + }) game.settings.register('DSA_4-1', 'optional_colorfuldice', { name: "Optional: Farbige Würfel nach Paramanthus", @@ -144,8 +164,6 @@ Hooks.once("init", () => { }, requiresReload: false }) - - game.settings.register('DSA_4-1', 'optional_trefferzonen', { name: "Optional: Trefferzonen", hint: "Ersetzt das Wundensystem aus dem BRW durch das Trefferzonensystem aus WdH", @@ -157,7 +175,6 @@ Hooks.once("init", () => { }, requiresReload: true }) - game.settings.register('DSA_4-1', 'optional_ruestungzonen', { name: "Optional: Zonenrüstung", hint: "Ersetzt das Rüstungssystem aus dem BRW durch das Zonenrüstungssystem aus WdH", @@ -169,7 +186,6 @@ Hooks.once("init", () => { }, requiresReload: true }) - game.settings.register('DSA_4-1', 'optional_ausdauer', { name: "Optional: Ausdauerregeln", hint: "Aktiviert Regeln für das Spiel mit Ausdauer", @@ -181,7 +197,6 @@ Hooks.once("init", () => { }, requiresReload: true }) - game.settings.register('DSA_4-1', 'optional_distanzklassen', { name: "Optional: Distanzklassen", hint: "Aktiviert Regeln für das Spiel mit Distanzklassen", @@ -197,17 +212,6 @@ Hooks.once("init", () => { return preloadHandlebarsTemplates(); }) -Hooks.on('dropActorSheetData', (actor, sheet, data) => { - /*if (data.uuid) { - if (actor.type === "character") { - return true - } else { - return GroupSheet.onDroppedData(actor, sheet, data); - } - }*/ - return true -}) - Hooks.once("ready", async function () { // Wait to register hotbar drop hook on ready so that modules could register earlier if they want to Hooks.on("hotbarDrop", (bar, data, slot) => { diff --git a/src/module/data/culture.mjs b/src/module/data/culture.mjs new file mode 100644 index 00000000..ac5f2cc4 --- /dev/null +++ b/src/module/data/culture.mjs @@ -0,0 +1,15 @@ +import BaseItem from "./base-item.mjs"; + +const {BooleanField, StringField, HTMLField} = foundry.data.fields; + +export class CultureDataModel extends BaseItem { + + static defineSchema() { + return { + description: new HTMLField(), + variant: new StringField(), + feminineDemonym: new StringField(), + masculineDemonym: new StringField() + } + } +} \ No newline at end of file diff --git a/src/module/data/species.mjs b/src/module/data/species.mjs new file mode 100644 index 00000000..4b487b7d --- /dev/null +++ b/src/module/data/species.mjs @@ -0,0 +1,15 @@ +import BaseItem from "./base-item.mjs"; + +const {BooleanField, NumberField, StringField, HTMLField} = foundry.data.fields; + +export class SpeciesDataModel extends BaseItem { + + static defineSchema() { + return { + description: new HTMLField(), + baseSpeed: new NumberField({required: true, initial: 6, integer: true}), + feminineDemonym: new StringField(), + masculineDemonym: new StringField(), + } + } +} \ No newline at end of file diff --git a/src/module/documents/character.mjs b/src/module/documents/character.mjs index da36c15e..00bb83db 100644 --- a/src/module/documents/character.mjs +++ b/src/module/documents/character.mjs @@ -92,7 +92,7 @@ export class Character extends Actor { 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 species + systemData.gs.aktuell = systemData.gs.basis + (systemData.gs.mod ?? 0); // TOOD: get GS from spezien if (game.settings.get("DSA_4-1", "optional_ruestungzonen")) { diff --git a/src/module/documents/culture.mjs b/src/module/documents/culture.mjs new file mode 100644 index 00000000..98da9ab2 --- /dev/null +++ b/src/module/documents/culture.mjs @@ -0,0 +1,9 @@ +export class Culture extends Item { + /** + * Augment the basic Item data model with additional dynamic data. + */ + prepareData() { + super.prepareData(); + } + +} diff --git a/src/module/documents/species.mjs b/src/module/documents/species.mjs new file mode 100644 index 00000000..f1e0f707 --- /dev/null +++ b/src/module/documents/species.mjs @@ -0,0 +1,9 @@ +export class Species extends Item { + /** + * Augment the basic Item data model with additional dynamic data. + */ + prepareData() { + super.prepareData(); + } + +} diff --git a/src/module/sheets/CultureSheet.mjs b/src/module/sheets/CultureSheet.mjs new file mode 100644 index 00000000..abd78fda --- /dev/null +++ b/src/module/sheets/CultureSheet.mjs @@ -0,0 +1,54 @@ +const {DocumentSheetV2, HandlebarsApplicationMixin} = foundry.applications.api + +export class CultureSheet extends HandlebarsApplicationMixin(DocumentSheetV2) { + + /** @inheritDoc */ + static DEFAULT_OPTIONS = { + position: {width: 520, height: 480}, + classes: ['dsa41', 'sheet', 'item', 'culture'], + tag: 'form', + form: { + submitOnChange: true, + closeOnSubmit: false, + handler: CultureSheet.#onSubmitForm + }, + actions: { + editImage: DocumentSheetV2.DEFAULT_OPTIONS.actions.editImage, + } + } + + /** @inheritDoc */ + static PARTS = { + form: { + template: `systems/DSA_4-1/templates/item/culture-sheet.hbs` + }, + } + + /** + * Handle form submission + * @this {CultureSheet} + * @param {SubmitEvent} event + * @param {HTMLFormElement} form + * @param {FormDataExtended} formData + */ + static async #onSubmitForm(event, form, formData) { + event.preventDefault() + + await this.document.update(formData.object) // Note: formData.object + } + + /** @override */ + async _prepareContext(options) { + + const context = await super._prepareContext(options) + context.system = context.document.system + + context.name = context.document.name + context.img = context.document.img + context.description = context.document.system.description + context.masculineDemonym = context.document.system.masculineDemonym + context.feminineDemonym = context.document.system.feminineDemonym + + return context + } +} \ No newline at end of file diff --git a/src/module/sheets/ProfessionSheet.mjs b/src/module/sheets/ProfessionSheet.mjs new file mode 100644 index 00000000..dbaa2465 --- /dev/null +++ b/src/module/sheets/ProfessionSheet.mjs @@ -0,0 +1,55 @@ +const {DocumentSheetV2, HandlebarsApplicationMixin} = foundry.applications.api + +export class ProfessionSheet extends HandlebarsApplicationMixin(DocumentSheetV2) { + + /** @inheritDoc */ + static DEFAULT_OPTIONS = { + position: {width: 520, height: 480}, + classes: ['dsa41', 'sheet', 'item', 'species'], + tag: 'form', + form: { + submitOnChange: true, + closeOnSubmit: false, + handler: ProfessionSheet.#onSubmitForm + }, + actions: { + editImage: DocumentSheetV2.DEFAULT_OPTIONS.actions.editImage, + } + } + + /** @inheritDoc */ + static PARTS = { + form: { + template: `systems/DSA_4-1/templates/item/profession-sheet.hbs` + }, + } + + /** + * Handle form submission + * @this {ProfessionSheet} + * @param {SubmitEvent} event + * @param {HTMLFormElement} form + * @param {FormDataExtended} formData + */ + static async #onSubmitForm(event, form, formData) { + event.preventDefault() + + await this.document.update(formData.object) // Note: formData.object + } + + /** @override */ + async _prepareContext(options) { + + const context = await super._prepareContext(options) + context.system = context.document.system + + context.name = context.document.name + context.img = context.document.img + context.description = context.document.system.description + context.alias = context.document.system.alias + context.isOwner = context.document.owner + context.revealed = context.document.system.revealed + + return context + } +} \ No newline at end of file diff --git a/src/module/sheets/SpeciesSheet.mjs b/src/module/sheets/SpeciesSheet.mjs new file mode 100644 index 00000000..61c2999a --- /dev/null +++ b/src/module/sheets/SpeciesSheet.mjs @@ -0,0 +1,55 @@ +const {DocumentSheetV2, HandlebarsApplicationMixin} = foundry.applications.api + +export class SpeciesSheet extends HandlebarsApplicationMixin(DocumentSheetV2) { + + /** @inheritDoc */ + static DEFAULT_OPTIONS = { + position: {width: 520, height: 480}, + classes: ['dsa41', 'sheet', 'item', 'species'], + tag: 'form', + form: { + submitOnChange: true, + closeOnSubmit: false, + handler: SpeciesSheet.#onSubmitForm + }, + actions: { + editImage: DocumentSheetV2.DEFAULT_OPTIONS.actions.editImage, + } + } + + /** @inheritDoc */ + static PARTS = { + form: { + template: `systems/DSA_4-1/templates/item/species-sheet.hbs` + }, + } + + /** + * Handle form submission + * @this {SpeciesSheet} + * @param {SubmitEvent} event + * @param {HTMLFormElement} form + * @param {FormDataExtended} formData + */ + static async #onSubmitForm(event, form, formData) { + event.preventDefault() + + await this.document.update(formData.object) // Note: formData.object + } + + /** @override */ + async _prepareContext(options) { + + const context = await super._prepareContext(options) + context.system = context.document.system + + context.name = context.document.name + context.img = context.document.img + context.description = context.document.system.description + context.baseSpeed = context.document.baseSpeed + context.masculineDemonym = context.document.system.masculineDemonym + context.feminineDemonym = context.document.system.feminineDemonym + + return context + } +} \ No newline at end of file diff --git a/src/module/sheets/actions/action-manager.mjs b/src/module/sheets/actions/action-manager.mjs index 3e1aee6e..05205b5e 100644 --- a/src/module/sheets/actions/action-manager.mjs +++ b/src/module/sheets/actions/action-manager.mjs @@ -211,20 +211,17 @@ export class ActionManager { #hatWaffeinHand() { const item = this.actor.findEquipmentOnSlot("links") ?? this.actor.findEquipmentOnSlot("rechts") - console.log(item) return item } #hatMunition() { const item = this.actor.findEquipmentOnSlot("munition") const weapon = this.actor.findEquipmentOnSlot("fernkampf") - console.log(item?.system.quantity, weapon) return item } #hatFernkampfWaffeinHand(art) { const item = this.actor.findEquipmentOnSlot("fernkampf") - console.log(item) return item } @@ -238,8 +235,6 @@ export class ActionManager { evaluate() { let actionArray = [...this.#freeActions, ...this.#regularActions, ...this.#continuingActions] - - return actionArray.filter(action => action.eval()); } diff --git a/src/module/sheets/characterSheet.mjs b/src/module/sheets/characterSheet.mjs index 1075b200..d0b6e6ec 100644 --- a/src/module/sheets/characterSheet.mjs +++ b/src/module/sheets/characterSheet.mjs @@ -34,6 +34,9 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { roll: CharacterSheet.#dieRoll, editImage: ActorSheetV2.DEFAULT_OPTIONS.actions.editImage, openEmbeddedDocument: CharacterSheet.#openEmbeddedDocument, + openCultureDocument: CharacterSheet.#openCultureDocument, + openSpeciesDocument: CharacterSheet.#openSpeciesDocument, + } } @@ -123,11 +126,19 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { * @param {MouseEvent} event */ static #openEmbeddedDocument(event) { - const dataset = event.target.parentElement.dataset + const dataset = event.target.dataset const id = dataset.itemId ?? dataset.id this.document.items.get(id).sheet.render(true) } + static #openCultureDocument() { + this.document.itemTypes["Culture"]?.[0]?.sheet.render(true) + } + + static #openSpeciesDocument() { + this.document.itemTypes["Species"]?.[0]?.sheet.render(true) + } + /** * Handle form submission * @this {AdvantageSheet} @@ -183,6 +194,26 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { } }) + context.spezies = "" + if (actorData.itemTypes["Species"]?.[0]) { + const speciesData = actorData.itemTypes["Species"][0] + if (actorData.system.geschlecht === "männlich") { + context.spezies = speciesData.system.masculineDemonym + } else { + context.spezies = speciesData.system.feminineDemonym + } + } + + context.kultur = "" + if (actorData.itemTypes["Culture"]?.[0]) { + const cultureData = actorData.itemTypes["Culture"][0] + if (actorData.system.geschlecht === "männlich") { + context.kultur = cultureData.system.masculineDemonym + } else { + context.kultur = cultureData.system.feminineDemonym + } + } + context.originalName = actorData.name context.name = context.derived.name ?? actorData.name context.img = actorData.img diff --git a/src/module/xml-import/xml-import.mjs b/src/module/xml-import/xml-import.mjs index f24f3189..3427a1d2 100644 --- a/src/module/xml-import/xml-import.mjs +++ b/src/module/xml-import/xml-import.mjs @@ -1,7 +1,8 @@ import {LiturgyData} from "../data/miracle/liturgydata.mjs"; -import {BlessingDataModel} from "../data/blessing.mjs"; import {Blessing} from "../documents/blessing.mjs"; import {Profession} from "../documents/profession.mjs"; +import {Culture} from "../documents/culture.mjs"; +import {Species} from "../documents/species.mjs"; let months = [ "Praios", @@ -19,20 +20,48 @@ let months = [ "Namenloser Tag" ] +/** + * @typedef ImportOptions + * @property {Boolean} skipSpecies + * @property {Boolean} skipCulture + * @property {Boolean} skipProfessions + * @property {Boolean} skipAdvantages + * @property {Boolean} skipSpecialAbilities + * @property {Boolean} skipEquipment + * @property {Boolean} skipSpells + * @property {Boolean} skipLiturgies + * @property {Boolean} skipSkills + **/ /** * 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 + * @param {ImportOptions?} options the set of item types the import should skip */ -export async function importCharacter(actorId, file) { +export async function importCharacter(actorId, file, options = undefined) { 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(actor, rawJson) + + if (!options) { + options = { + skipSpecies: false, + skipCulture: false, + skipProfessions: false, + skipAdvantages: false, + skipSpecialAbilities: false, + skipEquipment: false, + skipSkills: false, + skipSpells: false, + skipLiturgies: false + } + } + + let characterJson = mapRawJson(actor, rawJson, options) actor.update(characterJson) } @@ -75,6 +104,38 @@ function getJsonFromXML(dom) { return jsonResult; } +/** + * gets the text content of a file + * @param file the file with the desired content + * @returns {Promise} 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` +} + + async function addSkillFromCompendiumByNameToActor(talentName, taw, actor, combatStatistics, attributes) { const compendiumOfSkills = game.packs.get('DSA_4-1.talente'); const talentId = compendiumOfSkills.index.find(skill => skill.name === talentName) @@ -122,6 +183,7 @@ async function addAdvantageFromCompendiumByNameToActor(advantageName, advantageV } async function addSpellsFromCompendiumByNameToActor(spellName, zfw, representation, hauszauber, actor) { + const compendiumOfSpells = game.packs.get('DSA_4-1.spells'); const SCREAMING_NAME = spellName.toUpperCase() const spellId = compendiumOfSpells.index.find(spell => spell.name === SCREAMING_NAME) @@ -155,38 +217,17 @@ async function addLiturgiesFromCompendiumByNameToActor(liturgyName, actor) { } } -/** - * gets the text content of a file - * @param file the file with the desired content - * @returns {Promise} 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` -} - function mapSkills(actor, held, kampfwerte) { + if (actor.itemTypes["Skill"].length > 0) { + actor.itemTypes["Skill"].forEach(s => { + actor.items.get(s._id).delete() + }) + } + if (actor.itemTypes["Blessing"].length > 0) { + actor.itemTypes["Blessing"].forEach(p => { + actor.items.get(p._id).delete() + }) + } for (let talent in held.talentliste.talent) { talent = held.talentliste.talent[talent] @@ -213,6 +254,11 @@ function mapSkills(actor, held, kampfwerte) { } function mapAdvantages(actor, held) { + if (actor.itemTypes["Advantage"].length > 0) { + actor.itemTypes["Advantage"].forEach(a => { + actor.items.get(a._id).delete() + }) + } for (let advantage in held.vt.vorteil) { advantage = held.vt.vorteil[advantage] addAdvantageFromCompendiumByNameToActor(advantage.name, advantage.value, actor) @@ -220,20 +266,35 @@ function mapAdvantages(actor, held) { } function mapSpells(actor, held) { + if (actor.itemTypes["Spell"].length > 0) { + actor.itemTypes["Spell"].forEach(s => { + actor.items.get(s._id).delete() + }) + } for (let spell in held.zauberliste.zauber) { spell = held.zauberliste.zauber[spell] addSpellsFromCompendiumByNameToActor(spell.name, spell.value, spell.repraesentation, spell.hauszauber === "true", actor) } } -function mapMiracles(actor, liturgies) { +function mapLiturgies(actor, liturgies) { + if (actor.itemTypes["Liturgy"].length > 0) { + actor.itemTypes["Liturgy"].forEach(l => { + actor.items.get(l._id).delete() + }) + } for (let liturgy in liturgies) { liturgy = liturgies[liturgy] addLiturgiesFromCompendiumByNameToActor(liturgy.name, actor) } } -function mapProfessions(actor, professions) { +async function mapProfessions(actor, professions) { + if (actor.itemTypes["Profession"].length > 0) { + actor.itemTypes["Profession"].forEach(p => { + actor.items.get(p._id).delete() + }) + } if (professions.string) { professions = {hauptprofession: professions} } @@ -251,23 +312,88 @@ function mapProfessions(actor, professions) { ]) }) // actor.update({"system.meta.professions": professions}) +} +async function mapSpezies(actor, spezies) { + if (actor.itemTypes["Species"].length > 0) { + actor.itemTypes["Species"].forEach(s => { + actor.items.get(s._id).delete() + }) + } + const compendiumOfSpecies = game.packs.get('DSA_4-1.spezien'); + const speciesId = compendiumOfSpecies?.index.find(species => species.name === spezies.name) + + if (speciesId) { + const species = + await compendiumOfSpecies.getDocument(speciesId); + try { + await actor.createEmbeddedDocuments('Item', [species]) + } catch (e) { + } + } else { + actor.createEmbeddedDocuments('Item', [ + new Species({ + name: spezies.name, + type: "Species", + system: { + feminineDemonym: spezies.string, + masculineDemonym: spezies.string, + description: "Importiert", + } + }) + ]) + } + +} + +async function mapKultur(actor, kultur) { + if (actor.itemTypes["Culture"].length > 0) { + actor.itemTypes["Culture"].forEach(c => { + actor.items.get(c._id).delete() + }) + } + const compendiumOfCultures = game.packs.get('DSA_4-1.kulturen'); + const cultureId = compendiumOfCultures?.index.find(culture => culture.name === kultur.name) + + if (cultureId) { + const culture = + await compendiumOfCultures.getDocument(cultureId); + try { + await actor.createEmbeddedDocuments('Item', [culture]) + } catch (e) { + } + } else { + actor.createEmbeddedDocuments('Item', [ + new Culture({ + name: kultur.name, + type: "Culture", + system: { + variant: kultur.variante.name ?? "", + feminineDemonym: kultur.string, + masculineDemonym: kultur.string, + description: "Importiert", + } + }) + ]) + } } /** * parses a json into a fitting character-json + * @param {Character} actor * @param rawJson the json parsed from the Helden-Software XML + * @param {ImportOptions} options * @returns {{}} a json representation of the character */ -function mapRawJson(actor, rawJson) { +function mapRawJson(actor, rawJson, options) { 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 - mapProfessions(actor, held.basis.ausbildungen.ausbildung) + if (!options.skipSpecies) mapSpezies(actor, held.basis.rasse) // as string includes the demonymized form + if (!options.skipCulture) mapKultur(actor, held.basis.kultur) // as string includes the demonymized form + if (!options.skipProfessions) mapProfessions(actor, held.basis.ausbildungen.ausbildung) json.meta.geschlecht = held.basis.geschlecht.name json.meta.haarfarbe = held.basis.rasse.aussehen.haarfarbe json.meta.groesse = held.basis.rasse.groesse.value @@ -324,7 +450,7 @@ function mapRawJson(actor, rawJson) { aktuell: attribute.value } json.attribute.so = getAttributeJson(attributes, "Sozialstatus") - mapAdvantages(actor, held) + if (!options.skipAdvantages) mapAdvantages(actor, held) let specialAbilities = [] let liturgies = [] for (let specialAbility in held.sf.sonderfertigkeit) { @@ -372,9 +498,9 @@ function mapRawJson(actor, rawJson) { } json.kampfwerte = combatValues - mapSkills(actor, held, combatValues) - mapSpells(actor, held) - mapMiracles(actor, liturgies) + if (!options.skipSkills) mapSkills(actor, held, combatValues) + if (!options.skipSpells) mapSpells(actor, held) + if (!options.skipLiturgies) mapLiturgies(actor, liturgies) let notes = [] for (let note in held.kommentare) { diff --git a/src/packs/_source/kulturen/helden.model.kultur.Mittelreich.json b/src/packs/_source/kulturen/helden.model.kultur.Mittelreich.json new file mode 100644 index 00000000..f8c00cc0 --- /dev/null +++ b/src/packs/_source/kulturen/helden.model.kultur.Mittelreich.json @@ -0,0 +1,6 @@ +{ + "name": "helden.model.kultur.Mittelreich", + "masculineDemonym": "Mittelländische Landbevölkerung", + "feminineDemonym": "Mittelländische Landbevölkerung", + "description": "" +} \ No newline at end of file diff --git a/src/packs/_source/spezien/helden.model.rasse.Mittellaender.json b/src/packs/_source/spezien/helden.model.rasse.Mittellaender.json new file mode 100644 index 00000000..88b9b572 --- /dev/null +++ b/src/packs/_source/spezien/helden.model.rasse.Mittellaender.json @@ -0,0 +1,7 @@ +{ + "name": "helden.model.rasse.Mittellaender", + "masculineDemonym": "Mittelländer", + "feminineDemonym": "Mittelländerin", + "description": "", + "baseSpeed": 6 +} \ No newline at end of file diff --git a/src/packs/_source/zauber/MemorabiaFalsafir.json b/src/packs/_source/zauber/MemorabiaFalsafir.json index becc1728..04ce1171 100644 --- a/src/packs/_source/zauber/MemorabiaFalsafir.json +++ b/src/packs/_source/zauber/MemorabiaFalsafir.json @@ -26,7 +26,7 @@ "mod": "+7", "description": "Der Magier ersetzt die verdrängte Erinnerung wahlweise mit einer eigenen Erinnerung oder fügt das gestohlene Wissen des Opfers seiner eigenen Erinnerung hinzu.", "limit": "11" - }, + } }, "reversalis": "bringt die durch den MEMORABIA verdrängte Erinnerung wieder zurück.", "antimagie": "BEHERRSCHUNG BRECHEN hebt den Zauber auf und bringt die Erinnerung wieder zurück; in einer Zone dieses Zaubers kann der MEMORABIA nur erschwert gesprochen werden.", diff --git a/src/packs/_source/zauber/Windstille.json b/src/packs/_source/zauber/Windstille.json index 5bfbfb14..09e50f5e 100644 --- a/src/packs/_source/zauber/Windstille.json +++ b/src/packs/_source/zauber/Windstille.json @@ -1,7 +1,11 @@ { "seite": "287", "name": "WINDSTILLE", - "probe": ["KL", "CH", "KK"], + "probe": [ + "KL", + "CH", + "KK" + ], "technik": "Der Elf formt die Hände über seinem Kopf zu einem Dach, spricht 'rongra sala bian'dao' und breitet die Arme aus.", "zauberdauer": "30 Aktionen", "wirkung": "Der Zauber erschafft eine Zone völliger Windstille, unabhängig von der Windstärke außerhalb. Diese Zone bewegt sich mit dem Elfen. Innerhalb dieser Zone steht die Luft still – selbst Blätter und Rauch verharren. Geräusche tragen schlechter, Pfeile und Geschosse verlieren an Reichweite, und fliegende Wesen haben Mühe, sich zu bewegen. Die Zauberprobe ist um die aktuelle Windstärke (nach Beaufort-Skala) erschwert. Der Effekt wirkt bis zur Windstärke 12 (Orkan).", @@ -29,7 +33,11 @@ }, "reversalis": "Jede Luftbewegung innerhalb der Zone wird um ZfP*/2 Windstärken verstärkt, sodass schon ein leichtes Blasen einen kräftigen Wind erzeugen kann. Diese Variante kostet 10 AsP pro Spielrunde.", "antimagie": "VERÄNDERUNG AUFHEBEN oder LUFTBANN können den Zauber schwächen oder beenden. Befindet sich die Zone in einem Sturm, kann dieser abgeschwächt, aber nicht aufgehoben werden.", - "merkmal": ["Elementar (Luft)", "Umwelt"], + "merkmal": [ + "Elementar (Luft)", + "Umwelt" + ], "komplexität": "C", "repräsentation": "Elf 6, Mag, Dru (Mag) je 2", - "info": "WINDSTILLE gilt als Teil einer Hexalogie des Banns elementarer Gewalten, die vermutlich von el + "info": "WINDSTILLE gilt als Teil einer Hexalogie des Banns elementarer Gewalten, die vermutlich von el" +} diff --git a/src/packs/_source/zauber/XenographusSchriftenkunde.json b/src/packs/_source/zauber/XenographusSchriftenkunde.json index 753cffdc..73da6dda 100644 --- a/src/packs/_source/zauber/XenographusSchriftenkunde.json +++ b/src/packs/_source/zauber/XenographusSchriftenkunde.json @@ -17,4 +17,4 @@ "komplexität": "E", "repräsentation": "Mag 2", "info": "Der Zauber wurde erstmals in Punin dokumentiert und vermutlich aus einem alten Text über Rohals Systeme abgeleitet. Heute wird er in der Puniner Akademie, der Thorwaler Hellsichtschule und dem Khunchomer Artefaktmagier-Zirkel gelehrt. XENOGRAPHUS SCHRIFTENKUNDE wird vor allem von Gelehrten, Artefaktmagiern und Historikern genutzt, um uralte Texte, magische Runen oder verschlüsselte Aufzeichnungen zu entziffern. Der Zauber übersetzt jedoch keine Sprache und vermittelt auch kein echtes Sprachverständnis." -}, +} diff --git a/src/style/organisms/_culture-sheet.scss b/src/style/organisms/_culture-sheet.scss new file mode 100644 index 00000000..034e17b6 --- /dev/null +++ b/src/style/organisms/_culture-sheet.scss @@ -0,0 +1,26 @@ +.application.sheet.dsa41.item.culture { + + section.culture { + + display: flex; + flex-direction: column; + flex: 1; + + div { + flex: 0; + } + + div.editor { + flex: 1; + display: flex; + flex-direction: column; + + label { + flex: 0; + } + + } + + } + +} \ No newline at end of file diff --git a/src/style/organisms/_profession-sheet.scss b/src/style/organisms/_profession-sheet.scss new file mode 100644 index 00000000..5c9a5023 --- /dev/null +++ b/src/style/organisms/_profession-sheet.scss @@ -0,0 +1,26 @@ +.application.sheet.dsa41.item.profession { + + section.profession { + + display: flex; + flex-direction: column; + flex: 1; + + div { + flex: 0; + } + + div.editor { + flex: 1; + display: flex; + flex-direction: column; + + label { + flex: 0; + } + + } + + } + +} \ No newline at end of file diff --git a/src/style/organisms/_species-sheet.scss b/src/style/organisms/_species-sheet.scss new file mode 100644 index 00000000..dc0c31c6 --- /dev/null +++ b/src/style/organisms/_species-sheet.scss @@ -0,0 +1,26 @@ +.application.sheet.dsa41.item.species { + + section.species { + + display: flex; + flex-direction: column; + flex: 1; + + div { + flex: 0; + } + + div.editor { + flex: 1; + display: flex; + flex-direction: column; + + label { + flex: 0; + } + + } + + } + +} \ No newline at end of file diff --git a/src/style/styles.scss b/src/style/styles.scss index 97ef261f..32e2a996 100644 --- a/src/style/styles.scss +++ b/src/style/styles.scss @@ -20,3 +20,7 @@ @use "organisms/skill-sheet"; @use "organisms/active-effect-sheet"; @use "organisms/advantage-sheet"; +@use "organisms/culture-sheet"; +@use "organisms/species-sheet"; +@use "organisms/profession-sheet"; + diff --git a/src/system.json b/src/system.json index ae1ac3de..378e2ba9 100644 --- a/src/system.json +++ b/src/system.json @@ -92,6 +92,30 @@ "type": "Item", "path": "packs/wunden", "private": false + }, + { + "name": "Professions", + "label": "Professionen", + "system": "DSA_4-1", + "type": "Item", + "path": "packs/professionen", + "private": false + }, + { + "name": "Species", + "label": "Rassen", + "system": "DSA_4-1", + "type": "Item", + "path": "packs/spezien", + "private": false + }, + { + "name": "Cultures", + "label": "Kulturen", + "system": "DSA_4-1", + "type": "Item", + "path": "packs/kulturen", + "private": false } ], "languages": [ @@ -127,6 +151,19 @@ } }, "Item": { + "Culture": { + "htmlFields": [ + "description" + ] + }, + "Species": { + "htmlFields": [ + "description" + ], + "numberFields": [ + "baseSpeed" + ] + }, "Profession": { "htmlFields": [ "description" diff --git a/src/templates/actor/character/main-sheet.hbs b/src/templates/actor/character/main-sheet.hbs index 9f4c6003..ec7e1e6a 100644 --- a/src/templates/actor/character/main-sheet.hbs +++ b/src/templates/actor/character/main-sheet.hbs @@ -8,11 +8,11 @@
- {{system.meta.spezies}} - {{system.meta.kultur}} + {{spezies}} + {{kultur}} {{#each professions}} - {{this.name}} + {{this.name}} {{/each}}
diff --git a/src/templates/item/culture-sheet.hbs b/src/templates/item/culture-sheet.hbs new file mode 100644 index 00000000..b3b864ea --- /dev/null +++ b/src/templates/item/culture-sheet.hbs @@ -0,0 +1,21 @@ +
+
+ + +
+
+ + +
+
+ + + {{{description}}} + +
+
\ No newline at end of file diff --git a/src/templates/item/profession-sheet.hbs b/src/templates/item/profession-sheet.hbs new file mode 100644 index 00000000..dabd4e73 --- /dev/null +++ b/src/templates/item/profession-sheet.hbs @@ -0,0 +1,21 @@ +
+
+ + + {{#if isOwner}} + {{/if}} + +
+
+ + + {{{system.description}}} + +
+
\ No newline at end of file diff --git a/src/templates/item/species-sheet.hbs b/src/templates/item/species-sheet.hbs new file mode 100644 index 00000000..7f45c5d9 --- /dev/null +++ b/src/templates/item/species-sheet.hbs @@ -0,0 +1,25 @@ +
+
+ + +
+
+ + +
+
+ + +
+
+ + + {{{description}}} + +
+
\ No newline at end of file