Compare commits

...

7 Commits

Author SHA1 Message Date
macniel e4b6458520 Merge pull request 'feature/import-from-compendium' (#47) from feature/import-from-compendium into main
Reviewed-on: #47
2025-10-05 13:43:10 +02:00
macniel 41045cb482 Repairs XML-Import for Advantages/Vornachteile 2025-10-02 20:43:50 +02:00
macniel b6814c9f74 Repairs XML-Import for SKills/Talents 2025-10-02 20:28:31 +02:00
macniel e47deaf938 Implements simple advantage template that is then placed on the character sheet.
also fixes rollable partial and skills can be rolled on again.
2025-10-02 18:14:35 +02:00
macniel 21479ce082 Updates Rollables (Attributes, Skills) to include a ContextMenu to delete or adjust their values. Also added the capability to open SkillsSheets. 2025-10-02 17:45:52 +02:00
macniel 1afdd483e6 Character Creation with Default Values is no longer possible (got to be but its not scope of MVP anyway).
Skills either from Compendia or Imported Entries can now be uniquely added to a Character.

This approach will help us later with adding other Elements like Advantages (these need a "uniqueness" Attribute), Spells (always unique), Miracles (also always unique), Equipment
2025-10-02 16:52:56 +02:00
macniel 32031eb548 When a character is created, its base-skills are now added from compendium and added to the actor as an embedded document. 2025-10-01 22:27:21 +02:00
17 changed files with 382 additions and 431 deletions

View File

@ -7,7 +7,6 @@ import { VornachteileDataModel } from "./module/data/vornachteile.mjs";
import { Character } from "./module/documents/character.mjs";
import { CharacterSheet } from "./module/sheets/characterSheet.mjs";
import { VornachteilSheet } from "./module/sheets/vornachteilSheet.mjs";
import {DragDropDSA41} from "./module/extensions/DragDropDSA41.mjs";
async function preloadHandlebarsTemplates() {
return loadTemplates([
@ -15,12 +14,17 @@ async function preloadHandlebarsTemplates() {
'systems/DSA_4-1/templates/ui/partial-rollable-button.hbs',
'systems/DSA_4-1/templates/ui/partial-attribute-button.hbs',
'systems/DSA_4-1/templates/ui/partial-talent-editable.hbs',
'systems/DSA_4-1/templates/ui/partial-die.hbs'
'systems/DSA_4-1/templates/ui/partial-die.hbs',
'systems/DSA_4-1/templates/ui/partial-advantage-button.hbs'
]);
}
Hooks.once("init", () => {
game.DSA41 = {
rollItemMacro
}
// Configure custom Document implementations.
CONFIG.Actor.documentClass = Character;
@ -35,7 +39,10 @@ Hooks.once("init", () => {
Advantage: VornachteileDataModel
}
CONFIG.ux.DragDrop = DragDropDSA41;
CONFIG.Combat.initiative = {
formula: `1d6 + @attribute.ini`,
decimals: 0
}
console.log("DSA 4.1 is ready for development!")
@ -66,5 +73,44 @@ Hooks.once("init", () => {
})
Hooks.on('dropActorSheetData', (actor, sheet, data) => {
CharacterSheet.onDroppedData(actor, sheet, data);
return CharacterSheet.onDroppedData(actor, sheet, data);
} )
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) => createBoilerplateMacro(data, slot));
});
async function createBoilerplateMacro(data, slot) {
console.log(data, slot)
if (data.type !== "Item") return;
if (!("data" in data)) return ui.notifications.warn("You can only create macro buttons for owned Items");
const item = data.data;
// Create the macro command
const command = `game.DSA41.rollItemMacro("${item.name}");`;
let macro = game.macros.entities.find(m => (m.name === item.name) && (m.command === command));
if (!macro) {
macro = await Macro.create({
name: item.name,
type: "script",
img: item.img,
command: command,
flags: { "dsa41.itemMacro": true }
});
}
game.user.assignHotbarMacro(macro, slot);
return false;
}
function rollItemMacro(itemName) {
const speaker = ChatMessage.getSpeaker();
let actor;
if (speaker.token) actor = game.actors.tokens[speaker.token];
if (!actor) actor = game.actors.get(speaker.actor);
const item = actor ? actor.items.find(i => i.name === itemName) : null;
if (!item) return ui.notifications.warn(`Your controlled Actor does not have an item named ${itemName}`);
// Trigger the item roll
return item.roll();
}

View File

@ -1,5 +1,8 @@
import {Skill} from "../documents/skill.mjs";
import {SkillDataModel} from "./skill.mjs";
const {
SchemaField, NumberField, StringField, ArrayField, ForeignDocumentField
SchemaField, NumberField, StringField, EmbeddedDocumentField, DocumentIdField, ArrayField, ForeignDocumentField
} = foundry.data.fields;
export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel {
@ -103,7 +106,7 @@ export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel {
gilde: new StringField(),
}),
vornachteile: new ArrayField(new SchemaField({
vornachteil: new ForeignDocumentField(Item),
vornachteil: new DocumentIdField(Item),
wert: new NumberField({ required: false, integer: true }),
})),
sonderfertigkeiten: new ArrayField(new SchemaField({
@ -111,13 +114,9 @@ export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel {
auswahlen: new ArrayField(new StringField()),
})),
talente: new ArrayField(new SchemaField({
talent: new ForeignDocumentField(Item),
taw: new NumberField({integer: true, required: true}),
})
),
talente: new ArrayField(new DocumentIdField(Item)),
zauber: new ArrayField(new SchemaField({
talent: new ForeignDocumentField(Item),
talent: new DocumentIdField(),
zfw: new NumberField({integer: true, required: true}),
})),
liturgien: new ArrayField(new SchemaField({
@ -140,78 +139,6 @@ export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel {
}
async _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
await game.actors.getName(data.name).update({system: {talente}})
const startEigenschaften = {
"mu": {
start: 10,
aktuell: 10,
mod: 0
},
"kl": {
start: 10,
aktuell: 10,
mod: 0
},
"in": {
start: 10,
aktuell: 10,
mod: 0
},
"ch": {
start: 10,
aktuell: 10,
mod: 0
},
"ff": {
start: 10,
aktuell: 10,
mod: 0
},
"ge": {
start: 10,
aktuell: 10,
mod: 0
},
"ko": {
start: 10,
aktuell: 10,
mod: 0
},
"kk": {
start: 10,
aktuell: 10,
mod: 0
}
}
await game.actors.getName(data.name).update({system: {attribute: startEigenschaften}})
super._onCreate(data, options, userId);
}

View File

@ -8,6 +8,7 @@ export class SkillDataModel extends BaseItem {
return {
name: new StringField({ required: true }),
gruppe: new StringField({ required: true }),
taw: new NumberField({ integer: true, initial: 0 }),
probe: new ArrayField(new StringField(), { exact: 3 }), // References one of the eight attributes by name
voraussetzung: new SchemaField({
talent: new StringField({ model: SkillDataModel }),
@ -34,6 +35,56 @@ export class SkillDataModel extends BaseItem {
* @param {Event} event The originating click event
* @private
*/
async roll() { }
async roll() {
console.log(this.parent)
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: [this.system.probe[0], this.system.probe[1], this.system.probe[2]],
})
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,
}
}
}

View File

@ -34,11 +34,4 @@ export class Character extends Actor {
console.log(data);
return data;
}
static onDroppedData(character, characterSheet, uuid) {
}
}

View File

@ -1,62 +0,0 @@
import {DragDropDSA41} from "./DragDropDSA41.mjs";
export default function DragDropApplicationMixin(Base) {
return class DragDropApplication extends Base {
/** @override */
_onDragOver(event) {
const data = DragDropDSA41.getPayload(event);
DragDropDSA41.dropEffect = event.dataTransfer.dropEffect = (foundry.utils.getType(data) === "Object")
? this._dropBehavior(event, data) : "copy";
}
/* -------------------------------------------- */
/**
* The behavior for the dropped data. When called during the drop event, ensure this is called before awaiting
* anything or the drop behavior will be lost.
* @param {DragEvent} event The drag event.
* @param {object} [data] The drag payload.
* @returns {DropEffectValue}
*/
_dropBehavior(event, data) {
data ??= DragDropDSA41.getPayload(event);
const allowed = this._allowedDropBehaviors(event, data);
let behavior = DragDropDSA41.dropEffect ?? event.dataTransfer?.dropEffect;
if ( event.type === "dragover" ) {
if ( areKeysPressed(event, "dragMove") ) behavior = "move";
else if ( areKeysPressed(event, "dragCopy") ) behavior = "copy";
else behavior = this._defaultDropBehavior(event, data);
}
if ( (behavior !== "none") && !allowed.has(behavior) ) return allowed.first() ?? "none";
return behavior || "copy";
}
/* -------------------------------------------- */
/**
* Types of allowed drop behaviors based on the origin & target of a drag event.
* @param {DragEvent} event The drag event.
* @param {object} [data] The drag payload.
* @returns {Set<DropEffectValue>}
* @protected
*/
_allowedDropBehaviors(event, data) {
return new Set();
}
/* -------------------------------------------- */
/**
* Determine the default drop behavior for the provided operation.
* @param {DragEvent} event The drag event.
* @param {object} [data] The drag payload.
* @returns {DropEffectValue}
* @protected
*/
_defaultDropBehavior(event, data) {
return "copy";
}
};
}

View File

@ -1,54 +0,0 @@
export class DragDropDSA41 extends foundry.applications.ux.DragDrop {
/**
* Drop effect used for current drag operation.
* @type {DropEffectValue|null}
*/
static dropEffect = null;
/* -------------------------------------------- */
/**
* Stored drag event payload.
* @type {{ data: any, event: DragEvent }|null}
*/
static #payload = null;
/* -------------------------------------------- */
/** @override */
async _handleDragStart(event) {
await this.callback(event, "dragstart");
if ( event.dataTransfer.items.length ) {
console.log(event)
event.stopPropagation();
let data = event.dataTransfer.getData("application/json") || event.dataTransfer.getData("text/plain");
try { data = JSON.parse(data); } catch(err) {}
DragDropDSA41.#payload = data ? { event, data } : null;
} else {
DragDropDSA41.#payload = null;
}
}
/* -------------------------------------------- */
/** @override */
async _handleDragEnd(event) {
await this.callback(event, "dragend");
DragDropDSA41.dropEffect = null;
DragDropDSA41.#payload = null;
}
/* -------------------------------------------- */
/**
* Get the data payload for the current drag event.
* @param {DragEvent} event
* @returns {any}
*/
static getPayload(event) {
if ( !DragDropDSA41.#payload?.data ) return null;
return DragDropDSA41.#payload.data;
}
}

View File

@ -1,5 +1,3 @@
import {DragDropDSA41} from "../extensions/DragDropDSA41.mjs";
export class CharacterSheet extends ActorSheet {
/**@override */
static get defaultOptions() {
@ -24,10 +22,6 @@ export class CharacterSheet extends ActorSheet {
/** @override */
getData() {
// Retrieve the data structure from the base sheet. You can inspect or log
// the context variable to see the structure, but some key properties for
// sheets are the actor object, the data object, whether or not it's
// editable, the items array, and the effects array.
const context = super.getData();
// Use a safe clone of the actor data for further operations.
@ -36,6 +30,74 @@ 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;
this.#addSkillsToContext(context)
this.#addAdvantagesToContext(context)
this.#addAttributesToContext(context)
return context;
}
#addSkillsToContext(context) {
const actorData = context.data;
context.skills = {};
context.flatSkills = [];
Object.values(actorData.items).forEach( (item, index) => {
if (item.type === "Skill") {
const talentGruppe = item.system.gruppe;
const eigenschaften = Object.values(item.system.probe);
const werte = [
{name: eigenschaften[0], value: this.prepareEigenschaftRoll(actorData, eigenschaften[0])},
{name: eigenschaften[1], value: this.prepareEigenschaftRoll(actorData, eigenschaften[1])},
{name: eigenschaften[2], value: this.prepareEigenschaftRoll(actorData, eigenschaften[2])}
]
if (context.skills[talentGruppe] == null) {
context.skills[talentGruppe] = [];
}
const obj = {
type: "talent",
gruppe: talentGruppe,
name: item.name,
taw: "" + item.system.taw,
tawPath: `system.items.${index}.taw`,
werte,
rollEigenschaft1: werte[0].value,
rollEigenschaft2: werte[1].value,
rollEigenschaft3: werte[2].value,
eigenschaft1: werte[0].name,
eigenschaft2: werte[1].name,
eigenschaft3: werte[2].name,
probe: `(${eigenschaften.join("/")})`,
id: item._id,
};
context.skills[talentGruppe].push(obj);
context.flatSkills.push(obj);
}
}
);
}
#addAdvantagesToContext(context) {
context.advantages = [];
const actorData = context.data;
Object.values(actorData.items).forEach( (item) => {
if (item.type === "Advantage") {
context.advantages.push({
id: item._id,
name: item.name,
value: item.system.value,
options: item.system.auswahl,
description: item.system.description,
});
}
}
);
}
#addAttributesToContext(context) {
const actorData = context.data;
context.attributes = [
{
eigenschaft: "mu",
@ -57,7 +119,7 @@ export class CharacterSheet extends ActorSheet {
},
{
eigenschaft: "ch",
name: "IN",
name: "CH",
tooltip: "Charisma",
wert: actorData.system.attribute.ch.aktuell ?? 0,
},
@ -81,52 +143,13 @@ export class CharacterSheet extends ActorSheet {
},
{
eigenschaft: "kk",
name: "KO",
name: "KK",
tooltip: "Körperkraft",
wert: actorData.system.attribute.kk.aktuell ?? 0,
},
];
context.skills = {};
context.flatSkills = [];
if ( context.system.talente?.length >= 0) {
context.system.talente.forEach( (talent, index) => {
if (talent.talent) {
const taw = talent.taw;
const talentObjekt = game.items.get(talent.talent);
console.log(talent);
const talentGruppe = talentObjekt.system.gruppe;
const eigenschaften = Object.values(talentObjekt.system.probe);
const werte = [
{name: eigenschaften[0], value: this.prepareEigenschaftRoll(actorData, eigenschaften[0])},
{name: eigenschaften[1], value: this.prepareEigenschaftRoll(actorData, eigenschaften[1])},
{name: eigenschaften[2], value: this.prepareEigenschaftRoll(actorData, eigenschaften[2])}
]
if (context.skills[talentGruppe] == null) {
context.skills[talentGruppe] = [];
}
const obj = {
type: "talent",
gruppe: talentGruppe,
name: talentObjekt.name,
taw: "" + taw,
tawPath: `system.talente.${index}.taw`,
werte,
rollEigenschaft1: werte[0].value,
rollEigenschaft2: werte[1].value,
rollEigenschaft3: werte[2].value,
probe: `(${eigenschaften.join("/")})`
};
context.skills[talentGruppe].push(obj);
context.flatSkills.push(obj);
}
})
}
return context;
}
prepareEigenschaftRoll(actorData, name) {
@ -222,6 +245,42 @@ export class CharacterSheet extends ActorSheet {
}
}
openEmbeddedDocument(documentId) {
this.object.items.get(documentId).sheet.render(true)
}
showAdjustAttributeDialog(attributeName, attributeField, previousValue) {
const thisActor = this;
const myContent = `
Value:
<input id="attributeValue" type="number" value="${previousValue}" />
`;
function updateAttribute(html) {
const value = html.find("input#attributeValue").val();
const attribute = {}
attribute[attributeField.toLowerCase()] = {
aktuell: value
}
thisActor.object.update({ system: { attribute }})
}
new Dialog({
title: `${attributeName} ändern auf`,
content: myContent,
buttons: {
button1: {
label: "Ändern",
callback: (html) => {
updateAttribute(html)
},
icon: `<i class="fas fa-check"></i>`
}
}
}).render(true);
}
activateListeners(html) {
super.activateListeners(html);
@ -237,78 +296,94 @@ export class CharacterSheet extends ActorSheet {
this._onRoll(evt);
});
// Everything below here is only needed if the sheet is editable
if (!this.isEditable) return;
html.on('click', '.talent .name', (evt) => {
this.openEmbeddedDocument(evt.target.dataset.id);
evt.stopPropagation();
})
html.on('click', '.advantage .name', (evt) => {
this.openEmbeddedDocument(evt.target.dataset.id);
evt.stopPropagation();
})
new ContextMenu(html, '.talent.rollable', [
{
name: "Entfernen",
icon: '<i class="fa-solid fa-trash"></i>',
callback: (event) => {
this.object.deleteEmbeddedDocuments('Item', [event[0].dataset.id])
},
condition: () => true
}
]);
new ContextMenu(html, '.attribute.rollable', [
{
name: "Anpassen",
icon: '<i class="fa-solid fa-pen"></i>',
callback: (event) => {
this.showAdjustAttributeDialog(event[0].dataset.name, event[0].dataset.label, event[0].dataset.value)
},
condition: () => true
}
]);
let handler = ev => this._onDragStart(ev);
// Find all items on the character sheet.
html.find('.talent.rollable').each((i, li) => {
// Add draggable attribute and dragstart listener.
li.setAttribute("draggable", true);
li.addEventListener("dragstart", handler, false);
});
}
#handleDroppedSkill(actor, skill) {
const array = Array.from(actor.items);
for ( let i = 0; i < array.length; i++ ) {
if (array[i].name === skill.name) {
return false;
}
}
}
#handleDroppedAdvantage(actor, advantage) {
const array = Array.from(actor.items);
for ( let i = 0; i < array.length; i++ ) {
if (array[i].name === advantage.name) { // TODO: adjust for uniqueness
return false;
}
}
}
static getElementByName(collection, id) {
const array = Array.from(collection);
for (const element of array) {
if (element._id === id) {
return element;
}
}
}
static onDroppedData(actor, characterSheet, data) {
const item = game.items.get(foundry.utils.parseUuid(data.uuid).id)
console.log();
let alreadyInSet = false;
let previousTaw = 0;
actor.system.talente.forEach(({taw, talent}) => {
if (talent._id === item._id) {
alreadyInSet = talent;
previousTaw = taw;
}
})
const myContent = `
TaW:
<input id="taw" type="number" value="${previousTaw}" />
`;
new Dialog({
title: `Talent ${item.name} ${alreadyInSet?'ersetzen':'hinzufügen'}`,
content: myContent,
buttons: {
button1: {
label: "hinzufügen",
callback: (html) => myCallback(html),
icon: `<i class="fas fa-check"></i>`
}
}
}).render(true);
async function myCallback(html) {
const taw = html.find("input#taw").val();
let index = actor.system.talente.findIndex( predicate => predicate.talent._id === alreadyInSet._id )
let sorted = [];
if (alreadyInSet) {
actor.system.talente[index].taw = taw;
sorted = actor.system.talente;
} else {
sorted = [{
taw: taw,
talent: {_id: item._id, name: item.name}
}, ...actor.system.talente].sort((a, b) => a.talent.name.localeCompare(b.talent.name));
}
const serialised = sorted.map(({taw, talent}) => {
return {
taw: taw,
talent: talent._id
}
});
await actor.update({
system: {
talente: [
...serialised
]
}
});
await characterSheet.render(true);
ui.notifications.info(`Talent ${item.name} auf TaW ${taw} hinzugefügt`);
const uuid = foundry.utils.parseUuid(data.uuid);
const collection = uuid.collection.index ?? uuid.collection;
const document = CharacterSheet.getElementByName(collection, uuid.id);
const {
name,
type
} = document
console.log(name, type)
switch (type) {
case "Skill":
return characterSheet.#handleDroppedSkill(actor, document); // on false cancel this whole operation
case "Advantage":
return characterSheet.#handleDroppedAdvantage(actor, document);
default:
return false;
}
actor.items.clear()
}
}

View File

@ -1,7 +1,4 @@
import {DragDropDSA41} from "../extensions/DragDropDSA41.mjs";
import DragDropApplicationMixin from "../extensions/DragDropApplicationMixin.mjs";
export class SkillSheet extends DragDropApplicationMixin(foundry.appv1.sheets.ItemSheet) {
export class SkillSheet extends ItemSheet {
/**@override */
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
@ -58,59 +55,4 @@ export class SkillSheet extends DragDropApplicationMixin(foundry.appv1.sheets.It
if (!this.isEditable) return;
}
/* -------------------------------------------- */
/* Drag & Drop */
/* -------------------------------------------- */
/** @override */
_allowedDropBehaviors(event, data) {
console.log(data, event);
if ( !data?.uuid ) return new Set(["copy", "link"]);
const allowed = new Set(["copy", "move", "link"]);
const s = foundry.utils.parseUuid(data.uuid);
const t = foundry.utils.parseUuid(this.document.uuid);
const sCompendium = s.collection instanceof foundry.documents.collections.CompendiumCollection;
const tCompendium = t.collection instanceof foundry.documents.collections.CompendiumCollection;
// If either source or target are within a compendium, but not inside the same compendium, move not allowed
if ( (sCompendium || tCompendium) && (s.collection !== t.collection) ) allowed.delete("move");
return allowed;
}
/* -------------------------------------------- */
/** @override */
_defaultDropBehavior(event, data) {
if ( !data?.uuid ) return "copy";
const d = foundry.utils.parseUuid(data.uuid);
const t = foundry.utils.parseUuid(this.document.uuid);
const base = d.embedded?.length ? "document" : "primary";
console.log(d, t, base);
return (d.collection === t.collection) && (d[`${base}Id`] === t[`${base}Id`])
&& (d[`${base}Type`] === t[`${base}Type`]) ? "move" : "copy";
}
/* -------------------------------------------- */
/** @inheritDoc */
async _onDragStart(event) {
await super._onDragStart(event);
if ( !this.document.isOwner || this.document.collection?.locked ) {
event.dataTransfer.effectAllowed = "copyLink";
}
}
_onDragOver(event) {
super._onDragOver(event);
console.log(event);
}
_dropBehavior(event, data) {
console.log(event, data);
return super._dropBehavior(event, data);
}
}

View File

@ -1,7 +1,4 @@
import {DragDropDSA41} from "../extensions/DragDropDSA41.mjs";
import DragDropApplicationMixin from "../extensions/DragDropApplicationMixin.mjs";
export class VornachteilSheet extends DragDropApplicationMixin(foundry.appv1.sheets.ItemSheet) {
export class VornachteilSheet extends ItemSheet {
/**@override */
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {

View File

@ -27,7 +27,7 @@ export async function importCharacter(actorId, file) {
let dom = domParser.parseFromString(xmlString, 'application/xml')
let rawJson = getJsonFromXML(dom)
let characterJson = mapRawJson(rawJson)
let characterJson = mapRawJson(actor, rawJson)
actor.update(characterJson)
}
@ -70,6 +70,38 @@ function getJsonFromXML(dom) {
return jsonResult;
}
async function addSkillFromCompendiumByNameToActor(talentName, taw, actor) {
const compendiumOfSkills = game.packs.get('DSA_4-1.talente-brw');
const talentId = compendiumOfSkills.index.find( skill => skill.name === talentName)
if (talentId) {
const talent = await compendiumOfSkills.getDocument(talentId._id);
try {
const embeddedDocument = (await actor.createEmbeddedDocuments('Item', [talent]))[0]
embeddedDocument.update({system: {taw: taw}});
} catch (error) {
console.error(`${talentName} not found in items`, error)
}
}
}
async function addAdvantageFromCompendiumByNameToActor(advantageName, advantageValue, actor) {
const compendiumOfAdvantages = game.packs.get('DSA_4-1.Advantage');
const advantageId = compendiumOfAdvantages.index.find( skill => skill.name === advantageName)
if (advantageId) {
const advantage = await compendiumOfAdvantages.getDocument(advantageId._id);
try {
const embeddedDocument = (await actor.createEmbeddedDocuments('Item', [advantage]))[0]
embeddedDocument.update({system: {value: advantageValue}});
} catch (error) {
console.error(`${advantageName} not found in items`, error)
}
}
}
/**
* gets the text content of a file
* @param file the file with the desired content
@ -101,12 +133,26 @@ function calculateBirthdate(json) {
return `${day}. ${month} ${year} BF`
}
function mapSkills(actor, held) {
for (let talent in held.talentliste.talent) {
talent = held.talentliste.talent[talent]
addSkillFromCompendiumByNameToActor(talent.name, talent.value, actor)
}
}
function mapAdvantages(actor, held) {
for (let advantage in held.vt.vorteil) {
advantage = held.vt.vorteil[advantage]
addAdvantageFromCompendiumByNameToActor(advantage.name, advantage.value, actor)
}
}
/**
* 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) {
function mapRawJson(actor, rawJson) {
let json = {}
let held = rawJson.helden.held;
json.name = held.name
@ -201,18 +247,7 @@ function mapRawJson(rawJson) {
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
mapAdvantages(actor, held)
let specialAbilities = []
let liturgies = []
for (let specialAbility in held.sf.sonderfertigkeit) {
@ -248,19 +283,8 @@ function mapRawJson(rawJson) {
}
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
mapSkills(actor, held)
let spells = []
/*for (let spell in held.zauberliste.zauber) {
spell = held.zauberliste.zauber[spell]

View File

@ -24,13 +24,13 @@ $rollable_colours: (
height: 32px;
position: relative;
margin: 4px;
z-index: 2;
.die {
width: 32px;
height: 32px;
display: inline-block;
position: relative;
z-index: 1;
.border {
fill: #0000;
@ -64,7 +64,6 @@ $rollable_colours: (
left: 16px;
top: 0;
height: 32px;
z-index: -1;
padding-left: 24px;
span.name {

View File

@ -60,7 +60,11 @@
</div>
</div>
<div class="tab attributes" data-group="primary" data-tab="attributes">
<ul>
{{#each this.advantages}}
<li>{{> "systems/DSA_4-1/templates/ui/partial-advantage-button.hbs" this}}</li>
{{/each}}
</ul>
</div>
<div class="tab attributes" data-group="primary" data-tab="attributes">

View File

@ -8,7 +8,7 @@
{{!-- Sheet Body --}}
<section class="sheet-body" style="flex: 1">
<div class="tab json" data-group="primary" data-tab="json">
<pre style="overflow: auto">{{json}}</pre>
<pre style="overflow: auto; white-space: normal; position: relative; top: 8px; bottom: 8px; left: 8px; right: 8px">{{json}}</pre>
</div>
</section>
</form>

View File

@ -17,6 +17,12 @@
</select>
</label>
</div>
<div>
<label>TAW:
<input type="text" name="system.taw"
value="{{system.taw}}" />
</label>
</div>
<div>
<label>Erstes Attribut
<input type="text" name="system.probe.0"

View File

@ -0,0 +1,3 @@
<div class="advantage">
<span class="name" data-id="{{this.id}}">{{this.name}}</span>
</div>

View File

@ -1,4 +1,4 @@
<div class="attribute rollable" data-label="{{this.name}}" data-roll="1d20cs<=@{{this.eigenschaft}}.aktuell">
<div class="attribute rollable" data-name="{{this.tooltip}}" alt="{{this.tooltip}}" data-value="{{this.wert}}" data-label="{{this.name}}" data-roll="1d20cs<=@{{this.eigenschaft}}.aktuell">
<div class="die">
{{> 'systems/DSA_4-1/templates/ui/partial-die.hbs' }}
</div>

View File

@ -1,4 +1,4 @@
<div class="block rollable {{this.type}} {{this.gruppe}}" data-taw="{{this.taw}}" data-rollEigenschaft1="{{this.rollEigenschaft1}}" data-rollEigenschaft2="{{this.rollEigenschaft2}}" data-rollEigenschaft3="{{this.rollEigenschaft3}}">
<div class="block rollable {{this.type}} {{this.gruppe}}" data-id="{{this.id}}" data-taw="{{this.taw}}" data-name="{{this.name}}" data-eigenschaft1="{{this.eigenschaft1}}" data-eigenschaft2="{{this.eigenschaft2}}" data-eigenschaft3="{{this.eigenschaft3}}" data-taw="{{this.taw}}" data-rollEigenschaft1="{{this.rollEigenschaft1}}" data-rollEigenschaft2="{{this.rollEigenschaft2}}" data-rollEigenschaft3="{{this.rollEigenschaft3}}">
<div class="die">
{{> 'systems/DSA_4-1/templates/ui/partial-die.hbs' }}
@ -12,7 +12,7 @@
</span>
</div>
<div class="container">
<span class="name">{{this.name}}</span>
<span class="name" data-id="{{this.id}}">{{this.name}}</span>
<div class="werte">
{{#each this.werte}}
<div class="eigenschaft"><span class="name">{{this.name}}</span><span class="value">{{this.value}}</span></div>