feature/meta (#54)

implements styling of meta and various other segments e.g. sheets
feature/wounds
macniel 2025-10-09 21:16:51 +02:00
parent 5b76df3653
commit faa3bc1328
29 changed files with 1046 additions and 128 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 500 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -16,6 +16,10 @@ import {CreatureSheet} from "./module/sheets/creatureSheet.mjs";
import {LiturgySheet} from "./module/sheets/liturgySheet.mjs";
import {LiturgyDataModel} from "./module/data/liturgy.mjs";
import {BlessingDataModel} from "./module/data/blessing.mjs";
import {SpecialAbilityDataModel} from "./module/data/specialAbility.mjs";
import {SpecialAbilitySheet} from "./module/sheets/specialAbilitySheet.mjs";
import {ActiveEffectSheet} from "./module/sheets/ActiveEffectSheet.mjs";
import {ActiveEffectDataModel} from "./module/data/activeeffect.mjs";
async function preloadHandlebarsTemplates() {
return loadTemplates([
@ -26,6 +30,7 @@ async function preloadHandlebarsTemplates() {
'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-advantage-button.hbs',
'systems/DSA_4-1/templates/ui/partial-sf-button.hbs',
'systems/DSA_4-1/templates/ui/partial-action-button.hbs',
'systems/DSA_4-1/templates/ui/partial-equipment-button.hbs',
'systems/DSA_4-1/templates/ui/partial-equipment-group-button.hbs',
@ -56,7 +61,9 @@ Hooks.once("init", () => {
Advantage: VornachteileDataModel,
Equipment: EquipmentDataModel,
Liturgy: LiturgyDataModel,
Blessing: BlessingDataModel
Blessing: BlessingDataModel,
SpecialAbility: SpecialAbilityDataModel,
ActiveEffect: ActiveEffectDataModel,
}
CONFIG.Combat.initiative = {
@ -100,15 +107,26 @@ Hooks.once("init", () => {
})
Items.registerSheet('dsa41.equipment', AusruestungSheet, {
types: ["Equipment"],
makeDefault: true,
makeDefault: false,
label: 'DSA41.AusruestungLabels.Item'
})
Items.registerSheet('dsa41.liturgy', LiturgySheet, {
types: ["SpecialAbility"],
makeDefault: true,
label: 'DSA41.SpecialAbilityLabels.Item'
})
Items.registerSheet('dsa41.specialAbility', SpecialAbilitySheet, {
types: ["Liturgy"],
makeDefault: true,
label: 'DSA41.LiturgyLabels.Item'
})
Items.registerSheet('dsa41.activeEffect', ActiveEffectSheet, {
types: ['ActiveEffect'],
makeDefault: true,
label: 'DSA41.ActiveEffectLabels.ActiveFfect'
})
return preloadHandlebarsTemplates();
})

View File

@ -0,0 +1,49 @@
import BaseItem from "./base-item.mjs";
const {ArrayField, NumberField, StringField, HTMLField} = foundry.data.fields;
export class ActiveEffectDataModel extends BaseItem {
static defineSchema() {
return {
name: new StringField({required: true}),
notes: new HTMLField(),
}
/*
name: String, // Name of Vornachteil will be used for rendering and referencing by other Items
description: HTMLString, // only used for rendering
variant: [String]?, // variant name of Vornachteil e.g. "Mut" in the case of "Herausragende Eigenschaft"
levels: [Number]?, // available levels e.g. 1, 2 in the case of "Flink"
mods: [
{
level: Number?, // in reference to level of the Vornachteil, is null when it does not have any levels or is the only modification
field: String, // Reference to Actor Data e.g. "FF" maps to "FF.mod"
value: Number, // value of the Modification e.g. "+2" maps to 2
requirement: {
field: String // Reference to Actor Data e.g. "BE" maps "be.aktuell"
operation: String // Supported: "<=", ">"
value: Number // Target Value the referenced field has to compare against
}? // optional when the mod does not have an active requirement
}
]
*/
}
_onCreate(data, options, userId) {
super._onCreate(data, options, userId);
if (this.parent.getEmbeddedCollection("ActiveEffect").contents.length === 0) {
this.parent.createEmbeddedDocuments("ActiveEffect", [{
name: data.name,
changes: [],
duration: {},
icon: this.img,
}]);
console.log("added default activeffect");
}
}
}

View File

@ -1,5 +1,12 @@
const {
SchemaField, NumberField, StringField, EmbeddedDocumentField, DocumentIdField, ArrayField, ForeignDocumentField
SchemaField,
NumberField,
StringField,
HTMLField,
EmbeddedDocumentField,
DocumentIdField,
ArrayField,
ForeignDocumentField
} = foundry.data.fields;
export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel {
@ -13,16 +20,17 @@ export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel {
professions: new ArrayField(new StringField()),
geschlecht: new StringField(),
haarfarbe: new StringField(),
groesse: new NumberField({required: true, integer: false}),
groesse: new StringField(),
augenfarbe: new StringField(),
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()),
alter: new StringField(),
gewicht: new StringField(),
aussehen: new HTMLField(),
familie: new HTMLField(),
titel: new StringField(),
stand: new StringField(),
}),
setEquipped: new NumberField({required: true, initial: 0, max: 3, integer: true}),
ini: new SchemaField({
aktuell: new NumberField({required: true, integer: true, initial: 0}),
mod: new NumberField({required: true, integer: true, initial: 0}),
@ -144,27 +152,22 @@ export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel {
_initializeSource(data, options) {
if (data.heldenausruestung.length === 0) {
let sets = [];
for (let i = 0; i < 3; i++) {
const preppedSet = {}
PlayerCharacterDataModel.getSlots().forEach(slot => {
preppedSet[slot] = null;
})
sets.push(preppedSet);
}
data.heldenausruestung = sets
}
return super._initializeSource(data, options);
}
async _onCreate(data, options, userId) {
console.log(data, 'onCreate');
}
static getSlots() {

View File

@ -0,0 +1,12 @@
import BaseItem from "./base-item.mjs";
const {BooleanField, NumberField, SchemaField, ArrayField, StringField, HTMLField} = foundry.data.fields;
export class SpecialAbilityDataModel extends BaseItem {
static defineSchema() {
return {
name: new StringField()
}
}
}

View File

@ -50,14 +50,50 @@ export class Character extends Actor {
systemData.ini.aktuell = Math.round((mu + mu + _in + ge) / 5) + systemData.ini.mod;
systemData.mr.aktuell = Math.round((mu + kl + ko) / 5) + systemData.mr.mod;
// evaluate deities for KaP
systemData.rs = 0;
systemData.be = 0;
// map current set to RS and BE
const ausruestung = systemData.heldenausruestung[systemData.setEquipped];
if (ausruestung) {
if (ausruestung.brust) {
systemData.rs += systemData.parent.items.get(ausruestung.brust).system.armorValue ?? 0
systemData.be += systemData.parent.items.get(ausruestung.brust).system.armorHandicap ?? 0
}
if (ausruestung.bauch) {
systemData.rs += systemData.parent.items.get(ausruestung.bauch).system.armorValue ?? 0
systemData.be += systemData.parent.items.get(ausruestung.bauch).system.armorHandicap ?? 0
}
if (ausruestung.ruecken) {
systemData.rs += systemData.parent.items.get(ausruestung.ruecken).system.armorValue ?? 0
systemData.be += systemData.parent.items.get(ausruestung.ruecken).system.armorHandicap ?? 0
}
if (ausruestung.armlinks) {
systemData.rs += systemData.parent.items.get(ausruestung.armlinks).system.armorValue ?? 0
systemData.be += systemData.parent.items.get(ausruestung.armlinks).system.armorHandicap ?? 0
}
if (ausruestung.armrechts) {
systemData.rs += systemData.parent.items.get(ausruestung.armrechts).system.armorValue ?? 0
systemData.be += systemData.parent.items.get(ausruestung.armrechts).system.armorHandicap ?? 0
}
if (ausruestung.beinlinks) {
systemData.rs += systemData.parent.items.get(ausruestung.beinlinks).system.armorValue ?? 0
systemData.be += systemData.parent.items.get(ausruestung.beinlinks).system.armorHandicap ?? 0
}
if (ausruestung.beinrechts) {
systemData.rs += systemData.parent.items.get(ausruestung.beinrechts).system.armorValue ?? 0
systemData.be += systemData.parent.items.get(ausruestung.beinrechts).system.armorHandicap ?? 0
}
}
systemData.kap.max = 0;
const deities = systemData.parent.items.filter(p => p.type === "Blessing")
// evaluate deities for KaP
const deities = systemData.parent.items.filter(p => p.type === "Blessing")
deities?.forEach((deity) => {
if (LiturgyData.alverans.includes(deity.system.gottheit)) {
systemData.kap.max = 24;

View File

@ -0,0 +1,9 @@
export class SpecialAbility extends Item {
/**
* Augment the basic Item data model with additional dynamic data.
*/
prepareData() {
super.prepareData();
}
}

View File

@ -0,0 +1,44 @@
export class ActiveEffectSheet extends ItemSheet {
/**@override */
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ['dsa41', 'sheet', 'activeeffect'],
width: 520,
height: 480
});
}
/** @override */
get template() {
return `systems/DSA_4-1/templates/item/item-activeeffect-sheet.hbs`;
}
/** @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();
const effects = context.document.getEmbeddedCollection("ActiveEffect").contents;
if (effects.length > 0) {
context.effectId = effects[0]._id;
}
return context;
}
activateListeners(html) {
super.activateListeners(html);
// Everything below here is only needed if the sheet is editable
if (!this.isEditable) return;
html.on('click', '.editEffects', (evt) => {
const {id} = evt.currentTarget.dataset;
const effect = this.object.effects.get(id);
effect.sheet.render(true);
})
}
}

View File

@ -39,10 +39,14 @@ export class CharacterSheet extends ActorSheet {
return characterSheet.#handleDroppedSkill(actor, document); // on false cancel this whole operation
case "Advantage":
return characterSheet.#handleDroppedAdvantage(actor, document);
case "ActiveEffect":
return characterSheet.#handleDroppedActiveEffect(actor, document);
case "Equipment":
return characterSheet.#handleDroppedEquipment(actor, document);
case "Liturgy":
return characterSheet.#handleDroppedLiturgy(actor, document);
case "SpecialAbility":
return characterSheet.#handleDroppedSpecialAbility(actor, document);
default:
return false;
}
@ -117,10 +121,14 @@ export class CharacterSheet extends ActorSheet {
context.system = actorData.system;
context.flags = actorData.flags;
context.derived = context.document.system;
context.originalName = actorData.name;
context.name = context.derived.name ?? actorData.name;
context.effects = actorData.effects ?? [];
this.#addSkillsToContext(context)
this.#addAdvantagesToContext(context)
this.#addAttributesToContext(context)
this.#addSpecialAbilitiesToContext(context)
await this.#addAttributesToContext(context)
this.#addEquipmentsToContext(context)
await this.#addCombatStatistics(context)
this.#addActionsToContext(context)
@ -158,56 +166,79 @@ export class CharacterSheet extends ActorSheet {
context.hasSpells = context.spells.length > 0;
}
#addAttributesToContext(context) {
const actorData = context.data;
async #getModsOfAttribute(keyPath) {
let returnValue = [];
Array.from(this.object.appliedEffects).forEach(
(e) =>
e.changes.filter(c => c.key === keyPath).forEach(change => {
returnValue.push({
name: e.name,
value: change.value > 0 ? "+" + change.value : "" + change.value,
icon: e.icon,
})
}))
return returnValue;
}
async #addAttributesToContext(context) {
context.mods = {
"mu": await this.#getModsOfAttribute('system.attribute.mu.mod'),
"kl": await this.#getModsOfAttribute('system.attribute.kl.mod'),
"in": await this.#getModsOfAttribute('system.attribute.in.mod'),
"ch": await this.#getModsOfAttribute('system.attribute.ch.mod'),
"ff": await this.#getModsOfAttribute('system.attribute.ff.mod'),
"ge": await this.#getModsOfAttribute('system.attribute.ge.mod'),
"ko": await this.#getModsOfAttribute('system.attribute.ko.mod'),
"kk": await this.#getModsOfAttribute('system.attribute.kk.mod')
}
context.attributes = [
{
eigenschaft: "mu",
name: "MU",
tooltip: "Mut",
wert: actorData.system.attribute.mu.aktuell ?? 0,
wert: (context.derived.attribute.mu.aktuell + context.derived.attribute.mu.mod) ?? 0,
},
{
eigenschaft: "kl",
name: "KL",
tooltip: "Klugheit",
wert: actorData.system.attribute.kl.aktuell ?? 0,
wert: (context.derived.attribute.kl.aktuell + context.derived.attribute.kl.mod) ?? 0,
},
{
eigenschaft: "in",
name: "IN",
tooltip: "Intuition",
wert: actorData.system.attribute.in.aktuell ?? 0,
wert: (context.derived.attribute.in.aktuell + context.derived.attribute.in.mod) ?? 0,
},
{
eigenschaft: "ch",
name: "CH",
tooltip: "Charisma",
wert: actorData.system.attribute.ch.aktuell ?? 0,
wert: (context.derived.attribute.ch.aktuell + context.derived.attribute.ch.mod) ?? 0,
},
{
eigenschaft: "ff",
name: "FF",
tooltip: "Fingerfertigkeit",
wert: actorData.system.attribute.ff.aktuell ?? 0,
wert: (context.derived.attribute.ff.aktuell + context.derived.attribute.ff.mod) ?? 0,
},
{
eigenschaft: "ge",
name: "GE",
tooltip: "Geschicklichkeit",
wert: actorData.system.attribute.ge.aktuell ?? 0,
wert: (context.derived.attribute.ge.aktuell + context.derived.attribute.ge.mod) ?? 0,
},
{
eigenschaft: "ko",
name: "KO",
tooltip: "Konstitution",
wert: actorData.system.attribute.ko.aktuell ?? 0,
wert: (context.derived.attribute.ko.aktuell + context.derived.attribute.ko.mod) ?? 0,
},
{
eigenschaft: "kk",
name: "KK",
tooltip: "Körperkraft",
wert: actorData.system.attribute.kk.aktuell ?? 0,
wert: (context.derived.attribute.kk.aktuell + context.derived.attribute.kk.mod) ?? 0,
},
];
@ -231,12 +262,22 @@ export class CharacterSheet extends ActorSheet {
);
}
#findEquipmentOnSlot(slot, setNumber) {
return this.object.items.get(this.object.system.heldenausruestung[setNumber][slot])
#addSpecialAbilitiesToContext(context) {
context.specialAbilities = [];
const actorData = context.data;
Object.values(actorData.items).forEach((item) => {
if (item.type === "SpecialAbility") {
context.specialAbilities.push({
id: item._id,
name: item.name,
});
}
}
);
}
#findTalentsOfEquipment(equipment) {
#findEquipmentOnSlot(slot, setNumber) {
return this.object.items.get(this.object.system.heldenausruestung[setNumber]?.[slot])
}
#addActionsToContext(context) {
@ -244,14 +285,16 @@ export class CharacterSheet extends ActorSheet {
context.actions = am.evaluate()
}
#isWorn(itemId, setId) {
#isWorn(itemId) {
const slots = PlayerCharacterDataModel.getSlots()
const set = this.object.system.heldenausruestung[setId]
for (const slot of slots) {
const equipmentSlotId = set[slot]
if (equipmentSlotId === itemId) {
return slot
const set = this.object.system.heldenausruestung[this.object.system.setEquipped]
if (set) {
for (const slot of slots) {
const equipmentSlotId = set[slot]
if (equipmentSlotId === itemId) {
return slot
}
}
}
@ -267,13 +310,15 @@ export class CharacterSheet extends ActorSheet {
context.aupper = Math.min((context.actor.system.aup.aktuell / context.actor.system.aup.max) * 100, 100);
context.lepper = Math.min((context.actor.system.lep.aktuell / context.actor.system.lep.max) * 100, 100);
context.keper = Math.min((context.actor.system.kap.aktuell / context.actor.system.kap.max) * 100, 100);
context.aspper = Math.min((context.actor.system.asp.aktuell / context.actor.system.asp.max) * 100, 100);
context.lepcurrent = context.actor.system.lep.aktuell ?? 0
context.aupcurrent = context.actor.system.aup.aktuell ?? 0
const fernkampf = this.#findEquipmentOnSlot("fernkampf", 0)
const links = this.#findEquipmentOnSlot("links", 0)
const rechts = this.#findEquipmentOnSlot("rechts", 0)
const fernkampf = this.#findEquipmentOnSlot("fernkampf", context.actor.system.setEquipped)
const links = this.#findEquipmentOnSlot("links", context.actor.system.setEquipped)
const rechts = this.#findEquipmentOnSlot("rechts", context.actor.system.setEquipped)
context.attacks = [];
if (fernkampf) {
@ -342,16 +387,24 @@ export class CharacterSheet extends ActorSheet {
context.carryingweight = 0;
Object.values(actorData.items).forEach((item, index) => {
if (item.type === "Equipment") {
// worn items are halved weight
let effectiveWeight = item.system.weight ?? 0
if (this.#isWorn(item._id)) {
effectiveWeight = item.system.weight ? item.system.weight / 2 : 0
}
context.equipments.push({
index: index,
id: item._id,
quantity: item.system.quantity,
name: item.name,
icon: item.img ?? "",
weight: item.system.weight ?? 0,
worn: this.#isWorn(item._id, 0)
weight: item.system.weight,
worn: this.#isWorn(item._id)
})
context.carryingweight += item.system.quantity * item.system.weight;
context.carryingweight += item.system.quantity * effectiveWeight;
}
})
context.maxcarryingcapacity = actorData.system.attribute.kk.aktuell
@ -544,22 +597,23 @@ export class CharacterSheet extends ActorSheet {
}
}
#getEquipmentset(setId) {
const equipmentSet = this.object.system.heldenausruestung[setId]
#mapAllSets() {
const updateObject = {}
// TODO: there's got to be a better angle!
updateObject[`system.heldenausruestung.${setId}.links`] = equipmentSet.links;
updateObject[`system.heldenausruestung.${setId}.rechts`] = equipmentSet.rechts;
updateObject[`system.heldenausruestung.${setId}.brust`] = equipmentSet.brust;
updateObject[`system.heldenausruestung.${setId}.bauch`] = equipmentSet.bauch;
updateObject[`system.heldenausruestung.${setId}.ruecken`] = equipmentSet.ruecken;
updateObject[`system.heldenausruestung.${setId}.kopf`] = equipmentSet.kopf;
updateObject[`system.heldenausruestung.${setId}.fernkampf`] = equipmentSet.fernkampf;
updateObject[`system.heldenausruestung.${setId}.munition`] = equipmentSet.munition;
updateObject[`system.heldenausruestung.${setId}.armlinks`] = equipmentSet.armlinks;
updateObject[`system.heldenausruestung.${setId}.armrechts`] = equipmentSet.armrechts;
updateObject[`system.heldenausruestung.${setId}.beinlinks`] = equipmentSet.beinlinks;
updateObject[`system.heldenausruestung.${setId}.beinrechts`] = equipmentSet.beinrechts;
Array.from(this.object.system.heldenausruestung).forEach((equipmentSet, index) => {
updateObject[`system.heldenausruestung.${index}.links`] = equipmentSet.links;
updateObject[`system.heldenausruestung.${index}.rechts`] = equipmentSet.rechts;
updateObject[`system.heldenausruestung.${index}.brust`] = equipmentSet.brust;
updateObject[`system.heldenausruestung.${index}.bauch`] = equipmentSet.bauch;
updateObject[`system.heldenausruestung.${index}.ruecken`] = equipmentSet.ruecken;
updateObject[`system.heldenausruestung.${index}.kopf`] = equipmentSet.kopf;
updateObject[`system.heldenausruestung.${index}.fernkampf`] = equipmentSet.fernkampf;
updateObject[`system.heldenausruestung.${index}.munition`] = equipmentSet.munition;
updateObject[`system.heldenausruestung.${index}.armlinks`] = equipmentSet.armlinks;
updateObject[`system.heldenausruestung.${index}.armrechts`] = equipmentSet.armrechts;
updateObject[`system.heldenausruestung.${index}.beinlinks`] = equipmentSet.beinlinks;
updateObject[`system.heldenausruestung.${index}.beinrechts`] = equipmentSet.beinrechts;
})
return updateObject;
}
@ -696,6 +750,20 @@ export class CharacterSheet extends ActorSheet {
}
}
async #handleDroppedActiveEffect(actor, activeEffect) {
const array = Array.from(actor.items);
for (let i = 0; i < array.length; i++) {
if (array[i].name === activeEffect.name) {
// replace active effect
actor.deleteEmbeddedDocuments('Item', [array[i].id]).then(() => {
console.log("await")
})
return true;
}
}
}
#handleDroppedAdvantage(actor, advantage) {
const array = Array.from(actor.items);
for (let i = 0; i < array.length; i++) {
@ -711,7 +779,7 @@ export class CharacterSheet extends ActorSheet {
const tabs = new Tabs({
navSelector: ".paperdoll-tabs.tabs",
contentSelector: ".sheet-body.paperdoll-sets",
initial: "set1"
initial: "set" + (this.object.system.setEquipped + 1)
});
tabs.bind(html[0]);
@ -719,6 +787,12 @@ export class CharacterSheet extends ActorSheet {
this._onAttributeRoll(evt);
});
html.on('click', '[data-operation="switchSet"]', (evt) => {
const {id} = evt.currentTarget.dataset;
console.log(id);
this.object.update({"system.setEquipped": id})
})
html.on('click', '.talent.rollable', (evt) => {
this._onTalentRoll(evt);
});
@ -767,7 +841,8 @@ export class CharacterSheet extends ActorSheet {
if (actor === this.object._id && documentId) { // managing equipped items
//const slot = this.#isWorn(documentId, setId)
const updateObject = this.#getEquipmentset(setId)
//const updateObject = await this.#getEquipmentset(Number(setId))
const updateObject = this.#mapAllSets()
updateObject[`system.heldenausruestung.${setId}.${target}`] = documentId;
console.log(updateObject);
@ -828,7 +903,7 @@ export class CharacterSheet extends ActorSheet {
callback: (event) => {
const {setId, target, actor} = event[0].dataset
const updateObject = this.#getEquipmentset(setId)
const updateObject = this.#mapAllSets()
updateObject[`system.heldenausruestung.${setId}.${target}`] = null;
this.object.update(updateObject);
@ -910,4 +985,13 @@ export class CharacterSheet extends ActorSheet {
}
}
#handleDroppedSpecialAbility(actor, specialAbility) {
const array = Array.from(actor.items);
for (let i = 0; i < array.length; i++) {
if (array[i].name === specialAbility.name) { // TODO: allow multiple miracles with the same name
return false;
}
}
}
}

View File

@ -72,10 +72,10 @@ export class CreatureSheet extends foundry.appv1.sheets.ActorSheet {
})
html.on('click', '.editor .add-attack', async (evt) => {
const name = evt.target.parentElement.querySelector('#attack_name').value
const at = evt.target.parentElement.querySelector('#attack_at').value
const pa = evt.target.parentElement.querySelector('#attack_pa').value
const tp = evt.target.parentElement.querySelector('#attack_tp').value
const name = html[0].querySelector('#attack_name').value
const at = html[0].querySelector('#attack_at').value
const pa = html[0].querySelector('#attack_pa').value
const tp = html[0].querySelector('#attack_tp').value
const newAttack = {
name,

View File

@ -42,7 +42,9 @@ export class LiturgySheet extends ItemSheet {
super.activateListeners(html);
// Everything below here is only needed if the sheet is editable
if (!this.isEditable)
if (this.isEditable) {
}
}
}

View File

@ -0,0 +1,51 @@
export class SpecialAbilitySheet extends ItemSheet {
/**@override */
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ['dsa41', 'sheet', 'item', 'specialability'],
width: 520,
height: 480,
tabs: [
{
navSelector: '.sheet-tabs',
contentSelector: '.sheet-body',
initial: 'description',
},
],
});
}
/** @override */
get template() {
return `systems/DSA_4-1/templates/item/item-special-ability-sheet.hbs`;
}
/** @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.
const advantageData = context.data;
// Add the actor's data to context.data for easier access, as well as flags.
context.system = advantageData.system;
context.flags = advantageData.flags;
context.json = JSON.stringify(advantageData.system, null, 4);
return context;
}
activateListeners(html) {
super.activateListeners(html);
// Everything below here is only needed if the sheet is editable
if (!this.isEditable) {
}
}
}

View File

@ -34,7 +34,13 @@ export class VornachteilSheet extends ItemSheet {
// Add the actor's data to context.data for easier access, as well as flags.
context.system = advantageData.system;
context.flags = advantageData.flags;
context.json = JSON.stringify(advantageData.system, null, 4);
context.choices = {}
context.system.auswahl.forEach(a => {
context.choices[a] = a
})
context.hasChoices = context.system.auswahl.length > 0;
context.hasModality = context.system.value == null
return context;
}
@ -43,7 +49,7 @@ export class VornachteilSheet extends ItemSheet {
super.activateListeners(html);
// Everything below here is only needed if the sheet is editable
if (!this.isEditable) return;
if (!this.isEditable)
}
}
}

View File

@ -251,16 +251,14 @@ function mapRawJson(actor, rawJson) {
held.basis.rasse.aussehen.aussehentext0,
held.basis.rasse.aussehen.aussehentext1,
held.basis.rasse.aussehen.aussehentext2,
held.basis.rasse.aussehen.aussehentext3,
]
held.basis.rasse.aussehen.aussehentext3].join('\n')
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,
]
held.basis.rasse.aussehen.familietext5].join('\n')
json.meta.titel = held.basis.rasse.aussehen.titel
json.meta.stand = held.basis.rasse.aussehen.stand
let attributes = held.eigenschaften.eigenschaft

View File

@ -0,0 +1,38 @@
.active-effect {
display: flex;
flex-direction: column;
.header {
flex: 0;
display: grid;
grid-template-columns: 48px 1fr 48px;
grid-template-rows: 48px;
gap: 8px;
input {
height: 48px;
line-height: 48px;
}
}
.meta {
flex: 1;
display: flex;
flex-direction: column;
margin-top: 8px;
label {
flex: 0;
}
div.editor {
flex: 1;
border: 1px inset #ccc;
background-color: rgba(0, 0, 0, 0.2)
}
}
}

View File

@ -0,0 +1,48 @@
.dsa41.sheet.item.advantage {
.sheet-body {
padding: 8px;
}
.tab.advantage.active {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 48px 1fr;
gap: 8px;
height: 100%;
div {
label {
width: 100%;
display: inline-block;
}
input {
width: 100%;
display: inline-block;
}
}
.body {
grid-column: 1/3;
display: flex;
flex-direction: column;
height: 100%;
label {
flex: 0;
}
.editor {
flex: 1;
border: 1px inset #ccc;
background-color: rgba(0, 0, 0, 0.2);
}
}
}
}

View File

@ -57,6 +57,188 @@
overflow: auto;
}
.tab.overview.active {
display: flex;
flex-direction: column;
height: 100%;
.meta-data {
flex: 0;
columns: 2;
gap: 0 16px;
div {
break-inside: avoid;
padding: 4px 0;
}
.double {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-areas: 'label label' 'left right';
gap: 0 8px;
label {
grid-area: label;
}
}
.editor {
background-color: rgba(0, 0, 0, 0.2)
}
& + .meta-data {
flex: 1;
}
}
.meta-data.html {
& > div {
display: flex;
flex-direction: column;
height: 100%;
label {
flex: 0;
}
.editor {
flex: 1;
}
}
}
}
.tab.attributes.active {
height: 100%;
.attribute {
padding: 8px 0;
display: flex;
gap: 0 8px;
label {
width: 120px;
text-align: right;
vertical-align: middle;
line-height: 24px;
}
input {
max-width: 80px;
text-align: right;
}
.mod {
color: grey;
text-shadow: 0 -1px 0 #ccc;
box-Shadow: 1px 1px 1px rgba(0, 0, 0, 0.5);
border-radius: 4px;
height: 24px;
width: 24px;
background: linear-gradient(0deg, rgba(24, 24, 24, 1) 0%, rgba(80, 80, 80, 1) 100%);;
display: inline-block;
text-align: center;
vertical-align: middle;
line-height: 24px;
margin-left: 4px;
}
}
.attributes-overview {
columns: 2;
gap: 0 16px;
}
.resource-overview {
.attribute {
}
}
.advantages, .special-abilities {
margin-bottom: 16px;
ul {
list-style-type: none;
padding: 0;
margin: 0;
text-indent: 0;
li {
display: inline-block;
}
.advantage, .special-ability {
position: relative;
border: 1px solid gold;
box-shadow: 2px 2px 4px #000;
border-radius: 8px;
height: 24px;
color: gold;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.2);
display: inline-block;
padding: 0 8px;
margin-left: 0;
margin-bottom: 4px;
background-image: url("../assets/velvet_button.png");
background-repeat: repeat-y;
background-size: cover;
span {
position: relative;
z-index: 2;
line-height: 24px;
vertical-align: middle;
}
&.special-ability {
&::after {
background: rgba(128, 0, 96, 0.5);
}
}
&::after {
content: "";
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
border-radius: 8px;
background: rgba(0, 128, 0, 0.5);
}
& + .advantage, & + .special-ability {
margin-left: 8px;
}
&.nachteil {
font-style: italic;
&::after {
background: rgba(128, 0, 0, 0.5);
}
}
}
}
}
}
.backpack.active {
padding: 8px;
display: grid;

View File

@ -6,7 +6,6 @@
.sheet-body {
position: relative;
top: 5px;
.tab.active {
padding: 4px;

View File

@ -51,6 +51,8 @@
line-height: 24px;
vertical-align: middle;
text-indent: 8px;
color: white;
text-shadow: 2px 2px 1px rgba(0, 0, 0, 0.3);
}
span.resource-fill {
@ -58,9 +60,36 @@
left: 0;
top: 0;
bottom: 0;
background: linear-gradient(to bottom, #0bad29 0%, #11f128 50%, #0cde24 51%, #6ff77b 100%);
background: url('../assets/gradient.png');
background-size: 32px 100%;
&::after {
content: '';
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
&.lep::after {
background-color: rgba(0, 192, 96, 0.5);
}
&.aup::after {
background-color: rgba(192, 166, 0, 0.5);
}
&.asp::after {
background-color: rgba(0, 26, 192, 0.5);
}
&.kap::after {
background-color: rgba(128, 0, 192, 0.5);
}
}
}
}

View File

@ -0,0 +1,51 @@
.sheet.item.skill {
.tab.meta.active {
display: grid;
grid-template-areas:
"category category category"
"taw statistics ebe"
"language language language"
"attack attack attack";
gap: 8px;
margin: 8px;
.category {
grid-area: category;
}
.taw {
grid-area: taw;
}
.statistics {
grid-area: statistics;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: auto 1fr;
.statistics > label {
grid-column: 1/4;
text-align: center;
}
}
.ebe {
grid-area: ebe;
}
.language-statistics {
grid-area: language;
}
.attack-statistics {
grid-area: attack;
display: flex;
flex-direction: row;
}
}
}

View File

@ -11,3 +11,6 @@
@use "_player-action";
@use "_modify-liturgy";
@use "_liturgy-banner";
@use "_talent-sheet";
@use "_active-effect-sheet";
@use "_advantage-sheet";

View File

@ -113,7 +113,8 @@
],
"arrayFields": [
"talente",
"zauber"
"zauber",
"heldenausruestung"
]
}
},
@ -163,6 +164,14 @@
"maxLEP",
"maxMR"
]
},
"SpecialAbility": {
"stringFields": [
"name"
]
},
"ActiveEffect": {
},
"Skill": {
"stringFields": [

View File

@ -13,14 +13,30 @@
</header>
<div class="head-data">
<h1 class="charname"><input name="name" type="text" value="{{actor.name}}" placeholder="Name"/></h1>
<img class="profile-img" src="{{actor.img}}" data-edit="img" title="{{actor.name}}"/>
<h1 class="charname {{#if owner}}secret-identity{{/if}}" {{#if owner}}title="{{originalName}}"{{/if}} ><input
name="name" type="text" value="{{name}}" placeholder="Name"/></h1>
<img class="profile-img" src="{{actor.img}}" data-edit="img" title="{{name}}"/>
<h2 class="sidebar-element header">Kampf Daten</h2>
<div class="sidebar-element resource-bar">
<label>LeP: {{this.lep}}</label><span class="resource-fill" style="width: {{this.lepper}}%"></span></div>
<label>LeP: {{this.lep}}</label><span class="resource-fill lep" style="width: {{this.lepper}}%"></span>
</div>
<div class="sidebar-element resource-bar">
<label>AuP: {{this.aup}}</label><span class="resource-fill" style="width: {{this.aupper}}%"></span></div>
<label>AuP: {{this.aup}}</label><span class="resource-fill aup" style="width: {{this.aupper}}%"></span>
</div>
{{#if this.hasLiturgies}}
<div class="sidebar-element resource-bar">
<label>KE: {{this.ke}}</label><span class="resource-fill kap" style="width: {{this.keper}}%"></span>
</div>
{{/if}}
{{#if this.hasSpells}}
<div class="sidebar-element resource-bar">
<label>AsP: {{this.asp}}</label><span class="resource-fill asp" style="width: {{this.aspper}}%"></span>
</div>
{{/if}}
{{#each attacks}}
<div>
@ -64,26 +80,176 @@
{{!-- Sheet Body --}}
<section class="sheet-body">
<div class="tab overview" data-group="primary" data-tab="overview">
<div><label>Spezies
<input type="text" name="system.meta.spezies.value" value="{{system.meta.spezies}}"/>
</label>
<div class="meta-data">
<div><label>Spezies
<input type="text" name="system.meta.spezies" value="{{system.meta.spezies}}"/>
</label>
</div>
<div><label for="system.meta.kultur">Kultur</label>
<input type="text" name="system.meta.kultur"
value="{{system.meta.kultur}}"/>
</div>
<div><label for="system.meta.profession">Profession</label>
<input type="text" name="system.meta.profession"
value="{{system.meta.profession}}"/>
</div>
<div><label for="system.meta.geschlecht">Geschlecht</label>
<input type="text" name="system.meta.geschlecht"
value="{{system.meta.geschlecht}}"/>
</div>
<div class="double"><label>Sozialstatus</label>
<input type="text" name="system.meta.stand" value="{{system.meta.stand}}"/>
<input type="text" name="system.meta.titel" value="{{system.meta.titel}}"/>
</div>
<div><label for="system.meta.groesse">Größe</label>
<input type="number" name="system.meta.groesse" value="{{system.meta.groesse}}"/>
</div>
<div><label for="system.meta.gewicht">Gewicht</label>
<input type="number" name="system.meta.gewicht" value="{{system.meta.gewicht}}"/>
</div>
<div class="double"><label>Alter</label>
<input type="number" name="system.meta.groesse" value="{{system.meta.alter}}"/>
<input type="text" name="system.meta.geburtsdatum" value="{{system.meta.geburtstag}}"/>
</div>
</div>
<div><label>Kultur<input type="text" name="system.meta.kultur.value"
value="{{system.meta.kultur}}"/></label>
</div>
<div><label>Profession<input type="text" name="system.meta.profession.value"
value="{{system.meta.profession}}"/></label>
</div>
<div><label>Geschlecht<input type="text" name="system.meta.geschlecht.value"
value="{{system.meta.geschlecht}}"/></label>
<div class="meta-data html">
<div><label>Aussehen</label>
{{editor system.meta.aussehen target="system.meta.aussehen" button=true owner=owner
editable=editable}}
</div>
<div><label>Familie</label>
{{editor system.meta.familie target="system.meta.familie" button=true owner=owner
editable=editable}}
</div>
</div>
</div>
<div class="tab attributes" data-group="primary" data-tab="attributes">
<div class="attributes-overview">
<div class="attribute">
<label>Mut</label>
<input value="{{this.system.attribute.mu.aktuell}}">
<div class="mods">
{{#each this.mods.mu}}
<span class="mod" title="{{this.name}}">{{this.value}}</span>
{{/each}}
</div>
</div>
<div class="attribute">
<label>Klugheit</label>
<input value="{{this.system.attribute.kl.aktuell}}">
<div class="mods">
{{#each this.mods.kl}}
<span class="mod" title="{{this.name}}">{{this.value}}</span>
{{/each}}
</div>
</div>
<div class="attribute">
<label>Intuition</label>
<input value="{{this.system.attribute.in.aktuell}}">
<div class="mods">
{{#each this.mods.in}}
<span class="mod" title="{{this.name}}">{{this.value}}</span>
{{/each}}
</div>
</div>
<div class="attribute">
<label>Charisma</label>
<input value="{{this.system.attribute.ch.aktuell}}">
<div class="mods">
{{#each this.mods.ch}}
<span class="mod" title="{{this.name}}">{{this.value}}</span>
{{/each}}
</div>
</div>
<div class="attribute">
<label>Fingerfertigkeit</label>
<input value="{{this.system.attribute.ff.aktuell}}">
<div class="mods">
{{#each this.mods.ff}}
<span class="mod" title="{{this.name}}">{{this.value}}</span>
{{/each}}
</div>
</div>
<div class="attribute">
<label>Geschicklichkeit</label>
<input value="{{this.system.attribute.ge.aktuell}}">
<div class="mods">
{{#each this.mods.ge}}
<span class="mod" title="{{this.name}}">{{this.value}}</span>
{{/each}}
</div>
</div>
<div class="attribute">
<label>Konstitution</label>
<input value="{{this.system.attribute.ko.aktuell}}">
<div class="mods">
{{#each this.mods.ko}}
<span class="mod" title="{{this.name}}">{{this.value}}</span>
{{/each}}
</div>
</div>
<div class="attribute">
<label>Körperkraft</label>
<input value="{{this.system.attribute.kk.aktuell}}">
<div class="mods">
{{#each this.mods.kk}}
<span class="mod" title="{{this.name}}">{{this.value}}</span>
{{/each}}
</div>
</div>
<div class="attribute">
<label>Sozialstatus</label>
<input value="{{this.system.attribute.so.aktuell}}">
</div>
</div>
<div class="resources-overview">
<div class="attribute">
<label>Lebensenergie</label>
<input value="{{actor.system.lep.aktuell}}">
<input value="{{actor.system.lep.max}}">
</div>
<div class="attribute">
<label>Ausdauer</label>
<input value="{{actor.system.aup.aktuell}}">
<input value="{{actor.system.aup.max}}">
</div>
<div class="attribute">
<label>Astralenergie</label>
<input value="{{actor.system.asp.aktuell}}">
<input value="{{actor.system.asp.max}}">
</div>
<div class="attribute">
<label>Karmaenergie</label>
<input value="{{actor.system.kap.aktuell}}">
<input value="{{actor.system.kap.max}}">
</div>
</div>
<div class="advantages">
<h3>Vor- und Nachteile</h3>
<ul>
{{#each this.advantages}}
<li>{{> "systems/DSA_4-1/templates/ui/partial-advantage-button.hbs" this}}</li>
{{/each}}
</ul>
</div>
<div class="special-abilities">
<h3>Sonderfertigkeiten</h3>
<ul>
{{#each this.specialAbilities}}
<li>{{> "systems/DSA_4-1/templates/ui/partial-sf-button.hbs" this}}</li>
{{/each}}
</ul>
</div>
</div>
<div class="tab combat" data-group="primary" data-tab="combat">
@ -112,6 +278,12 @@
<label>RS:</label>
{{derived.rs}}
</div>
<div class="handicap">
<label>BE:</label>
{{derived.be}}
</div>
</div>
<div class="actions">
@ -250,7 +422,11 @@
data-target="{{this.target}}" data-actor="{{../../actor.id}}"><img
src="{{this.icon}}"/></div>
{{/each}}
{{#if (eq ../actor.system.setEquipped @index)}}
<button disabled="disabled">Ausgerüstet</button>
{{else}}
<button data-operation="switchSet" data-id="{{@index}}">Wechseln</button>
{{/if}}
</div>
</div>
@ -334,9 +510,29 @@
</tr>
</thead>
<tbody>
{{#if this.countI}}
{{#if this.countO}}
<tr>
<th rowspan="{{this.total}}" class="background"></th>
<th class="banner-mid" rowspan="{{countO}}">
<div>
<div class="rank-label">0</div>
</div>
</th>
</tr>
{{#each this.O}}
<tr>
<td class="liturgy rollable" data-id="{{this.id}}" data-rank="{{this.rank}}"
data-lkp="{{../lkp}}" data-deity="{{this.deity}}">
{{> 'systems/DSA_4-1/templates/ui/partial-die.hbs' }}
</td>
<td class="clickable" data-id="{{this.id}}" data-operation="openActorSheet">
{{this.name}}</td>
<td></td>
</tr>
{{/each}}
{{/if}}
{{#if this.countI}}
<tr>
<th class="banner-mid" rowspan="{{countI}}">
<div>
<div class="rank-label">I</div>
@ -491,8 +687,10 @@
</div>
{{/if}}
{{#if this.hasPets}}
<div class="tab pets" data-group="primary" data-tab="pets">
</div>
{{/if}}
</section>
</form>

View File

@ -0,0 +1,20 @@
<form class="{{cssClass}} {{item.type}} flexcol" autocomplete="off">
<div class="active-effect">
<div class="header">
<img src="{{item.img}}" data-edit="img" title="{{item.name}}"/>
<input type="text" name="actor.name" value="{{item.name}}"/>
<button class="editEffects" data-id="{{this.effectId}}" data-operation="editActiveEffect">
<i class="fas fa-pencil-alt"></i>
</button>
</div>
<div class="meta">
<label>Spielleiter Hinweise</label>
{{editor item.system.notes target="system.notes" button=true owner=owner editable=editable}}
</div>
</div>
</form>

View File

@ -2,13 +2,34 @@
{{!-- Sheet Tab Navigation --}}
<nav class="sheet-tabs tabs" style="flex: 0" data-group="primary">
<a class="item" data-tab="json">JSON</a>
<a class="item" data-tab="advantage">Vorteil</a>
</nav>
{{!-- Sheet Body --}}
<section class="sheet-body" style="flex: 1">
<div class="tab json" data-group="primary" data-tab="json">
<pre style="overflow: auto; white-space: normal; position: relative; top: 8px; bottom: 8px; left: 8px; right: 8px">{{json}}</pre>
<div class="tab advantage {{#if hasModality}}modality{{/if}}" data-group="primary" data-tab="advantage">
<div>
<label>Name</label>
<input name="item.name" value="{{item.name}}"/>
</div>
{{#if hasModality}}
<div>
<label>Auswahl</label>
{{#if hasChoices}}
<select name="system.gruppe">
{{selectOptions choices selected=system.auswahl inverted=true}}
</select>
{{else}}
<input name="actor.system.value" value="{{actor.system.value}}"/>
{{/if}}
</div>
{{/if}}
<div class="body">
<label>Beschreibung</label>
{{editor system.description target="system.description" button=true owner=owner editable=editable}}
</div>
</div>
</section>
</form>

View File

@ -10,57 +10,62 @@
<section class="sheet-body" style="flex: 1">
<div class="tab meta" data-group="primary" data-tab="meta">
<div>
<div class="category">
<label>Kategorie
<select name="system.gruppe">
{{selectOptions categoryOptions selected=system.gruppe inverted=true}}
</select>
</label>
</div>
<div>
<div class="taw">
<label>TAW:
<input type="text" name="system.taw"
value="{{system.taw}}"/>
</label>
</div>
<div>
<label>Erstes Attribut
<input type="text" name="system.probe.0"
value="{{system.probe.[0]}}"/>
</label>
<div class="statistics">
<label>Probe</label>
<div>
<label>
<input type="text" name="system.probe.0"
value="{{system.probe.[0]}}"/>
</label>
</div>
<div><label>
<input type="text" name="system.probe.1"
value="{{system.probe.[1]}}"/>
</label></div>
<div>
<label>
<input type="text" name="system.probe.2"
value="{{system.probe.[2]}}"/>
</label>
</div>
</div>
<div><label>Zweites Attribut
<input type="text" name="system.probe.1"
value="{{system.probe.[1]}}"/>
</label></div>
<div>
<label>Drittes Attribut
<input type="text" name="system.probe.2"
value="{{system.probe.[2]}}"/>
</label>
</div>
<div>
<label>Behinderung
<div class="ebe">
<label>Effektive Behinderung
<input type="text" name="system.behinderung" value="{{system.behinderung}}"/>
</label>
</div>
<div>
<div class="language-statistics">
<label>Sprachenkomplexizität
<input type="text" name="system.komplexität" value="{{system.komplexität}}"/>
</label>
</div>
<div>
<label>Attacke
<input type="text" name="system.at" value="{{system.at}}"/>
</label>
<div class="attack-statistics">
<div>
<label>Attacke
<input type="text" name="system.at" value="{{system.at}}"/>
</label>
</div>
<div>
<label>Parade
<input type="text" name="system.pa" value="{{system.pa}}"/>
</label>
</div>
</div>
<div>
<label>Parade
<input type="text" name="system.pa" value="{{system.pa}}"/>
</label>
</div>
</div>
<div class="tab description" data-group="primary" data-tab="description">

View File

@ -15,7 +15,7 @@
<td class="icon"><img alt="" src="{{this.icon}}" width="16" height="16"></td>
<td class="name">{{this.name}}</td>
<td class="quantity">{{this.quantity}}</td>
<td class="weight">{{this.weight}}</td>
<td class="weight">{{#if this.worn}}({{/if}}{{this.weight}}{{#if this.worn}}){{/if}}</td>
</tr>
{{/each}}
</tbody>

View File

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