diff --git a/package-lock.json b/package-lock.json index 653f0089..f782fa7b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "gulp-json-modify": "^1.0.2" }, "devDependencies": { - "@foundryvtt/foundryvtt-cli": "^3.0.0", + "@foundryvtt/foundryvtt-cli": "^3.0.2", "cb": "^0.1.1", "del": "^8.0.1", "fvtt-types": "npm:@league-of-foundry-developers/foundry-vtt-types@^13.346.0-beta.20250812191140", @@ -209,9 +209,9 @@ } }, "node_modules/@foundryvtt/foundryvtt-cli": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@foundryvtt/foundryvtt-cli/-/foundryvtt-cli-3.0.0.tgz", - "integrity": "sha512-OiF4HtnYg5An1ivVxB68mOj5LO5gMHd4uHmC5nWdD8IYxpK0pSYw3t+cHrUYDp+Tic78uwFuHxLyc+ZNeZXulA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@foundryvtt/foundryvtt-cli/-/foundryvtt-cli-3.0.2.tgz", + "integrity": "sha512-coh4Cf4FD/GHxk2QMsd+3wLMivNeih4rfkbZy8CaYjdlpo6iciFQwxLqznZWtn+5p06zekvS2xLUF55NnbXQDw==", "dev": true, "dependencies": { "chalk": "^5.4.1", diff --git a/package.json b/package.json index 5d99859c..3071030a 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "installToFoundry": "node installToFoundry.mjs" }, "devDependencies": { - "@foundryvtt/foundryvtt-cli": "^3.0.0", + "@foundryvtt/foundryvtt-cli": "^3.0.2", "cb": "^0.1.1", "del": "^8.0.1", "fvtt-types": "npm:@league-of-foundry-developers/foundry-vtt-types@^13.346.0-beta.20250812191140", diff --git a/src/lang/de.json b/src/lang/de.json index a503b8d3..164dc25c 100644 --- a/src/lang/de.json +++ b/src/lang/de.json @@ -1,22 +1,50 @@ { - "TYPES": { - "Actor": { - "Character": "Held", - "Creature": "Kreatur", - "Group": "Heldengruppe", - "Merchant": "Händler" - }, - "Item": { - "ActiveEffect": "Aktiver Effekt", - "Equipment": "Ausrüstungsgegenstand", - "Skill": "Talent", - "Advantage": "Vor-/Nachteil", - "SpecialAbility": "Sonderfertigkeit", - "Spell": "Zauber", - "Liturgy": "Liturgie", - "Species": "Spezies", - "Culture": "Kultur", - "Profession": "Profession" - } + "TYPES": { + "Actor": { + "Character": "Held", + "Creature": "Kreatur", + "Group": "Heldengruppe", + "Merchant": "Händler" + }, + "Item": { + "ActiveEffect": "Aktiver Effekt", + "Equipment": "Ausrüstungsgegenstand", + "Skill": "Talent", + "Advantage": "Vor-/Nachteil", + "SpecialAbility": "Sonderfertigkeit", + "Spell": "Zauber", + "Liturgy": "Liturgie", + "Species": "Spezies", + "Culture": "Kultur", + "Profession": "Profession" } + }, + "COOLDOWN": { + "cancel": "{t} abbrechen" + }, + "WEAPON": { + "attack": "Mit {weapon} angreifen", + "parry": "Mit {weapon} parrieren", + "damage": "Mit {weapon} schaden machen", + "initiative": "Initiative würfeln" + }, + "COMBAT_DIALOG": { + "notReadyReason": { + "title": "Angriff kann aus folgenden Gründen nicht ausgeführt werden:", + "noTarget": "Kein Ziel ausgewählt", + "noWeapon": "Keine Waffe ausgewählt", + "noSkill": "Kein Waffentalent ausgewählt", + "noManeuver": "Kein Manöver ausgewählt", + "impossible": "Erschwernis zu hoch für Talentwert" + } + }, + "COMBAT_DIALOG_TP": { + "windowTitle": "Schaden Würfeln", + "regularFormula": "Schadensformel:", + "bonusDamage": "Zusätzlicher Schaden:", + "buttonText": "Würfeln" + }, + "ITEM_BROWSER": { + "progress": "{current}/{max}: Importiere von {compendium}" + } } diff --git a/src/main.mjs b/src/main.mjs index 09d2c24a..f0da9309 100644 --- a/src/main.mjs +++ b/src/main.mjs @@ -2,14 +2,23 @@ import {XmlImportDialog} from "./module/dialog/xmlImportDialog.mjs"; import {initGlobalSettings, initUserSettings} from "./module/settings/global-settings.mjs"; import {setUpActorSheets, setUpItemSheets} from "./module/setup/sheets.mjs"; import {loadPartials} from "./module/setup/partials.mjs"; -import {initCombat, initDataModels, initDocumentClasses, initGlobalAccess} from "./module/setup/config.mjs"; +import { + initSocketLib, + initCombat, + initDataModels, + initDocumentClasses, + initGlobalAccess +} from "./module/setup/config.mjs"; import {initHandlebarHelpers} from "./module/handlebar-helpers/index.mjs"; Hooks.once("init", () => { console.log("DSA 4.1 is ready for development!") - game.DSA41 = initGlobalAccess() + game.DSA41 = { + ...game.DSA41, + ...initGlobalAccess() + } initDocumentClasses(CONFIG) initUserSettings(game.settings) @@ -27,6 +36,9 @@ Hooks.once("init", () => { initHandlebarHelpers(Handlebars) }) +game.DSA41 = {} +initSocketLib(game.DSA41) + Hooks.on("getActorContextOptions", (application, menuItems) => { menuItems.push({ name: "Import from XML", diff --git a/src/module/data/character.mjs b/src/module/data/character.mjs index 8297bd7d..c571c246 100644 --- a/src/module/data/character.mjs +++ b/src/module/data/character.mjs @@ -138,6 +138,10 @@ export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel { key: new StringField(), notiz: new StringField(), })), + erschoepfung: new SchemaField({ // only with DSA_4-1.optional_erschoepfung + max: new NumberField({required: true, integer: true}), + aktuell: new NumberField({required: true, integer: true}), + }), wunden: new SchemaField({ aktuell: new NumberField({required: true, integer: true}), // only with DSA_4-1.optional_trefferzonen = false max: new NumberField({required: true, integer: true}), // only with DSA_4-1.optional_trefferzonen = false diff --git a/src/module/dialog/combatAction.mjs b/src/module/dialog/combatAction.mjs index 87d9c7f4..05eaae06 100644 --- a/src/module/dialog/combatAction.mjs +++ b/src/module/dialog/combatAction.mjs @@ -50,12 +50,12 @@ export class CombatActionDialog extends HandlebarsApplicationMixin(ApplicationV2 */ _actor = null - constructor(actor) { + constructor(actor, data) { super(); this._actor = actor this._targetId = null - this._skillId = null - this._weaponId = null + this._skillId = data.skill ? data.skill : null + this._weaponId = data.weapon ? data.weapon : null this._defenseManeuverId = null this._actionManager = new ActionManager(this._actor) CombatActionDialog._instance = this @@ -126,7 +126,7 @@ export class CombatActionDialog extends HandlebarsApplicationMixin(ApplicationV2 /** @type Cooldown */ const newCooldown = { start: maneuver.cooldown({weapon, skill, target, mod: this._mod}), - current: maneuver.cooldown({weapon, skill, target, mod: this._mod}), + current: 0, data: { cssClass: "Kampf", weapon: this._weaponId, @@ -288,9 +288,9 @@ export class CombatActionDialog extends HandlebarsApplicationMixin(ApplicationV2 const context = await super._prepareContext(options) context.actor = this._actor - context.distanceUnit = game.scenes.current.grid.units + context.distanceUnit = game.scenes.current?.grid.units - if (this._actor.getActiveTokens()[0]?.id) { + if (context.distanceUnit && this._actor.getActiveTokens()[0]?.id) { context.tokenDistances = this.#evaluateDistances() context.weapons = this.#evaluateWeapons() @@ -307,7 +307,28 @@ export class CombatActionDialog extends HandlebarsApplicationMixin(ApplicationV2 // TODO get W/M of weapon NOW - context.ready = this._targetId && this._weaponId && this._skillId && this._defenseManeuverId + if (this._targetNumber >= 0 && this._targetId && this._weaponId && this._skillId && maneuver) { + context.ready = true + } else { + context.notReadyReason = `${game.i18n.format("COMBAT_DIALOG.notReadyReason.title")}" + context.ready = false + } return context } else { ui.notifications.error(`Feature funktioniert nur wenn der Akteur ein Token auf der aktuellen Szene hat`); @@ -335,12 +356,10 @@ export class CombatActionDialog extends HandlebarsApplicationMixin(ApplicationV2 target.textContent = `(${result})` targetDescription.textContent = this._modDescription - if (result <= 0) { - context.ready = false + if (result <= 0 || !context.ready) { this.element.querySelector(".actions button").classList.remove("ready") this.element.querySelector(".actions button").setAttribute("disabled", true) } else { - context.ready = true this.element.querySelector(".actions button").classList.add("ready") this.element.querySelector(".actions button").removeAttribute("disabled") } diff --git a/src/module/dialog/defenseAction.mjs b/src/module/dialog/defenseAction.mjs index 6d4db78e..5e136d90 100644 --- a/src/module/dialog/defenseAction.mjs +++ b/src/module/dialog/defenseAction.mjs @@ -49,16 +49,16 @@ export class DefenseActionDialog extends HandlebarsApplicationMixin(ApplicationV */ _actor = null - constructor(actor, attackData) { + constructor(actor, data, attackData) { super(); - this._attackData = attackData ?? { + /*this._attackData = attackData ?? { modToDefense: 0, attacker: null, weapon: null, // is important to note as weapons like Chain Weapons or Flails can ignore Shields - } + }*/ this._actor = actor - this._skillId = null - this._weaponId = null + this._skillId = data.skill ? data.skill : null + this._weaponId = data.weapon ? data.weapon : null this._defenseManeuverId = null this._actionManager = new ActionManager(this._actor) //if (this._actor) { @@ -254,7 +254,26 @@ export class DefenseActionDialog extends HandlebarsApplicationMixin(ApplicationV // TODO get W/M of weapon NOW - context.ready = this._targetId && this._weaponId && this._skillId && this._defenseManeuverId + if (this._weaponId && this._skillId && this._defenseManeuverId) { + context.ready = true + } else { + context.notReadyReason = `${game.i18n.format("COMBAT_DIALOG.notReadyReason.title")}" + context.ready = false + } + return context } else { ui.notifications.error(`Feature funktioniert nur wenn der Akteur ein Token auf der aktuellen Szene hat`); @@ -282,12 +301,10 @@ export class DefenseActionDialog extends HandlebarsApplicationMixin(ApplicationV target.textContent = `(${result})` targetDescription.textContent = this._modDescription - if (result <= 0) { - context.ready = false + if (result <= 0 || !context.ready) { this.element.querySelector(".actions button").classList.remove("ready") this.element.querySelector(".actions button").setAttribute("disabled", true) } else { - context.ready = true this.element.querySelector(".actions button").classList.add("ready") this.element.querySelector(".actions button").removeAttribute("disabled") } diff --git a/src/module/dialog/itemBrowserDialog.mjs b/src/module/dialog/itemBrowserDialog.mjs new file mode 100644 index 00000000..21acf80c --- /dev/null +++ b/src/module/dialog/itemBrowserDialog.mjs @@ -0,0 +1,243 @@ +import {Equipment} from "../documents/equipment.mjs"; + +const { + ApplicationV2, + HandlebarsApplicationMixin +} = foundry.applications.api + +export class ItemBrowserDialog extends HandlebarsApplicationMixin(ApplicationV2) { + + static DEFAULT_OPTIONS = { + classes: ['dsa41', 'dialog', 'item-browser'], + tag: "form", + form: { + submitOnChange: true, + closeOnSubmit: false, + handler: ItemBrowserDialog.#onSubmitForm + }, + position: { + width: 640, + height: 480 + }, + window: { + resizable: true, + title: "Gegenstände Browser" + }, + actions: { + select: ItemBrowserDialog.#selectItem, + buy: ItemBrowserDialog.#buyItem + } + } + + static PARTS = { + form: { + template: 'systems/DSA_4-1/templates/dialog/item-browser-dialog.hbs', + } + } + + /** + * @type {Actor} + * @private + */ + _actor = null + /** + * + * @type {[Equipment]} + * @private + */ + _items = [] + _selectedItem = null + filter_price_lower = 0 + filter_price_upper = 0 + filter_weight_lower = 0 + filter_weight_upper = 0 + filter_name = "" + filter_category = "" + + constructor(actor) { + super(); + this._actor = actor + this._items = [] + this._selectedItem = null + } + + static async #onSubmitForm(event, form, formData) { + event.preventDefault() + this.filter_price_lower = formData.object.filter_price_lower + this.filter_price_upper = formData.object.filter_price_upper + this.filter_weight_lower = formData.object.filter_weight_lower + this.filter_weight_upper = formData.object.filter_weight_upper + this.filter_name = formData.object.filter_name + this.filter_category = formData.object.filter_category + + this.render({parts: ["form"]}) + } + + static async #selectItem(event, target) { + const {itemId} = target.dataset + const selectedItem = this._items.find(item => item.uuid === itemId) + + this._items?.forEach((item) => { + item.selected = item.uuid === itemId + }) + + if (selectedItem) { + this._selectedItem = selectedItem + } + this.render({parts: ["form"]}) + } + + static async #buyItem(event, target) { + if (this._actor && this._selectedItem) { + const canBuy = await this._actor.reduceWealth(this._selectedItem.price) + if (canBuy) { + const document = await foundry.utils.fromUuid(this._selectedItem.uuid) + if (document) { + await this._actor.createEmbeddedDocuments("Item", [document]) + ui.notifications.info(this._selectedItem.name + " wurde von " + this._actor.name + " gekauft") + } + } else { + ui.notifications.error(this._selectedItem.name + " ist zu teuer für " + this._actor.name) + } + + + } + } + + _canDragDrop(event, options) { + return game.user.isGM + } + + _canDrag(event, options) { + return true + } + + /** + * An event that occurs when a drag workflow begins for a draggable item on the sheet. + * @param {DragEvent} event The initiating drag start event + * @returns {Promise} + * @protected + */ + async _onDragStart(event) { + const target = event.currentTarget; + let dragData; + + if (target.dataset.itemId) { + dragData = { + type: "Item", + uuid: target.dataset.itemId + } + } + + // Set data transfer + if (!dragData) return; + event.dataTransfer.setData("text/plain", JSON.stringify(dragData)); + } + + async _prepareContext(options) { + const context = await super._prepareContext(options) + context.categories = { + "": "", + "Gegenstand": "Gegenstand", + "Nahkampfwaffe": "Nahkampfwaffe", + "Fernkampfwaffe": "Fernkampfwaffe", + "Munition": "Munition", + "Währung": "Währung" + } + context.filterName = this.filter_name + context.filterCategory = this.filter_category + context.filter_price_lower = this.filter_price_lower ?? this._minPrice + context.filter_price_upper = this.filter_price_upper ?? this._maxPrice + context.filter_weight_lower = this.filter_weight_lower ?? this._minWeight + context.filter_weight_upper = this.filter_weight_upper ?? this._maxWeight + context.price_lower = this._minPrice + context.price_upper = this._maxPrice + context.weight_lower = this._minWeight + context.weight_upper = this._maxWeight + context.hasSelectedItem = this._selectedItem != null + + context.items = this._items + ?.filter(p => p.name.toLowerCase().indexOf(context.filterName.toLowerCase()) !== -1 || context.filterName === "") + ?.filter(p => p.category.indexOf(context.filterCategory) !== -1 || context.filterCategory === "") + ?.filter(p => Number(context.filter_price_lower) <= p.price && p.price <= Number(context.filter_price_upper)) + ?.filter(p => Number(context.filter_weight_lower) <= p.weight && p.weight <= Number(context.filter_weight_upper)) + + + + return context + } + + #updateProgress(compendiumName, current, max) { + if (compendiumName && current && max) { + this.element.querySelector('.progress').style.display = 'block'; + this.element.querySelector('.progress .fill').style.width = (current / max * 100) + "%"; + this.element.querySelector('.progress .text').textContent = game.i18n.format("ITEM_BROWSER.progress", { + compendium: compendiumName, + current: current, + max: max + }) + + } else { + this.element.querySelector('.progress').style.display = 'none'; + } + } + + async _onRender(context, options) { + + if (this._items.length === 0) { + const compendia = [ + game.packs.get('DSA_4-1.Armor'), + game.packs.get('DSA_4-1.Weapons'), + game.packs.get('DSA_4-1.Ammunition'), + game.packs.get('DSA_4-1.Items') + ] + + let totalEntries = compendia.reduce((p, c) => p + c.index.size, 0) + let parsedEntries = 0 + let currentCompendiumName = "" + + for (const c of compendia) { + const it = await c.getDocuments() + currentCompendiumName = c.metadata.label + it.forEach((item) => { + const uuid = item.uuid + const e = new Equipment(item) + this._items.push({ + img: item.img, + uuid, + type: item.type, + name: item.name, + price: e.system.price, + weight: e.system.weight, + category: e.system.category.join(", "), + selected: false, + }) + + parsedEntries += 1 + this.#updateProgress(currentCompendiumName, parsedEntries, totalEntries) + }) + } + this._minPrice = Math.min(...this._items.map(item => item.price)) + this._maxPrice = Math.max(...this._items.map(item => item.price)) + + this._minWeight = Math.min(...this._items.map(item => item.weight)) + this._maxWeight = Math.max(...this._items.map(item => item.weight)) + + + this.#updateProgress() + this.render({parts: ["form"]}) + } + + + new foundry.applications.ux.DragDrop.implementation({ + dropSelector: ".window-content", + dragSelector: ".item", + permissions: { + drag: this._canDrag.bind(this) + }, + callbacks: { + dragstart: this._onDragStart.bind(this), + } + }).bind(this.element) + } +} \ No newline at end of file diff --git a/src/module/dialog/liturgyDialog.mjs b/src/module/dialog/liturgyDialog.mjs index 7d7764d6..23fea809 100644 --- a/src/module/dialog/liturgyDialog.mjs +++ b/src/module/dialog/liturgyDialog.mjs @@ -163,7 +163,7 @@ export class LiturgyDialog extends HandlebarsApplicationMixin(ApplicationV2) { } cooldowns.push({ start: castingTime, - current: castingTime, + current: 0, data: { cssClass: "Karmal", title: this._liturgy.name, diff --git a/src/module/dialog/restingDialog.mjs b/src/module/dialog/restingDialog.mjs index 01da6bd9..7dcc00dd 100644 --- a/src/module/dialog/restingDialog.mjs +++ b/src/module/dialog/restingDialog.mjs @@ -99,6 +99,7 @@ export class RestingDialog extends HandlebarsApplicationMixin(ApplicationV2) { lepMod: [-1, -2, -3, -4, -5], display: "range", noLabel: "Gutes Wetter", + nestingLevel: 1, labels: [ "Schlechtes Wetter I", "Schlechtes Wetter II", @@ -114,6 +115,7 @@ export class RestingDialog extends HandlebarsApplicationMixin(ApplicationV2) { aspMod: -1, lepMod: -1, display: "boolean", + nestingLevel: 1, group: "bad_camp", active: false, value: "on" @@ -220,6 +222,14 @@ export class RestingDialog extends HandlebarsApplicationMixin(ApplicationV2) { const elementInMod = this.element.querySelector('output[name="inMod"]') const elementWoundMod = this.element.querySelector('output[name="woundMod"]') + if (this.restingType === this.#type.DRAUßEN) { + this.element.querySelector('input[name="bad_weather"]').removeAttribute('disabled') + this.element.querySelector('input[name="bad_camp"]').removeAttribute('disabled') + } else { + this.element.querySelector('input[name="bad_weather"]').setAttribute('disabled', 'disabled') + this.element.querySelector('input[name="bad_camp"]').setAttribute('disabled', 'disabled') + } + const context = this.#updateData() elementLepMod.value = context.lepModDisplay elementKoMod.value = context.koRollDisplay @@ -334,8 +344,6 @@ export class RestingDialog extends HandlebarsApplicationMixin(ApplicationV2) { context.inRollDisplay = `1w20+${this.regInMod}` } - console.log(this, context) - return context } diff --git a/src/module/documents/character.mjs b/src/module/documents/character.mjs index f158a631..bf116fee 100644 --- a/src/module/documents/character.mjs +++ b/src/module/documents/character.mjs @@ -105,6 +105,19 @@ export class Character extends Actor { systemData.ausweichen.basis = systemData.pa.basis systemData.ausweichen.aktuell = systemData.ausweichen.basis + systemData.ueberanstrengung = 0 + + if (game.settings.get("DSA_4-1", "optional_erschoepfung")) { + + systemData.erschoepfung = { + aktuell: systemData.erschoepfung.aktuell ?? 0, + max: ko + } + if (systemData.erschoepfung.aktuell > systemData.erschoepfung.max) { + systemData.ueberanstrengung = systemData.erschoepfung.aktuell - systemData.erschoepfung.max + } + } + if (game.settings.get("DSA_4-1", "optional_ruestungzonen")) { systemData.rs = { @@ -119,11 +132,11 @@ export class Character extends Actor { } else { systemData.rs = 0; // only with DSA_4-1.optional_trefferzonen = false } - systemData.be = 0; + systemData.be = 0 + systemData.ueberanstrengung; // half KO is the maximum a character can sustain wounds before collapsing - systemData.wunden.max = ko / 2; + systemData.wunden.max = Math.round(ko / 2); if (game.settings.get("DSA_4-1", "optional_trefferzonen")) { systemData.wunden.kopf = 0 systemData.wunden.brust = 0 diff --git a/src/module/settings/global-settings.mjs b/src/module/settings/global-settings.mjs index 50d919a8..65777bb8 100644 --- a/src/module/settings/global-settings.mjs +++ b/src/module/settings/global-settings.mjs @@ -36,6 +36,18 @@ function initGlobalSettings(settings) { requiresReload: true }) + settings.register('DSA_4-1', 'optional_erschoepfung', { + name: "Optional: Erschöpfung", + hint: "Aktiviert Regeln für das Spiel mit Erschöpfung und Überanstregung", + scope: "world", + config: true, + type: Boolean, + default: false, + onChange: value => { + }, + requiresReload: true + }) + settings.register('DSA_4-1', 'optional_distanzklassen', { name: "Optional: Distanzklassen", hint: "Aktiviert Regeln für das Spiel mit Distanzklassen", diff --git a/src/module/setup/config.mjs b/src/module/setup/config.mjs index 53a2dfd8..b603f42e 100644 --- a/src/module/setup/config.mjs +++ b/src/module/setup/config.mjs @@ -20,6 +20,7 @@ import {Talent} from "../data/talent.mjs"; import {Character} from "../documents/character.mjs"; import {currency} from "../handlebar-helpers/currency.mjs"; import {DeityDataModel} from "../data/deity.mjs"; +import {ItemBrowserDialog} from "../dialog/itemBrowserDialog.mjs"; function initGlobalAccess() { @@ -30,6 +31,7 @@ function initGlobalAccess() { Wunde, RestingDialog, BattleDialog, + ItemBrowserDialog, Talent, displayCurrency: currency } @@ -71,7 +73,48 @@ function initCombat(config) { } } +function initSocketLib() { + + + Hooks.on("socketlib.ready", () => { + console.log("Socketlib is registering") + let socket = socketlib.registerSystem("DSA_4-1") + socket.register("removeFromLootTable", removeFromLootTable) + socket.register("buyFromLootTable", buyFromLootTable) + if (!game.DSA41) { + game.DSA41 = {} + } + game.DSA41.socket = socket + }) + + + async function removeFromLootTable(actorId, itemId) { + if (actorId && game.actors.get(actorId)) { + const actor = game.actors.get(actorId) + + return await actor.deleteEmbeddedDocuments('Item', [itemId]) + } + } + + async function buyFromLootTable(actorId, itemId) { + if (actorId && game.actors.get(actorId)) { + const actor = game.actors.get(actorId) + const item = actor.items.find(p => p.id === itemId) + if (item.system.quantity != -1) { // -1 means infinite + if (item.system.quantity > 1) { + item.update({'system.quantity': item.system.quantity - 1}) + } else { + actor.deleteEmbeddedDocuments('Item', [item._id]) // delete when the quantity is equal to 0 + } + } + return true + } + } + +} + export { + initSocketLib, initGlobalAccess, initDocumentClasses, initDataModels, diff --git a/src/module/setup/partials.mjs b/src/module/setup/partials.mjs index 9ff711c4..ce3115dd 100644 --- a/src/module/setup/partials.mjs +++ b/src/module/setup/partials.mjs @@ -4,6 +4,7 @@ function loadPartials(hbs) { hbs.loadTemplates([ // ui partials. 'systems/DSA_4-1/templates/ui/partial-rollable-button.hbs', + 'systems/DSA_4-1/templates/ui/partial-cooldown.hbs', 'systems/DSA_4-1/templates/ui/partial-rollable-weaponskill-button.hbs', 'systems/DSA_4-1/templates/ui/partial-rollable-language-button.hbs', 'systems/DSA_4-1/templates/ui/partial-attribute-button.hbs', @@ -14,7 +15,13 @@ function loadPartials(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-array-editor.hbs', - 'systems/DSA_4-1/templates/dialog/liturgy-dialog.hbs' + 'systems/DSA_4-1/templates/actor/character/tab-set.hbs', + 'systems/DSA_4-1/templates/dialog/liturgy-dialog.hbs', + 'systems/DSA_4-1/templates/ui/partial-mini-rollable-button.hbs', + 'systems/DSA_4-1/templates/ui/partial-mini-rollable-liturgy-button.hbs', + 'systems/DSA_4-1/templates/ui/partial-mini-rollable-weaponskill-button.hbs', + 'systems/DSA_4-1/templates/ui/partial-mini-rollable-language-button.hbs', + 'systems/DSA_4-1/templates/ui/partial-mini-rollable-spell-button.hbs', ]).then(resolve); }) diff --git a/src/module/setup/sheets.mjs b/src/module/setup/sheets.mjs index 51fe9bec..781333f5 100644 --- a/src/module/setup/sheets.mjs +++ b/src/module/setup/sheets.mjs @@ -4,7 +4,7 @@ import {GroupSheet} from "../sheets/groupSheet.mjs"; import {SkillSheet} from "../sheets/skillSheet.mjs"; import {SpellSheet} from "../sheets/spellSheet.mjs"; import {AdvantageSheet} from "../sheets/advantageSheet.mjs"; -import {EquipmentSheet} from "../sheets/equipmentSheet.mjs"; +import EquipmentSheet from "../sheets/equipmentSheet.mjs"; import {LiturgySheet} from "../sheets/liturgySheet.mjs"; import {SpecialAbilitySheet} from "../sheets/specialAbilitySheet.mjs"; import {ActiveEffectSheet} from "../sheets/activeEffectSheet.mjs"; diff --git a/src/module/sheets/character-standalone/advsf.mjs b/src/module/sheets/character-standalone/advsf.mjs new file mode 100644 index 00000000..11777bbf --- /dev/null +++ b/src/module/sheets/character-standalone/advsf.mjs @@ -0,0 +1,118 @@ +const {HandlebarsApplicationMixin, DocumentSheetV2} = foundry.applications.api +const {ActorSheetV2} = foundry.applications.sheets + +export class StandaloneADVSF extends HandlebarsApplicationMixin(ActorSheetV2) { + + /** @inheritDoc */ + static DEFAULT_OPTIONS = { + position: {width: 520, height: 480}, + classes: ['dsa41', 'sheet', 'actor', 'character', 'standalone', 'advsf'], + tag: 'form', + actions: { + rollFlaw: StandaloneADVSF.#rollFlaw, + openEmbeddedDocument: StandaloneADVSF.#openEmbeddedDocument, + + } + } + + /** @inheritDoc */ + static PARTS = { + form: { + template: `systems/DSA_4-1/templates/actor/character/standalone/advsf.hbs` + } + } + + _actor = null + + constructor(actor) { + super(actor) + this._actor = actor + this.render(true) + this.options.window.title = `${this.document.name} Vor und Nachteile` + } + + static async #rollFlaw(event, target) { + this._actor?.sheet.options.actions.rollFlaw.bind(this)(event, target) + } + + static async #openEmbeddedDocument(event, target) { + this._actor?.sheet.options.actions.openEmbeddedDocument.bind(this)(event, target) + } + + _configureRenderOptions(options) { + super._configureRenderOptions(options) + + options.window.title = `${this.document.name}: Vor und Nachteile` + + return options + } + + async _prepareContext(context, options, object) { + if (this._actor) { + const actorData = this.document + context.system = actorData.system + context.flags = actorData.flags + context.derived = actorData.system + context.originalName = actorData.name + context.name = context.derived.name ?? actorData.name + context.effects = actorData.effects ?? [] + context.advantages = [] + context.flaws = [] + + actorData.itemTypes.Advantage.forEach((item) => { + if (!item.system.schlechteEigenschaft) { + context.advantages.push({ + id: item._id, + name: item.name, + value: item.system.value, + options: item.system.auswahl, + description: item.system.description, + isAdvantage: !item.system.nachteil, + isDisadvantage: item.system.nachteil, + isBadAttribute: item.system.schlechteEigenschaft, + fav: item.getFlag("DSA_4-1", "favourite") + }) + } else { + context.flaws.push({ + id: item._id, + name: item.name, + value: item.system.value, + options: item.system.auswahl, + description: item.system.description, + isAdvantage: !item.system.nachteil, + isDisadvantage: item.system.nachteil, + isBadAttribute: item.system.schlechteEigenschaft, + fav: item.getFlag("DSA_4-1", "favourite") + }) + } + } + ) + + context.specialAbilities = [] + actorData.itemTypes.SpecialAbility.forEach((item) => { + context.specialAbilities.push({ + id: item._id, + name: item.system.value ? item.system.value : item.name, + fav: item.getFlag("DSA_4-1", "favourite") + }); + } + ); + + return context + } + } + + _onRender(context, options) { + if (this._actor) { + new foundry.applications.ux.DragDrop.implementation({ + dropSelector: ".advantages, .special-abilities", + permissions: { + drop: this._actor.sheet._canDragDrop.bind(this._actor.sheet) + }, + callbacks: { + drop: this._actor.sheet._onDrop.bind(this._actor.sheet), + } + }).bind(this.element); + } + } +} \ No newline at end of file diff --git a/src/module/sheets/character-standalone/bagpack.mjs b/src/module/sheets/character-standalone/bagpack.mjs new file mode 100644 index 00000000..24a52224 --- /dev/null +++ b/src/module/sheets/character-standalone/bagpack.mjs @@ -0,0 +1,98 @@ +const {HandlebarsApplicationMixin, DocumentSheetV2} = foundry.applications.api +const {ActorSheetV2} = foundry.applications.sheets + +export class Bagpack extends HandlebarsApplicationMixin(ActorSheetV2) { + + /** @inheritDoc */ + static DEFAULT_OPTIONS = { + position: {width: 520, height: 480}, + classes: ['dsa41', 'sheet', 'actor', 'character', 'standalone', 'bagpack'], + tag: 'form', + actions: { + openItemBrowser: Bagpack.#openItemBrowser, + newItem: Bagpack.#newItem, + openEmbeddedDocument: Bagpack.#openEmbeddedDocument, + + } + } + + /** @inheritDoc */ + static PARTS = { + form: { + template: `systems/DSA_4-1/templates/actor/character/standalone/bagpack.hbs` + } + } + + _actor = null + + constructor(actor) { + super(actor) + this._actor = actor + this.render(true) + } + + static async #openItemBrowser(event, target) { + this._actor?.sheet.options.actions.openItemBrowser().bind(this)(event, target) + } + + static async #newItem(event, target) { + this._actor?.sheet.options.actions.newItem.bind(this)(event, target) + } + + static async #openEmbeddedDocument(event, target) { + this._actor?.sheet.options.actions.openEmbeddedDocument.bind(this)(event, target) + } + + _configureRenderOptions(options) { + super._configureRenderOptions(options) + + options.window.title = `${this.document.name}: Inventar` + + return options + } + + async _prepareContext(context, options, object) { + const actorData = this.document + context.system = actorData.system + context.flags = actorData.flags + + context.equipments = [] + context.carryingweight = 0 + actorData.itemTypes["Equipment"].sort((a, b) => a.sort - b.sort).forEach((item, index) => { + + // worn items are halved weight + + let effectiveWeight = item.system.weight ?? 0 + if (this.document.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, + worn: this.document.isWorn(item._id) + }) + context.carryingweight += item.system.quantity * effectiveWeight; + + }) + context.maxcarryingcapacity = actorData.system.attribute.kk.aktuell + context.carryingpercentage = Math.min((context.carryingweight / context.maxcarryingcapacity) * 100, 100); + + context.wealth = 0 + + actorData.itemTypes["Equipment"].forEach(coin => { + if (coin.system.category.indexOf("Währung") !== -1) { + context.wealth += (coin.system.quantity * coin.system.currencyDenominator) + } + }) + + return context + } + + _onRender(context, options) { + } +} \ No newline at end of file diff --git a/src/module/sheets/character-standalone/health.mjs b/src/module/sheets/character-standalone/health.mjs new file mode 100644 index 00000000..6d4157df --- /dev/null +++ b/src/module/sheets/character-standalone/health.mjs @@ -0,0 +1,111 @@ +const {HandlebarsApplicationMixin, DocumentSheetV2} = foundry.applications.api +const {ActorSheetV2} = foundry.applications.sheets + +export class StandaloneHealth extends HandlebarsApplicationMixin(ActorSheetV2) { + + /** @inheritDoc */ + static DEFAULT_OPTIONS = { + position: {width: 520, height: 716}, + classes: ['dsa41', 'sheet', 'actor', 'character', 'standalone', 'health'], + tag: 'form', + actions: { + openEmbeddedDocument: StandaloneHealth.#openEmbeddedDocument, + setWounds: StandaloneHealth.#setWounds, + + } + } + + /** @inheritDoc */ + static PARTS = { + form: { + template: `systems/DSA_4-1/templates/actor/character/standalone/health.hbs` + } + } + + _actor = null + + constructor(actor) { + super(actor) + this._actor = actor + this.render(true) + } + + static async #openEmbeddedDocument(event, target) { + this._actor?.sheet.options.actions.openEmbeddedDocument.bind(this)(event, target) + } + + static async #setWounds(event, target) { + this._actor?.sheet.options.actions.setWounds.bind(this)(event, target) + } + + _configureRenderOptions(options) { + super._configureRenderOptions(options) + + options.window.title = `${this.document.name}: Gesundheit` + + return options + } + + async _prepareContext(context, options, object) { + const actorData = this.document + context.system = actorData.system + context.flags = actorData.flags + context.derived = this.document.system + context.originalName = actorData.name + context.name = context.derived.name ?? actorData.name + context.effects = actorData.effects ?? [] + + const findEquipmentOnSlot = (slot, setNumber, object) => { + return object.items.get(object.system.heldenausruestung[setNumber]?.[slot]) + } + + context.inidice = actorData.system.ini.wuerfel + context.inivalue = actorData.system.ini.aktuell + context.inimod = actorData.system.ini.mod + + context.zonenruestung = game.settings.get("DSA_4-1", "optional_ruestungzonen") + context.trefferzonen = game.settings.get("DSA_4-1", "optional_trefferzonen") + context.ausdauer = game.settings.get("DSA_4-1", "optional_ausdauer") + context.colorfulDice = game.settings.get('DSA_4-1', 'optional_colorfuldice') + + context.aupper = Math.min((actorData.system.aup.aktuell / actorData.system.aup.max) * 100, 100) + context.lepper = Math.min((actorData.system.lep.aktuell / actorData.system.lep.max) * 100, 100) + context.keper = Math.min((actorData.system.kap.aktuell / actorData.system.kap.max) * 100, 100) + context.aspper = Math.min((actorData.system.asp.aktuell / actorData.system.asp.max) * 100, 100) + + context.lepcurrent = actorData.system.lep.aktuell ?? 0 + context.aupcurrent = actorData.system.aup.aktuell ?? 0 + context.aspcurrent = actorData.system.asp.aktuell ?? 0 + context.kapcurrent = actorData.system.kap.aktuell ?? 0 + + context.maxWounds = actorData.system.wunden.max ?? 3 + context.wounds = actorData.system.wunden.aktuell ?? 0 + context.woundsFilled = [] + for (let i = 1; i <= context.maxWounds; i++) { + context.woundsFilled[i] = i <= context.wounds + } + + context.withErschoepfung = game.settings.get("DSA_4-1", "optional_erschoepfung") + context.ueberanstrengung = actorData.system.ueberanstrengung + context.erschoepfung = actorData.system.erschoepfung.aktuell + context.maxErschoepfung = actorData.system.erschoepfung.max + + context.erschoepfungFilled = [] + for (let i = 1; i <= context.maxErschoepfung; i++) { + context.erschoepfungFilled[i] = i <= context.erschoepfung + } + + + context.effects = [] + for (let i = 0; i < actorData.appliedEffects.length; i++) { + const item = actorData.appliedEffects[i] + context.effects.push(item.name) + } + + return context + + } + + _onRender(context, options) { + } +} \ No newline at end of file diff --git a/src/module/sheets/character-standalone/liturgies.mjs b/src/module/sheets/character-standalone/liturgies.mjs new file mode 100644 index 00000000..e88459df --- /dev/null +++ b/src/module/sheets/character-standalone/liturgies.mjs @@ -0,0 +1,152 @@ +import {LiturgyData} from "../../data/miracle/liturgyData.mjs"; + +const {HandlebarsApplicationMixin, DocumentSheetV2} = foundry.applications.api +const {ActorSheetV2} = foundry.applications.sheets + +export class StandaloneLiturgies extends HandlebarsApplicationMixin(ActorSheetV2) { + + /** @inheritDoc */ + static DEFAULT_OPTIONS = { + position: {width: 520, height: 480}, + classes: ['dsa41', 'sheet', 'actor', 'character', 'standalone', 'liturgies'], + tag: 'form', + actions: { + openEmbeddedDocument: StandaloneLiturgies.#openEmbeddedDocument, + openLiturgyDialog: StandaloneLiturgies.#openLiturgyDialog, + + } + } + + /** @inheritDoc */ + static PARTS = { + form: { + template: `systems/DSA_4-1/templates/actor/character/standalone/liturgies.hbs` + } + } + + _actor = null + + constructor(actor) { + super(actor) + this._actor = actor + this.render(true) + } + + static async #openEmbeddedDocument(event, target) { + this._actor?.sheet.options.actions.openEmbeddedDocument.bind(this)(event, target) + } + + static async #openLiturgyDialog(event, target) { + this._actor?.sheet.options.actions.openLiturgyDialog.bind(this)(event, target) + } + + _configureRenderOptions(options) { + super._configureRenderOptions(options) + + options.window.title = `${this.document.name}: Segnungen und Liturgien` + + return options + } + + async _prepareContext(context, options, object) { + + const actorData = this.document + context.system = actorData.system + context.flags = actorData.flags + context.derived = this.document.system + context.originalName = actorData.name + context.name = context.derived.name ?? actorData.name + + context.effects = actorData.effects ?? [] + context.liturgies = []; + context.blessings = []; + + actorData.itemTypes.Blessing.forEach((item, index) => { + context.blessings.push({ + deity: item.system.gottheit, + value: item.system.wert + }) + }) + actorData.itemTypes.Liturgy.forEach((item, index) => { + + context.blessings.forEach(({deity, value}) => { + let insertObject = context.liturgies.find(p => p.deity === deity); + if (!insertObject) { + insertObject = { + deity: deity, + lkp: value, + O: [], + I: [], + II: [], + III: [], + IV: [], + V: [], + VI: [], + VII: [], + VIII: [], + "NA": [], + countO: 1, + countI: 1, + countII: 1, + countIII: 1, + countIV: 1, + countV: 1, + countVI: 1, + countVII: 1, + countVIII: 1, + countNA: 0, + total: 3, + + } + context.liturgies.push(insertObject); + } + + // sort by rank + const rankData = LiturgyData.getRankOfLiturgy(item.system, deity) + if (rankData) { + console.log(rankData) + let {index, name, lkp, mod, costKaP} = rankData; + + insertObject["count" + name] = insertObject["count" + name] + 1; + + insertObject[name]?.push({ + id: item._id, + name: item.name, + lkpReq: lkp, + lkpMod: mod, + costKaP, + fav: item.getFlag("DSA_4-1", "favourite"), + rank: index, // get effective liturgy rank based on deity + liturgiekenntnis: deity, + }) + insertObject.total = insertObject.total + 2; + + } + }) + }) + + + // clean up counter + Object.values(context.liturgies).forEach((litObject) => { + + if (litObject.I.length === 0) litObject.countI = false; + if (litObject.II.length === 0) litObject.countII = false; + if (litObject.III.length === 0) litObject.countIII = false; + if (litObject.IV.length === 0) litObject.countIV = false; + if (litObject.V.length === 0) litObject.countV = false; + if (litObject.VI.length === 0) litObject.countVI = false; + if (litObject.VII.length === 0) litObject.countVII = false; + if (litObject.VIII.length === 0) litObject.countVIII = false; + if (litObject.NA.length === 0) litObject.countNA = false; + + + }) + + context.hasLiturgies = context.blessings.length > 0; + + return context + } + + _onRender(context, options) { + } +} \ No newline at end of file diff --git a/src/module/sheets/character-standalone/skills.mjs b/src/module/sheets/character-standalone/skills.mjs new file mode 100644 index 00000000..7d752bb6 --- /dev/null +++ b/src/module/sheets/character-standalone/skills.mjs @@ -0,0 +1,127 @@ +const {HandlebarsApplicationMixin, DocumentSheetV2} = foundry.applications.api +const {ActorSheetV2} = foundry.applications.sheets + +export class StandaloneSkills extends HandlebarsApplicationMixin(ActorSheetV2) { + + /** @inheritDoc */ + static DEFAULT_OPTIONS = { + position: {width: 520, height: 480}, + classes: ['dsa41', 'sheet', 'actor', 'character', 'standalone', 'skills'], + tag: 'form', + actions: { + rollCombatSkill: StandaloneSkills.#rollCombatSkill, + rollSkill: StandaloneSkills.#rollSkill, + openEmbeddedDocument: StandaloneSkills.#openEmbeddedDocument, + + } + } + + /** @inheritDoc */ + static PARTS = { + form: { + template: `systems/DSA_4-1/templates/actor/character/standalone/skills.hbs` + } + } + + _actor = null + + constructor(actor) { + super(actor) + this._actor = actor + this.render(true) + } + + static async #rollCombatSkill(event, target) { + this._actor?.sheet.options.actions.rollCombatSkill.bind(this)(event, target) + } + + static async #rollSkill(event, target) { + this._actor?.sheet.options.actions.rollSkill.bind(this)(event, target) + } + + static async #openEmbeddedDocument(event, target) { + this._actor?.sheet.options.actions.openEmbeddedDocument.bind(this)(event, target) + } + + _configureRenderOptions(options) { + super._configureRenderOptions(options) + + options.window.title = `${this.document.name}: Talente` + + return options + } + + async _prepareContext(context, options, object) { + const actorData = this.document + context.system = actorData.system + context.flags = actorData.flags + context.derived = this.document.system + context.originalName = actorData.name + context.name = context.derived.name ?? actorData.name + context.effects = actorData.effects ?? [] + + const prepareEigenschaftRoll = (actorData, name) => { + if (name && name !== "*") { + return actorData.system.attribute[name.toLowerCase()].aktuell + } else { + return 0 + } + } + + context.skills = {}; + context.flatSkills = []; + + actorData.itemTypes.Skill.forEach((item, index) => { + + const talentGruppe = item.system.gruppe; + const eigenschaften = Object.values(item.system.probe); + const werte = [ + {name: eigenschaften[0], value: prepareEigenschaftRoll(actorData, eigenschaften[0])}, + {name: eigenschaften[1], value: prepareEigenschaftRoll(actorData, eigenschaften[1])}, + {name: eigenschaften[2], value: prepareEigenschaftRoll(actorData, eigenschaften[2])} + ] + if (context.skills[talentGruppe] == null) { + context.skills[talentGruppe] = []; + } + const obj = { + type: "talent", + gruppe: talentGruppe, + name: item.name.replace(/Sprachen kennen/g, "Sprache:").replace(/Lesen\/Schreiben/g, "Schrift: "), + 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, + at: item.system.at, + pa: item.system.pa, + komplexität: item.system.komplexität, + fav: item.getFlag("DSA_4-1", "favourite") + }; + + if (talentGruppe === "Kampf") { + + if (item.system.pa != null) { // has no parry value so it must be ranged talent (TODO: but it isnt as there can be combatstatistics which has no pa value assigned to) + obj.at = item.system.at + context.derived.at.aktuell + obj.pa = item.system.pa + context.derived.pa.aktuell + } else { + obj.at = item.system.at + context.derived.fk.aktuell + } + } + + context.skills[talentGruppe].push(obj); + context.flatSkills.push(obj); + } + ) + + return context + } + + _onRender(context, options) { + } +} \ No newline at end of file diff --git a/src/module/sheets/character-standalone/spells.mjs b/src/module/sheets/character-standalone/spells.mjs new file mode 100644 index 00000000..57b822e8 --- /dev/null +++ b/src/module/sheets/character-standalone/spells.mjs @@ -0,0 +1,98 @@ +const {HandlebarsApplicationMixin, DocumentSheetV2} = foundry.applications.api +const {ActorSheetV2} = foundry.applications.sheets + +export class StandaloneSpells extends HandlebarsApplicationMixin(ActorSheetV2) { + + /** @inheritDoc */ + static DEFAULT_OPTIONS = { + position: {width: 520, height: 480}, + classes: ['dsa41', 'sheet', 'actor', 'character', 'standalone', 'spells'], + tag: 'form', + actions: { + openEmbeddedDocument: StandaloneSpells.#openEmbeddedDocument, + + } + } + + /** @inheritDoc */ + static PARTS = { + form: { + template: `systems/DSA_4-1/templates/actor/character/standalone/spells.hbs` + } + } + + _actor = null + + constructor(actor) { + super(actor) + this._actor = actor + this.render(true) + } + + static async #openEmbeddedDocument(event, target) { + this._actor?.sheet.options.actions.openEmbeddedDocument.bind(this)(event, target) + } + + _configureRenderOptions(options) { + super._configureRenderOptions(options) + + options.window.title = `${this.document.name}: Zauber und Rituale` + + return options + } + + async _prepareContext(context, options, object) { + + const actorData = this.document + context.spells = [] + context.system = actorData.system + context.flags = actorData.flags + context.derived = this.document.system + context.originalName = actorData.name + context.name = context.derived.name ?? actorData.name + context.effects = actorData.effects ?? [] + + const cleanUpMerkmal = (merkmale) => { + return merkmale.split(",").map((merkmal) => merkmal.trim()) + } + + const prepareEigenschaftRoll = (actorData, name) => { + if (name && name !== "*") { + return actorData.system.attribute[name.toLowerCase()].aktuell + } else { + return 0 + } + } + + actorData.itemTypes["Spell"].forEach((item, index) => { + + const eigenschaften = item.system.probe; + const werte = [ + {name: eigenschaften[0], value: prepareEigenschaftRoll(actorData, eigenschaften[0])}, + {name: eigenschaften[1], value: prepareEigenschaftRoll(actorData, eigenschaften[1])}, + {name: eigenschaften[2], value: prepareEigenschaftRoll(actorData, eigenschaften[2])} + ] + context.spells.push({ + id: item._id, + name: item.name, + zfw: item.system.zfw, + hauszauber: item.system.hauszauber, + merkmal: cleanUpMerkmal(item.system.merkmal), + rollEigenschaft1: werte[0].value, + rollEigenschaft2: werte[1].value, + rollEigenschaft3: werte[2].value, + eigenschaft1: werte[0].name, + eigenschaft2: werte[1].name, + eigenschaft3: werte[2].name, + fav: item.getFlag("DSA_4-1", "favourite") + }) + + }) + context.hasSpells = context.spells.length > 0 + + return context + } + + _onRender(context, options) { + } +} \ No newline at end of file diff --git a/src/module/sheets/character/advsf.mjs b/src/module/sheets/character/advsf.mjs index f0074d84..2bd10a79 100644 --- a/src/module/sheets/character/advsf.mjs +++ b/src/module/sheets/character/advsf.mjs @@ -21,7 +21,8 @@ export default { description: item.system.description, isAdvantage: !item.system.nachteil, isDisadvantage: item.system.nachteil, - isBadAttribute: item.system.schlechteEigenschaft + isBadAttribute: item.system.schlechteEigenschaft, + fav: item.getFlag("DSA_4-1", "favourite") }) } else { context.flaws.push({ @@ -32,7 +33,8 @@ export default { description: item.system.description, isAdvantage: !item.system.nachteil, isDisadvantage: item.system.nachteil, - isBadAttribute: item.system.schlechteEigenschaft + isBadAttribute: item.system.schlechteEigenschaft, + fav: item.getFlag("DSA_4-1", "favourite") }) } } @@ -43,6 +45,7 @@ export default { context.specialAbilities.push({ id: item._id, name: item.system.value ? item.system.value : item.name, + fav: item.getFlag("DSA_4-1", "favourite") }); } ); diff --git a/src/module/sheets/character/equipment.mjs b/src/module/sheets/character/equipment.mjs index d38ac604..8c34b1c8 100644 --- a/src/module/sheets/character/equipment.mjs +++ b/src/module/sheets/character/equipment.mjs @@ -1,7 +1,7 @@ import {PlayerCharacterDataModel} from "../../data/character.mjs"; export default { - _prepareContext: (context) => { + _prepareContext: (context, actor, thisObject) => { const actorData = context.document context.spells = [] @@ -53,7 +53,7 @@ export default { context.sets.push({ tab: "set" + (setIndex + 1), - name: romanNumerals[setIndex], + label: romanNumerals[setIndex], index: setIndex, slots: [ { @@ -280,7 +280,7 @@ export default { name: "Aus dem Inventar entfernen", icon: '', callback: (target) => { - thisObject.document.deleteEmbeddedDocuments('Item', [target.dataset.itemId]) + game.DSA41.socket.executeAsGM("removeFromLootTable", thisObject.document.id, target.dataset.itemId) }, condition: (target) => { const {itemId} = target.dataset @@ -290,7 +290,7 @@ export default { ], {jQuery: false}); }, _getTabConfig: (group) => { - group.tabs.push({id: "equipment", group: "sheet", label: "Ausrüstung"}) + group?.tabs.push({id: "equipment", group: "sheet", label: "Ausrüstung"}) }, template: `systems/DSA_4-1/templates/actor/character/tab-equipment.hbs` } \ No newline at end of file diff --git a/src/module/sheets/character/combat.mjs b/src/module/sheets/character/health.mjs similarity index 52% rename from src/module/sheets/character/combat.mjs rename to src/module/sheets/character/health.mjs index 10485d66..9c3de352 100644 --- a/src/module/sheets/character/combat.mjs +++ b/src/module/sheets/character/health.mjs @@ -1,5 +1,3 @@ -import {ActionManager} from "../actions/action-manager.mjs"; - export default { _prepareContext: async (context, object) => { @@ -15,9 +13,6 @@ export default { return object.items.get(object.system.heldenausruestung[setNumber]?.[slot]) } - const am = new ActionManager(actorData) - context.actions = am.evaluate().filter(action => action.type !== ActionManager.ATTACK) - context.inidice = actorData.system.ini.wuerfel context.inivalue = actorData.system.ini.aktuell context.inimod = actorData.system.ini.mod @@ -26,9 +21,35 @@ export default { context.lepper = Math.min((actorData.system.lep.aktuell / actorData.system.lep.max) * 100, 100) context.keper = Math.min((actorData.system.kap.aktuell / actorData.system.kap.max) * 100, 100) context.aspper = Math.min((actorData.system.asp.aktuell / actorData.system.asp.max) * 100, 100) - context.lepcurrent = actorData.system.lep.aktuell ?? 0 + context.lepcurrent = actorData.system.lep.aktuell ?? 0 context.aupcurrent = actorData.system.aup.aktuell ?? 0 + context.aspcurrent = actorData.system.asp.aktuell ?? 0 + context.kapcurrent = actorData.system.kap.aktuell ?? 0 + + context.maxWounds = actorData.system.wunden.max ?? 3 + context.wounds = actorData.system.wunden.aktuell ?? 0 + context.woundsFilled = [] + for (let i = 1; i <= context.maxWounds; i++) { + context.woundsFilled[i] = i <= context.wounds + } + + context.withErschoepfung = game.settings.get("DSA_4-1", "optional_erschoepfung") + context.ueberanstrengung = actorData.system.ueberanstrengung + context.erschoepfung = actorData.system.erschoepfung.aktuell + context.maxErschoepfung = actorData.system.erschoepfung.max + + context.erschoepfungFilled = [] + for (let i = 1; i <= context.maxErschoepfung; i++) { + context.erschoepfungFilled[i] = i <= context.erschoepfung + } + + + context.effects = [] + for (let i = 0; i < actorData.appliedEffects.length; i++) { + const item = actorData.appliedEffects[i] + context.effects.push(item.name) + } return context @@ -37,7 +58,7 @@ export default { }, _getTabConfig: (group) => { - group.tabs.push({id: "combat", group: "sheet", label: "Kampf"}) + group.tabs.push({id: "health", group: "sheet", label: "Gesundheit"}) }, - template: `systems/DSA_4-1/templates/actor/character/tab-combat.hbs` + template: `systems/DSA_4-1/templates/actor/character/tab-health.hbs` } \ No newline at end of file diff --git a/src/module/sheets/character/liturgies.mjs b/src/module/sheets/character/liturgies.mjs index 4c3d38f0..ac5022c8 100644 --- a/src/module/sheets/character/liturgies.mjs +++ b/src/module/sheets/character/liturgies.mjs @@ -68,6 +68,7 @@ export default { lkpReq: lkp, lkpMod: mod, costKaP, + fav: item.getFlag("DSA_4-1", "favourite"), rank: index, // get effective liturgy rank based on deity liturgiekenntnis: deity, }) @@ -104,7 +105,7 @@ export default { _getTabConfig: (group, thisObject) => { const hasLiturgies = thisObject.document.items.filter(p => p.type === "Liturgy").length > 0 ?? false if (hasLiturgies) { - group.tabs.push({id: "liturgies", group: "sheet", label: "Liturgien"}) + group?.tabs.push({id: "liturgies", group: "sheet", label: "Liturgien"}) } }, template: `systems/DSA_4-1/templates/actor/character/tab-liturgies.hbs` diff --git a/src/module/sheets/character/meta.mjs b/src/module/sheets/character/meta.mjs index de5d56fe..57b92286 100644 --- a/src/module/sheets/character/meta.mjs +++ b/src/module/sheets/character/meta.mjs @@ -14,7 +14,7 @@ export default { }, _getTabConfig: (group) => { - group.tabs.push({id: "meta", group: "sheet", label: "Meta"}) + group?.tabs.push({id: "meta", group: "sheet", label: "Meta"}) }, template: `systems/DSA_4-1/templates/actor/character/tab-meta.hbs` } \ No newline at end of file diff --git a/src/module/sheets/character/skills.mjs b/src/module/sheets/character/skills.mjs index 9cf1d9d5..319f1f58 100644 --- a/src/module/sheets/character/skills.mjs +++ b/src/module/sheets/character/skills.mjs @@ -2,7 +2,6 @@ export default { _prepareContext: (context) => { const actorData = context.document - context.spells = [] context.system = actorData.system context.flags = actorData.flags context.derived = context.document.system @@ -51,7 +50,8 @@ export default { id: item._id, at: item.system.at, pa: item.system.pa, - komplexität: item.system.komplexität + komplexität: item.system.komplexität, + fav: item.getFlag("DSA_4-1", "favourite") }; if (talentGruppe === "Kampf") { diff --git a/src/module/sheets/character/spells.mjs b/src/module/sheets/character/spells.mjs index 917679be..db409ba5 100644 --- a/src/module/sheets/character/spells.mjs +++ b/src/module/sheets/character/spells.mjs @@ -42,6 +42,7 @@ export default { eigenschaft1: werte[0].name, eigenschaft2: werte[1].name, eigenschaft3: werte[2].name, + fav: item.getFlag("DSA_4-1", "favourite") }) }) diff --git a/src/module/sheets/characterSheet.mjs b/src/module/sheets/characterSheet.mjs index 5508fab8..46331c88 100644 --- a/src/module/sheets/characterSheet.mjs +++ b/src/module/sheets/characterSheet.mjs @@ -1,5 +1,5 @@ import Advsf from "./character/advsf.mjs" -import Combat from "./character/combat.mjs" +import Health from "./character/health.mjs" import Effects from "./character/effects.mjs" import Equipment from "./character/equipment.mjs" import Liturgies from "./character/liturgies.mjs" @@ -14,6 +14,14 @@ import {RestingDialog} from "../dialog/restingDialog.mjs"; import {LiturgyDialog} from "../dialog/liturgyDialog.mjs"; import {TalentDialog} from "../dialog/talentDialog.mjs"; import {AttributeDialog} from "../dialog/attributeDialog.mjs"; +import {ItemBrowserDialog} from "../dialog/itemBrowserDialog.mjs"; +import * as EquipmentDocument from "../documents/equipment.mjs"; +import {StandaloneADVSF} from "./character-standalone/advsf.mjs"; +import {StandaloneSkills} from "./character-standalone/skills.mjs"; +import {Bagpack} from "./character-standalone/bagpack.mjs"; +import {StandaloneSpells} from "./character-standalone/spells.mjs"; +import {StandaloneLiturgies} from "./character-standalone/liturgies.mjs"; +import {StandaloneHealth} from "./character-standalone/health.mjs"; const {HandlebarsApplicationMixin, DocumentSheetV2} = foundry.applications.api const {ActorSheetV2} = foundry.applications.sheets @@ -37,19 +45,31 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { actions: { rollCombatSkill: CharacterSheet.#rollCombatSkill, rollSkill: CharacterSheet.#rollSkill, - rollFlaw: CharacterSheet.#rollFlaw, + rollFlaw: CharacterSheet.rollFlaw, rollAttribute: CharacterSheet.#rollAttribute, editImage: DocumentSheetV2.DEFAULT_OPTIONS.actions.editImage, - openEmbeddedDocument: CharacterSheet.#openEmbeddedDocument, + openEmbeddedDocument: CharacterSheet.openEmbeddedDocument, openCultureDocument: CharacterSheet.#openCultureDocument, openSpeciesDocument: CharacterSheet.#openSpeciesDocument, openCombatAction: CharacterSheet.#openCombatAction, - openLiturgyDialog: CharacterSheet.#openLiturgyDialog, + openLiturgyDialog: CharacterSheet.openLiturgyDialog, + openSpellDialog: CharacterSheet.openSpellDialog, progressCooldown: CharacterSheet.#progressCooldown, cancelCooldown: CharacterSheet.#cancelCooldown, activateCooldown: CharacterSheet.#activateCooldown, rest: CharacterSheet.#startResting, removeEffect: CharacterSheet.#removeEffect, + rollDamage: CharacterSheet.#rollDamage, + openItemBrowser: CharacterSheet.openItemBrowser, + newItem: CharacterSheet.addNewItem, + toggleFav: CharacterSheet.toggleFav, + openStandaloneADVSF: CharacterSheet.#openStandaloneADVSF, + openStandaloneSkills: CharacterSheet.#openStandaloneSkills, + openBagpack: CharacterSheet.#openBagpack, + openStandaloneSpells: CharacterSheet.#openStandaloneSpells, + openStandaloneLiturgies: CharacterSheet.#openStandaloneLiturgies, + openStandaloneHealth: CharacterSheet.#openStandaloneHealth, + setWounds: CharacterSheet.#setWounds, } } @@ -58,7 +78,7 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { sheet: { tabs: [], initial: 'meta' - } + }, } /** @inheritDoc */ @@ -75,15 +95,16 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { advsf: { template: Advsf.template }, - combat: { - template: Combat.template + health: { + template: Health.template }, equipment: { template: Equipment.template, scrollable: ['.inventory'] }, skills: { - template: Skills.template + template: Skills.template, + scrollable: ['.tab.skills'] }, spells: { template: Spells.template @@ -93,7 +114,16 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { }, effects: { template: Effects.template - } + }, + set1: { + template: "systems/DSA_4-1/templates/actor/character/tab-set.hbs" + }, + set2: { + template: "systems/DSA_4-1/templates/actor/character/tab-set.hbs" + }, + set3: { + template: "systems/DSA_4-1/templates/actor/character/tab-set.hbs" + }, } @@ -128,7 +158,7 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { }).render(true) } - static async #rollFlaw(event, target) { + static async rollFlaw(event, target) { new AttributeDialog(this.document, target.dataset.itemId).render(true) } @@ -139,7 +169,7 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { cooldowns.splice(cooldownId, 1) if (cooldown) { - cooldown.current = cooldown.current - 1 + cooldown.current = cooldown.current + 1 } cooldowns.push(cooldown) @@ -163,7 +193,7 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { const cooldowns = this.document.system.cooldowns const cooldown = this.document.system.cooldowns[cooldownId] - if (cooldown && cooldown.current <= 0) { + if (cooldown && cooldown.current >= cooldown.start) { const am = new ActionManager(this.document) console.log(cooldown.data.maneuver) const action = new Function(`return ${cooldown.data.maneuver}`) @@ -183,7 +213,7 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { * * @param {MouseEvent} event */ - static #openEmbeddedDocument(event) { + static openEmbeddedDocument(event) { let dataset = event.target.dataset if (!dataset.itemId && !dataset.id) { dataset = event.target.parentElement.dataset @@ -202,21 +232,29 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { } static #openCombatAction(event, target) { + let {weapon, skill} = target.dataset + switch (target.dataset.mode) { case "attack": - new CombatActionDialog(this.document).render(true) + new CombatActionDialog(this.document, {weapon, skill}).render(true) break case "defense": - new DefenseActionDialog(this.document).render(true) + new DefenseActionDialog(this.document, {weapon, skill}).render(true) break } } - static #openLiturgyDialog(event, target) { + static openLiturgyDialog(event, target) { const {id, lkp, deity} = target.dataset new LiturgyDialog(this.document, lkp, id, deity).render(true) } + static openSpellDialog(event, target) { + const {itemId} = target.dataset + console.log(itemId) + this.document.itemTypes["Spell"]?.find(p => p.id === itemId)?.sheet.render(true) + } + static #startResting(event, target) { const dialog = new RestingDialog(this.document) @@ -238,6 +276,59 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { } + static async openItemBrowser(event, target) { + new ItemBrowserDialog(this.document).render(true) + } + + static async addNewItem(event, target) { + let item = new EquipmentDocument.Equipment({ + name: "Neuer Gegenstand", + type: "Equipment", + }) + const items = await this.document.createEmbeddedDocuments("Item", [item]) + items[0].sheet.render(true) + } + + static async toggleFav(event, target) { + const {itemId} = target.dataset + const doc = this.document.items.find(p => p.id === itemId) + + if (doc) { + const previous = doc.getFlag("DSA_4-1", "favourite") ?? false + doc.setFlag("DSA_4-1", "favourite", !previous) + } + } + + static async #openStandaloneADVSF(event, target) { + new StandaloneADVSF(this.document) + } + + static async #openStandaloneHealth(event, target) { + new StandaloneHealth(this.document) + } + + static async #openStandaloneSkills(event, target) { + new StandaloneSkills(this.document) + } + + static async #openBagpack(event, target) { + new Bagpack(this.document) + } + + static async #openStandaloneSpells(event, target) { + new StandaloneSpells(this.document) + } + + static async #openStandaloneLiturgies(event, target) { + new StandaloneLiturgies(this.document) + } + + static async #setWounds(event, target) { + const {value} = target.dataset + this.document.update({"system.wunden.aktuell": value}) + this.render(true) + } + _configureRenderOptions(options) { super._configureRenderOptions(options) @@ -248,6 +339,7 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { return options } + /** * Handle form submission * @this {AdvantageSheet} @@ -261,13 +353,46 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { await this.document.update(formData.object) } + static async #rollDamage(event, target) { + let {weapon, isRanged} = target.dataset + isRanged = isRanged == "true" + weapon = this.document.items.get(weapon) + if (weapon) { + const damageFormula = isRanged ? weapon.system.rangedAttackDamage : weapon.system.meleeAttackDamage + const calculation = await foundry.applications.api.DialogV2.prompt({ + window: {title: game.i18n.format("COMBAT_DIALOG_TP.windowTitle")}, + content: `
`, + ok: { + label: game.i18n.format("COMBAT_DIALOG_TP.buttonText"), + callback: (event, button, dialog) => { + return { + formula: button.form.elements.formula.value, + bonusDamage: button.form.elements.bonusDamage.value + } + } + } + }); + + const sanitisedFormula = calculation.formula.replace(/wW/g, "d") + const suffix = calculation.bonusDamage >= 0 ? "+" + calculation.bonusDamage : calculation.bonusDamage + + let r = new Roll(sanitisedFormula + suffix, this.document.getRollData()); + const label = `Schadenswurf` + await r.toMessage({ + speaker: ChatMessage.getSpeaker({actor: this.document}), + flavor: label, + rollMode: game.settings.get('core', 'rollMode'), + }) + } + } + _getTabsConfig(group) { const tabs = foundry.utils.deepClone(super._getTabsConfig(group)) Meta._getTabConfig(tabs, this) Social._getTabConfig(tabs, this) Advsf._getTabConfig(tabs, this) - Combat._getTabConfig(tabs, this) + Health._getTabConfig(tabs, this) Equipment._getTabConfig(tabs, this) Skills._getTabConfig(tabs, this) Spells._getTabConfig(tabs, this) @@ -329,13 +454,6 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { context.img = actorData.img context.effects = actorData.effects ?? [] - context.maxWounds = actorData.system.wunden.max ?? 3 - context.wounds = actorData.system.wunden.gesamt ?? 0 - context.woundsFilled = [] - for (let i = 1; i <= context.maxWounds; i++) { - context.woundsFilled[i] = i <= context.wounds - } - context.zonenruestung = game.settings.get("DSA_4-1", "optional_ruestungzonen") context.trefferzonen = game.settings.get("DSA_4-1", "optional_trefferzonen") context.ausdauer = game.settings.get("DSA_4-1", "optional_ausdauer") @@ -350,8 +468,11 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { context.lepper = Math.min((actorData.system.lep.aktuell / actorData.system.lep.max) * 100, 100) context.keper = Math.min((actorData.system.kap.aktuell / actorData.system.kap.max) * 100, 100) context.aspper = Math.min((actorData.system.asp.aktuell / actorData.system.asp.max) * 100, 100) + context.lepcurrent = actorData.system.lep.aktuell ?? 0 context.aupcurrent = actorData.system.aup.aktuell ?? 0 + context.aspcurrent = actorData.system.asp.aktuell ?? 0 + context.kapcurrent = actorData.system.kap.aktuell ?? 0 const fernkampf = actorData.findEquipmentOnSlot("fernkampf", actorData.system.setEquipped, actorData) const links = actorData.findEquipmentOnSlot("links", actorData.system.setEquipped, actorData) @@ -360,14 +481,19 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { if (fernkampf) { const fkitems = fernkampf.system.rangedSkills.map((skillInQuestion) => actorData.items.find(p => p.name === skillInQuestion)) - fkitems.forEach(skill => { - context.attacks.push({ - name: skill.name, - using: fernkampf.name, - at: `${this.document.system.fk.aktuell + skill.system.at}`, - tp: `${fernkampf.system.rangedAttackDamage}`, - ini: `${context.inidice}w6 + ${context.inivalue + fernkampf.system.iniModifier ?? 0}`, - }) + fkitems?.forEach(skill => { + if (skill) { + context.attacks.push({ + name: skill.name, + id: fernkampf._id, + skillId: skill._id, + using: fernkampf.name, + isRanged: true, + at: `${this.document.system.fk.aktuell + skill.system.at}`, + tp: `${fernkampf.system.rangedAttackDamage}`, + ini: `${context.inidice}w6 + ${context.inivalue + fernkampf.system.iniModifier ?? 0}`, + }) + } }) } if (links) { @@ -378,11 +504,14 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { meitems.push(item) } }) - meitems.forEach(skill => { + meitems?.forEach(skill => { const obj = skill context.attacks.push({ name: obj.name, + id: links._id, + skillId: skill._id, using: links.name, + isRanged: false, at: `${this.document.system.at.links.aktuell + obj.system.at + links.system.attackModifier}`, pa: `${this.document.system.pa.links.aktuell + obj.system.pa + links.system.parryModifier}`, tp: `${links.system.meleeAttackDamage}`, @@ -398,11 +527,14 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { meitems.push(item) } }) - meitems.forEach(skill => { + meitems?.forEach(skill => { const obj = skill context.attacks.push({ name: obj.name, + id: rechts._id, + skillId: skill._id, using: rechts.name, + isRanged: false, at: `${this.document.system.at.rechts.aktuell + obj.system.at + rechts.system.attackModifier}`, pa: `${this.document.system.pa.rechts.aktuell + obj.system.pa + rechts.system.parryModifier}`, tp: `${rechts.system.meleeAttackDamage}`, @@ -411,6 +543,45 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { }) } + context.favourites = actorData.items.filter(item => item.getFlag("DSA_4-1", "favourite") === true).map(item => { + let id = item.id + let t = null + switch (item.type) { + case "Spell": + t = "systems/DSA_4-1/templates/ui/partial-mini-rollable-spell-button.hbs" + break; + case "Skill": + switch (item.system.gruppe) { + case "Kampf": + t = "systems/DSA_4-1/templates/ui/partial-mini-rollable-weaponskill-button.hbs" + break; + case "Sprachen": + t = "systems/DSA_4-1/templates/ui/partial-mini-rollable-language-button.hbs" + break; + default: + t = "systems/DSA_4-1/templates/ui/partial-mini-rollable-button.hbs" + } + break; + case "Liturgy": + t = "systems/DSA_4-1/templates/ui/partial-mini-rollable-liturgy-button.hbs" + break; + default: + t = null + } + + let obj = Object.assign({}, item) + + obj.fav = item.getFlag("DSA_4-1", "favourite") + obj.id = id + obj.group = item.system.gruppe ?? null + + if (t) { + obj.template = t + } + + return obj + }) + context.cooldowns = actorData.system.cooldowns ?? [] context.cooldowns.forEach(cooldown => { let weapon = null @@ -418,15 +589,20 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { let tooltip = cooldown.data.title if (cooldown.data.weapon) { weapon = this.document.itemTypes["Equipment"].find(p => p._id === cooldown.data.weapon) - tooltip += `
Waffe:${weapon.name}` + tooltip += `
Waffe: ${weapon.name}` } if (cooldown.data.target) { target = game.actors.get(game.scenes.current.tokens.find(p => p._id === cooldown.data.target).actorId) - tooltip += `
Waffe:${target.name}` + tooltip += `
Ziel: ${target.name}` } cooldown.title = cooldown.data.title cooldown.progress = ((cooldown.current / cooldown.start) * 100) + "%" - cooldown.tooltip = tooltip + `
Aktionen verbleibend: ${cooldown.current}` + if (cooldown.start - cooldown.current > 0) { + cooldown.tooltip = tooltip + `
Aktionen verbleibend: ${cooldown.start - cooldown.current}
: 1 Aktion aufwenden` + } else { + cooldown.tooltip = tooltip + `
Aktionen verbleibend: ${cooldown.start - cooldown.current}
: Aktion durchführen` + } + }) context.hasSpells = actorData.itemTypes["Spell"].length > 0 @@ -493,11 +669,11 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { case "advsf": await Advsf._prepareContext(context, this.document) break - case "combat": - await Combat._prepareContext(context, this.document) + case "health": + await Health._prepareContext(context, this.document) break case "equipment": - await Equipment._prepareContext(context, this.document) + await Equipment._prepareContext(context, this.document, this) break case "skills": await Skills._prepareContext(context, this.document) @@ -515,11 +691,23 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { return context } + _onPosition(position) { + + if (position.width < 300) { + this.element.classList.add("tiny") + this.element.querySelector(".sidebuttons").style.left = (position.width + position.left) + "px" + this.element.querySelector(".sidebuttons").style.top = (position.top) + "px" + } else { + this.element.classList.remove("tiny") + } + + } + _onRender(context, options) { Meta._onRender(context, options, this.element) Social._onRender(context, options, this.element) Advsf._onRender(context, options, this) - Combat._onRender(context, options, this.element) + Health._onRender(context, options, this.element) Effects._onRender(context, options, this.element) Equipment._onRender(context, options, this) Liturgies._onRender(context, options, this.element) @@ -559,7 +747,7 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { } else { if (document.parent && document.parent !== this.actor) { - document.parent.items.get(document._id).delete() + game.DSA41.socket.executeAsGM("removeFromLootTable", document.parent.id, document._id) } await this._onDropDocument(event, document) diff --git a/src/module/sheets/creatureSheet.mjs b/src/module/sheets/creatureSheet.mjs index 87aafffa..895dc9e2 100644 --- a/src/module/sheets/creatureSheet.mjs +++ b/src/module/sheets/creatureSheet.mjs @@ -25,7 +25,6 @@ export class CreatureSheet extends HandlebarsApplicationMixin(ActorSheetV2) { static TABS = { sheet: { tabs: [], - initial: 'meta' } } @@ -66,6 +65,19 @@ export class CreatureSheet extends HandlebarsApplicationMixin(ActorSheetV2) { tabs.tabs.push(tab) } } + + if (!game.user.isGM) { + if (this.document.system.visibility.meta) { + tabs.initial = 'meta' + } else if (this.document.system.visibility.attacks) { + tabs.initial = 'attacks' + } else if (this.document.system.visibility.description) { + tabs.initial = 'description' + } else if (this.document.system.visibility.loot) { + tabs.initial = 'loot' + } + } + return tabs } diff --git a/src/module/sheets/equipmentSheet.mjs b/src/module/sheets/equipmentSheet.mjs index 6d29b1fd..76b352c2 100644 --- a/src/module/sheets/equipmentSheet.mjs +++ b/src/module/sheets/equipmentSheet.mjs @@ -1,6 +1,6 @@ const {DocumentSheetV2, HandlebarsApplicationMixin} = foundry.applications.api -export class EquipmentSheet extends HandlebarsApplicationMixin(DocumentSheetV2) { +class EquipmentSheet extends HandlebarsApplicationMixin(DocumentSheetV2) { /** @inheritDoc */ static DEFAULT_OPTIONS = { @@ -311,3 +311,5 @@ export class EquipmentSheet extends HandlebarsApplicationMixin(DocumentSheetV2) } } + +export default EquipmentSheet diff --git a/src/module/sheets/merchantSheet.mjs b/src/module/sheets/merchantSheet.mjs index bc9b2b63..2774656f 100644 --- a/src/module/sheets/merchantSheet.mjs +++ b/src/module/sheets/merchantSheet.mjs @@ -109,13 +109,8 @@ export class MerchantSheet extends HandlebarsApplicationMixin(ActorSheetV2) { actor.createEmbeddedDocuments('Item', [item]).then(documents => { documents[0].update({'system.quantity': 1}) }) - if (item.system.quantity != -1) { // -1 means infinite - if (item.system.quantity > 1) { - item.update({'system.quantity': item.system.quantity - 1}) - } else { - this.document.deleteEmbeddedDocuments('Item', [item._id]) // delete when the quantity is equal to 0 - } - } + + game.DSA41.socket.executeAsGM("buyFromLootTable", this.document.id, item.id) ChatMessage.create({ user: game.user._id, speaker: {actor}, diff --git a/src/style/atoms/_buttons.scss b/src/style/atoms/_buttons.scss new file mode 100644 index 00000000..270daf15 --- /dev/null +++ b/src/style/atoms/_buttons.scss @@ -0,0 +1,58 @@ +@use "./colours"; + +@mixin raised_button { + background: rgba(0, 0, 0, 0.05); + border: 1px solid #ccc; + box-shadow: 1px 1px 1px #ccc; + border-top-right-radius: 8px; + border-bottom-right-radius: 8px; +} + +@mixin uncut_raised_button { + @include raised_button; + border-radius: 8px; + + button { + background: unset; + border: unset; + box-shadow: unset; + color: black; + + &:hover { + color: black; + } + + @include colours.highlight; + } +} + +@mixin pill-button($color:false, $button-color: #F80) { + background-image: url("/systems/DSA_4-1/assets/velvet_button.png"); + background-color: $color; + background-repeat: repeat-y; + background-size: cover; + @if $color { + background-blend-mode: overlay; + } + border-color: $button-color; + border-width: 1px; + border-style: solid; + border-radius: 8px; + box-shadow: 2px 2px 4px #000; + color: $button-color; + cursor: pointer; + display: inline-block; + text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.2); + padding: 0 8px; + height: 28px; + line-height: 28px; + vertical-align: middle; +} + +@mixin mini-button($color:false, $button-color: #F80) { + @include pill-button($color, $button-color); + width: 100%; + border-radius: 2px; + height: 24px; + line-height: 24px; +} \ No newline at end of file diff --git a/src/style/atoms/_colours.scss b/src/style/atoms/_colours.scss index e495c79a..774e4429 100644 --- a/src/style/atoms/_colours.scss +++ b/src/style/atoms/_colours.scss @@ -1,25 +1,25 @@ $nachteil-color: #555753; $nachteil-text-color: #FFF; -$liturgie-color: #edd400; +$liturgie-color: #59aebf; $liturgie-text-color: #000; $zauber-color: #3465a4; $zauber-text-color: #000; $talent-color: #f57900; $talent-text-color: #000; -$kampftalent-color: #cc0000; +$kampftalent-color: #c36e14; $kampftalent-text-color: #FFF; -$talent-body-color: #16bd6c; +$talent-body-color: #45d18c; $talent-body-text-color: #000; -$talent-nature-color: #46800d; +$talent-nature-color: #7cb544; $talent-nature-text-color: #000; -$talent-social-color: #ae9809; +$talent-social-color: #c8588a; $talent-social-text-color: #000; -$talent-knowledge-color: #d319ba; +$talent-knowledge-color: #2ba9c5; $talent-knowledge-text-color: #000; -$talent-language-color: #573bbc; -$talent-language-text-color: #fff; -$talent-crafting-color: #ae6813; +$talent-language-color: #957fe8; +$talent-language-text-color: #000; +$talent-crafting-color: #cd9551; $talent-crafting-text-color: #000; @@ -83,4 +83,14 @@ $special-action: rgba(68, 34, 204, 0.8); $special-action-color: #000; +@mixin highlight { + &:hover { + text-shadow: 0 0 10px red; + } + cursor: pointer; +} +@mixin svg-highlight { + filter: drop-shadow(0px 0px 10px rgb(255 0 0)); + cursor: pointer; +} \ No newline at end of file diff --git a/src/style/atoms/_typography.scss b/src/style/atoms/_typography.scss index 0827abfe..cdbf65cc 100644 --- a/src/style/atoms/_typography.scss +++ b/src/style/atoms/_typography.scss @@ -24,16 +24,21 @@ } input, - .rkp .pill, .cooldown > span, .attribute.rollable > .name, .attribute.rollable > .wert { font-family: Andalus, sans-serif; } + .mini-skill, .mini-weaponskill, .mini-language-skill, .mini-liturgy, .mini-spell, .editor.prosemirror.active, .editor.prosemirror.inactive { font-family: Gentium, sans-serif; } + .mini-skill, .mini-weaponskill, .mini-language-skill, .mini-liturgy, .mini-spell { + font-weight: bold; + text-transform: capitalize; + } + } diff --git a/src/style/molecules/_attribute-die.scss b/src/style/molecules/_attribute-die.scss index c3126fcf..484520a5 100644 --- a/src/style/molecules/_attribute-die.scss +++ b/src/style/molecules/_attribute-die.scss @@ -31,11 +31,12 @@ position: absolute; left: -1px; width: 48px; - top: -1px; - font-size: smaller; + top: 2px; + font-size: large; line-height: 48px; vertical-align: middle; text-align: center; + @if $darkmode { color: colours.$attribute-die-label-color-darkmode; } @else { @@ -62,7 +63,7 @@ &.colorfulDice { - &.Mut { + .Mut { .die svg path { fill: colours.$attribute-die-co-color; diff --git a/src/style/molecules/_equipment.scss b/src/style/molecules/_equipment.scss index 8b541f92..1ff14ae4 100644 --- a/src/style/molecules/_equipment.scss +++ b/src/style/molecules/_equipment.scss @@ -1,4 +1,5 @@ @use "../atoms/assets"; +@use "../atoms/colours" as colour; @mixin equipment($darkmode: false) { position: relative; @@ -9,14 +10,12 @@ padding: 2px 0 0 2px; margin: 4px 0 0 4px; gap: 8px; - @if darkmode { - background: rgba(0, 0, 0, 0.3); - } @else { - background: assets.$tab-pane-background; - } + background: assets.$tab-pane-background; .icon { - width: 32px; + object-fit: cover; + max-width: 32px; + max-height: 32px; padding: 0; } @@ -46,7 +45,7 @@ &:hover { .name { - text-shadow: 0 0 10px rgb(255 0 0); + @include colour.highlight; } border: 1px solid #ccc; @@ -55,4 +54,12 @@ padding: 0 4px 4px 0; z-index: 2; } + + &.worn { + + .name { + font-weight: bold; + } + + } } \ No newline at end of file diff --git a/src/style/molecules/_liturgy-banner.scss b/src/style/molecules/_liturgy-banner.scss index 75b73fe7..affcde17 100644 --- a/src/style/molecules/_liturgy-banner.scss +++ b/src/style/molecules/_liturgy-banner.scss @@ -205,24 +205,29 @@ $deity_colours_tint: ( .liturgy.rollable { width: 24px; + padding: 0; svg { - $color: #e4de61; - width: 24px; - height: 24px; + + $color: colour.$liturgie-color; + width: 32px; + height: 32px; top: 1px; z-index: 1; position: relative; - path { fill: $color; } } + + &:hover { + @include colour.svg-highlight; + } } - .clickable:hover { - text-shadow: 0 0 10px rgb(255 0 0); + .clickable { + @include colour.highlight; } } diff --git a/src/style/molecules/_rollable.scss b/src/style/molecules/_rollable.scss index a1c7cb5b..ca2d0893 100644 --- a/src/style/molecules/_rollable.scss +++ b/src/style/molecules/_rollable.scss @@ -2,6 +2,7 @@ @use 'sass:map'; @use "../atoms/colours" as colour; @use "../atoms/numbers" as numbers; +@use "../atoms/buttons"; $rollable_colours: ( "nachteil": colour.$nachteil-color, @@ -36,7 +37,7 @@ $rollable_colours_font: ( display: block; height: 32px; position: relative; - margin: 4px; + margin: 4px 32px 4px 4px; .die { width: 32px; @@ -52,7 +53,7 @@ $rollable_colours_font: ( .value { position: absolute; left: 0; - top: 0; + top: 1px; width: 32px; line-height: 30px; vertical-align: middle; @@ -69,6 +70,24 @@ $rollable_colours_font: ( top: 6px; } } + + &:active { + + + scale: 0.8; + + .value { + + img { + position: absolute; + width: 20px; + height: 20px; + left: 4px; + top: 7px; + } + } + + } } .container { @@ -122,43 +141,30 @@ $rollable_colours_font: ( } } } + + .outside { + position: absolute; + right: -24px; + top: 8px; + bottom: 8px; + z-index: 3; + + @include colour.highlight; + } } // interactivity .block.rollable { - &:hover { + .die:hover { - .die svg { - filter: drop-shadow(0px 0px 10px rgb(255 0 0)); + svg { + @include colour.svg-highlight; } } - &:active { - .die { - width: 28px; - height: 28px; - left: 2px; - top: 2px; - - .value { - width: 28px; - height: 28px; - left: -1px; - top: -2px; - - img { - position: absolute; - width: 20px; - height: 20px; - left: 4px; - top: 7px; - } - } - } - } } } @@ -168,7 +174,6 @@ $rollable_colours_font: ( .#{$name}.rollable { .die { - stroke-width: 0.5; svg { @@ -176,8 +181,7 @@ $rollable_colours_font: ( path { fill: $color; - stroke: colour.$rollable-die-border-color; - stroke-width: 5px; + } } @@ -189,9 +193,8 @@ $rollable_colours_font: ( } .container { - background-image: linear-gradient(to right, rgba($color, numbers.$start_gradient), rgba($color, numbers.$end_gradient)); - border-top-right-radius: 8px; - border-bottom-right-radius: 8px; + // background-image: linear-gradient(to right, rgba($color, numbers.$start_gradient), rgba($color, numbers.$end_gradient)); + @include buttons.raised_button; } } } diff --git a/src/style/molecules/_sidebar-elements.scss b/src/style/molecules/_sidebar-elements.scss index 2ec297a8..f8a5ba5c 100644 --- a/src/style/molecules/_sidebar-elements.scss +++ b/src/style/molecules/_sidebar-elements.scss @@ -1,5 +1,6 @@ @use "../atoms/colours" as colors; @use "sass:color"; +@use "../atoms/buttons"; .dsa41.sheet.actor.character { @@ -11,7 +12,7 @@ width: 100%; position: relative; border: 1px inset #ccc; - background-color: rgba(0, 0, 0, 0.2); + background-color: rgba(0, 0, 0, 0.5); margin: 8px 0; label { @@ -19,8 +20,6 @@ left: 0; top: 0; bottom: 0; - width: 48px; - background-color: rgba(0, 0, 0, 0.1); z-index: 3; line-height: 24px; vertical-align: middle; @@ -112,9 +111,7 @@ cursor: pointer; } - &:hover { - text-shadow: 0 0 2px rgba(255, 0, 0, 1); - } + @include colors.highlight; } .pa { @@ -126,21 +123,84 @@ cursor: pointer; } - &:hover { - text-shadow: 0 0 2px rgba(255, 0, 0, 1); - } + @include colors.highlight; } .ini { grid-area: ini; + cursor: pointer; + + label { + cursor: pointer; + } + + @include colors.highlight; } .tp { grid-area: tp; + cursor: pointer; + label { + cursor: pointer; + } + + @include colors.highlight; } } + .favourites { + + h3 { + text-align: center; + margin: 8px 0; + } + + .mini-skill, .mini-weaponskill, .mini-language-skill, .mini-spell, .mini-liturgy { + margin-bottom: 4px; + + + @include colors.highlight; + } + + .mini-skill.Körperlich { + @include buttons.mini-button($color: color.scale(colors.$talent-body-color, $lightness: -50%), $button-color: gold); + } + + .mini-skill.Gesellschaft { + @include buttons.mini-button($color: color.scale(colors.$talent-social-color, $lightness: -50%), $button-color: gold); + } + + .mini-skill.Natur { + @include buttons.mini-button($color: color.scale(colors.$talent-nature-color, $lightness: -50%), $button-color: gold); + } + + .mini-skill.Wissen { + @include buttons.mini-button($color: color.scale(colors.$talent-knowledge-color, $lightness: -50%), $button-color: gold); + } + + .mini-skill.Handwerk { + @include buttons.mini-button($color: color.scale(colors.$talent-crafting-color, $lightness: -50%), $button-color: gold); + } + + .mini-weaponskill { + @include buttons.mini-button($color: color.scale(colors.$kampftalent-color, $lightness: -50%), $button-color: gold); + } + + .mini-language-skill { + @include buttons.mini-button($color: color.scale(colors.$talent-language-color, $lightness: -50%), $button-color: gold); + } + + .mini-spell { + @include buttons.mini-button($color: colors.$zauber-color, $button-color: gold); + } + + .mini-liturgy { + @include buttons.mini-button($color: colors.$liturgie-color, $button-color: gold); + } + + } + .cooldowns { h3 { @@ -208,22 +268,13 @@ position: absolute; background: url('/systems/DSA_4-1/assets/gradient.png'); background-size: 12px 100%; + background-blend-mode: overlay; + background-color: rgba(128, 0, 192, 1); left: 0; bottom: 0; top: 0; border-radius: 12px; z-index: 2; - - &::after { // progress fill - content: ''; - position: absolute; - left: 0; - top: 0; - right: 0; - bottom: 0; - border-radius: 12px; - background-color: rgba(128, 0, 192, 0.5); - } } &::after { @@ -238,20 +289,20 @@ } - &.Kampf .progress::after { - background-color: rgba(colors.$kampftalent-color, 0.5); + &.Kampf .progress { + background-color: rgba(colors.$kampftalent-color, 1); } - &.Karmal .progress::after { - background-color: rgba(colors.$liturgie-color, 0.5); + &.Karmal .progress { + background-color: rgba(colors.$liturgie-color, 1); } - &.Magisch .progress::after { - background-color: rgba(colors.$zauber-color, 0.5); + &.Magisch .progress { + background-color: rgba(colors.$zauber-color, 1); } - &.Talent .progress::after { - background-color: rgba(colors.$talent-color, 0.5); + &.Talent .progress { + background-color: rgba(colors.$talent-color, 1); } } diff --git a/src/style/organisms/_character-sheet.scss b/src/style/organisms/_character-sheet.scss index 5b452e73..52e67a4b 100644 --- a/src/style/organisms/_character-sheet.scss +++ b/src/style/organisms/_character-sheet.scss @@ -5,10 +5,11 @@ @use "./character-tabs/social"; @use "./character-tabs/advsf"; @use "./character-tabs/inventory"; -@use "./character-tabs/combat"; +@use "./character-tabs/health"; @use "./character-tabs/spells"; @use "./character-tabs/liturgies"; @use "./character-tabs/skills"; +@use "../atoms/buttons"; .application.sheet.dsa41.actor.character { @@ -36,6 +37,14 @@ font-size: large; } + .rkp { + + .pill { + @include buttons.pill-button; + } + + } + } div.head-data { @@ -50,6 +59,7 @@ .profile-img { width: $sidebar-width - 16px; } + } nav.sheet-tabs.tabs { @@ -86,8 +96,8 @@ @include inventory.tab; } - .tab.combat.active { - @include combat.tab; + .tab.health.active { + @include health.tab; } .tab.skills.active { @@ -102,6 +112,76 @@ @include liturgies.tab; } + .sidebuttons { + display: none; + } + } + + &.tiny:not(.minimized):not(.minimizing) { + + .header-fields { + & > div:not(.attributes) { + display: none; + } + + & > div.attributes { + position: absolute; + left: -8px; + top: -8px; + right: -8px; + display: grid; + gap: 8px; + grid-template-columns: repeat(4, 1fr); + } + + } + + div.head-data { + width: unset; + top: 136px; + left: 8px; + right: 8px; + bottom: 8px; + + .profile-img { + justify-self: center; + } + } + + .sheet-tabs { + display: none; + } + + section.tab.tab.tab { + display: none; + } + + .sidebuttons { + position: fixed; + display: block; + margin-left: 27px; + margin-top: 48px; + pointer-events: none; + rotate: 90deg; + transform-origin: top left; + + div { + display: flex; + gap: 4px; + + button { + background-image: url("/systems/DSA_4-1/assets/velvet_button.png"); + background-repeat: repeat-y; + background-size: cover; + color: white; + font-weight: bold; + pointer-events: all; + border-radius: 0; + border-top-left-radius: 8px; + border-top-right-radius: 8px; + } + } + } } } @@ -126,8 +206,8 @@ @include inventory.tab(true); } - .tab.combat.active { - @include combat.tab(true); + .tab.health.active { + @include health.tab(true); } .tab.skills.active { diff --git a/src/style/organisms/_combat-action-dialog.scss b/src/style/organisms/_combat-action-dialog.scss index 274d1032..e5359f2b 100644 --- a/src/style/organisms/_combat-action-dialog.scss +++ b/src/style/organisms/_combat-action-dialog.scss @@ -45,6 +45,8 @@ li { + cursor: pointer; + height: 32px; display: grid; line-height: 32px; @@ -63,6 +65,7 @@ } &.name-only { + padding-left: 40px; display: block; } } diff --git a/src/style/organisms/_group-sheet.scss b/src/style/organisms/_group-sheet.scss index 00635e88..4d4aa441 100644 --- a/src/style/organisms/_group-sheet.scss +++ b/src/style/organisms/_group-sheet.scss @@ -1,3 +1,5 @@ +@use "../atoms/colours" as colour; + .application.sheet.dsa41.actor.group { .sheet-header { @@ -30,8 +32,8 @@ } } - .rollable:hover { - text-shadow: 0 0 10px rgb(255 0 0); + .rollable { + @include colour.highlight; } .character { @@ -131,9 +133,7 @@ display: inline-block; padding: 0 4px; - &:hover { - text-shadow: 0 0 10px rgb(255 0 0); - } + @include colour.highlight; } } } @@ -168,7 +168,7 @@ &:hover { .item-name { - text-shadow: 0 0 10px rgb(255 0 0); + @include colour.highlight; } } } diff --git a/src/style/organisms/_item-browser-dialog.scss b/src/style/organisms/_item-browser-dialog.scss new file mode 100644 index 00000000..9636d2df --- /dev/null +++ b/src/style/organisms/_item-browser-dialog.scss @@ -0,0 +1,102 @@ +.dsa41.dialog.item-browser { + + .window-content [data-application-part] { + + position: relative; + + .progress { + position: absolute; + top: 0; + left: 0; + right: 0; + height: 24px; + border-radius: 4px; + background-color: rgba(0, 0, 0, 0.3); + + .fill { + position: absolute; + left: 0; + top: 0; + bottom: 0; + background: url('/systems/DSA_4-1/assets/gradient.png'); + background-size: 24px 100%; + background-color: rgba(64, 192, 192, 0.8); + background-blend-mode: multiply; + } + + .text { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + height: 24px; + line-height: 24px; + vertical-align: middle; + font-weight: bold; + margin-left: 8px; + text-shadow: 2px 2px 0 rgba(0, 0, 0, 0.3); + color: white; + } + + + } + + display: grid; + grid-template-columns: 240px 1fr; + height: 100%; + gap: 16px; + + aside { + width: 240px; + } + + .scrollable-table { + + display: grid; + grid-template-rows: 32px 1fr; + overflow: hidden; + + .header { + display: grid; + grid-template-columns: 64px 1fr 120px 120px 120px; + gap: 4px; + background-color: rgba(0, 0, 0, 0.3); + + * { + font-weight: bold; + line-height: 32px; + vertical-align: middle; + } + } + + .scroll-y { + overflow-x: hidden; + overflow-y: auto; + + border: 1px inset #ccc; + + .item { + display: grid; + grid-template-columns: 64px 1fr 120px 120px 120px; + gap: 4px; + + &:nth-child(odd) { + background-color: rgba(0, 0, 0, 0.1); + } + + &.selected { + background-color: rgba(255, 140, 0, 0.2); + border: 1px solid orange; + } + } + + } + + + } + + } + + +} \ No newline at end of file diff --git a/src/style/organisms/_merchant-sheet.scss b/src/style/organisms/_merchant-sheet.scss index 8325fdf6..1eba2f36 100644 --- a/src/style/organisms/_merchant-sheet.scss +++ b/src/style/organisms/_merchant-sheet.scss @@ -1,3 +1,6 @@ +@use "../atoms/buttons"; + + .dsa41.actor.merchant { .tab.goods.active { @@ -22,9 +25,12 @@ overflow-y: auto; .merchant-good { + @include buttons.uncut_raised_button; + width: 100%; display: grid; break-inside: avoid; + grid-template-columns: 32px 1fr 32px; grid-template-rows: 16px 16px; grid-template-areas: "img title options" "img price options"; @@ -39,6 +45,7 @@ button { grid-area: options; + height: 100%; } .price { @@ -57,12 +64,7 @@ overflow-y: auto; .service { - - border: 1px inset rgba(0, 0, 0, 1); - background-color: rgba(0, 0, 0, 0.2); - border-radius: 4px; - padding: 4px; - margin: 4px 0; + @include buttons.uncut_raised_button; display: grid; grid-template-columns: 32px 1fr 32px; @@ -105,6 +107,9 @@ } &.infinite { + width: 32px; + bottom: -2px; + &::before { content: '∞'; } diff --git a/src/style/organisms/_resting-dialog.scss b/src/style/organisms/_resting-dialog.scss index f2b212b6..6d6ef938 100644 --- a/src/style/organisms/_resting-dialog.scss +++ b/src/style/organisms/_resting-dialog.scss @@ -14,6 +14,10 @@ margin-left: 32px; padding-bottom: 16px; + &.setting-nest-1 { + margin-left: 64px; + } + &.range { padding-bottom: 0; margin-left: 34px; diff --git a/src/style/organisms/character-tabs/_advsf.scss b/src/style/organisms/character-tabs/_advsf.scss index 7d7834c9..26022e6f 100644 --- a/src/style/organisms/character-tabs/_advsf.scss +++ b/src/style/organisms/character-tabs/_advsf.scss @@ -1,3 +1,5 @@ +@use "../../atoms/buttons"; + @mixin tab($darkmode: false) { .advantages-and-specialabilities { @@ -21,19 +23,9 @@ .advantage, .special-ability, .flaw { 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("/systems/DSA_4-1/assets/velvet_button.png"); - background-repeat: repeat-y; - background-size: cover; + @include buttons.pill-button($color: rgb(0, 128, 0), $button-color: gold); span { position: relative; @@ -43,20 +35,7 @@ } &.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); + @include buttons.pill-button($color: rgb(128, 0, 96), $button-color: gold); } & + .advantage, & + .special-ability { @@ -80,21 +59,23 @@ width: 24px; } - &::after { - background: rgba(106, 36, 216, 0.5); - } - + @include buttons.pill-button($color: rgb(106, 36, 216), $button-color: gold); } &.disadvantage { font-style: italic; - - &::after { - background: rgba(128, 0, 0, 0.5); - } + @include buttons.pill-button($color: rgb(128, 0, 0), $button-color: gold); } } } } } + +.dsa41.actor.character.standalone.advsf { + @include tab; + + .advantages-and-specialabilities { + flex-direction: column; + } +} diff --git a/src/style/organisms/character-tabs/_combat.scss b/src/style/organisms/character-tabs/_health.scss similarity index 56% rename from src/style/organisms/character-tabs/_combat.scss rename to src/style/organisms/character-tabs/_health.scss index 1e3163b7..52769dbe 100644 --- a/src/style/organisms/character-tabs/_combat.scss +++ b/src/style/organisms/character-tabs/_health.scss @@ -3,8 +3,8 @@ display: grid; grid-template-columns: 1fr 320px; - grid-template-rows: 32px 32px 1fr; - grid-template-areas: "res res" "wounds wounds" "actions actions" "cooldowns cooldowns"; + grid-template-rows: 32px 32px 32px 1fr; + grid-template-areas: "res res" "wounds wounds" "fatigue fatigue" "effects effects" "effects effects"; padding: 8px; gap: 8px; @@ -31,7 +31,7 @@ } - .wounds { + .wounds, .fatigue { position: relative; height: 24px; display: flex; @@ -90,22 +90,46 @@ } - .actions { - grid-area: actions; - padding: 0; + .fatigue { + grid-area: fatigue; + flex-direction: row; - .grid-of-actions { - display: unset; + label { + span { + display: inline-block; + height: 26px; + line-height: 26px; + vertical-align: middle; + } + + input { + position: absolute; + right: 0; + height: 26px; + text-align: center; + padding: 0; + margin: 0; + width: 28px; + } } + + .filled-segment { + background-color: rgba(0, 128, 0, 0.8); + + &.danger { + background-color: rgba(255, 0, 0, 0.8); + } + } + } - .cooldowns { - grid-area: cooldowns; + .effects { + grid-area: effects; } &.zones { - grid-template-areas: "res res" "wounds wounds" "actions paperdoll" "cooldowns paperdoll"; + grid-template-areas: "res res" "wounds wounds" "fatigue fatigue" "effects paperdoll" "effects paperdoll"; .paperdoll { grid-area: paperdoll; @@ -230,4 +254,145 @@ } +} + +.mini-health { + @include tab; + + display: flex; + flex-direction: column; + padding: 0; + + input { + height: 26px !important; + text-align: center !important; + padding: 0 !important; + margin: 0 !important; + width: 28px !important; + } + + .side-by-side { + display: flex; + + > * { + flex: 1; + } + } + + .paperdoll { + + div { + position: relative; + + .wound { + position: absolute; + width: 32px; + height: 32px; + border-radius: 16px; + border: 1px solid black; + background-color: rgba(0, 0, 0, 0.5); + box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.3); + line-height: 32px; + vertical-align: middle; + text-align: center; + color: red; + + + &.armlinks { + top: 146px; + left: 210px; + } + + &.armrechts { + top: 146px; + left: 60px; + } + + &.beinlinks { + top: 346px; + left: 210px; + } + + &.beinrechts { + top: 346px; + left: 60px; + } + + &.bauch { + top: 166px; + left: 136px; + } + + &.kopf { + top: 6px; + left: 136px + } + + &.brust { + top: 86px; + left: 110px; + } + + &.ruecken { + top: 86px; + left: 160px; + } + + } + + .armor { + position: absolute; + width: 32px; + height: 32px; + border-radius: 0 0 16px 16px; + line-height: 32px; + vertical-align: middle; + text-align: center; + border: 1px solid silver; + background-color: rgba(0, 0, 0, 0.5); + box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.3); + color: silver; + + &.armlinks { + top: 180px; + left: 210px; + } + + &.armrechts { + top: 180px; + left: 60px; + } + + &.beinlinks { + top: 380px; + left: 210px; + } + + &.beinrechts { + top: 380px; + left: 60px; + } + + &.bauch { + top: 200px; + left: 136px; + } + + &.kopf { + top: 40px; + left: 136px + } + + &.ruecken { + top: 120px; + left: 160px; + } + + &.brust { + top: 120px; + left: 110px; + } + } + } + } } \ No newline at end of file diff --git a/src/style/organisms/character-tabs/_inventory.scss b/src/style/organisms/character-tabs/_inventory.scss index 3ad244aa..d8c96658 100644 --- a/src/style/organisms/character-tabs/_inventory.scss +++ b/src/style/organisms/character-tabs/_inventory.scss @@ -28,7 +28,7 @@ position: relative; border: 1px inset #ccc; background-color: rgba(0, 0, 0, 0.2); - height: 8px; + height: 16px; span.fill { position: absolute; @@ -56,11 +56,29 @@ } } - h3.inventory-header { + .inventory-header { line-height: 32px; grid-area: inventory-header; margin: 0; padding: 0; + display: flex; + + h3 { + flex: 1; + line-height: 32px; + } + + .buttons { + flex: 0; + display: flex; + gap: 4px; + + button { + + } + } + + } @@ -81,5 +99,67 @@ } } + + h3.equipment-header { + line-height: 32px; + grid-area: equipment-header; + margin: 0; + padding: 0; + } + + .set { + grid-area: equipment; + margin: 0; + padding: 0; + } } } + +.dsa41.sheet.actor.character.standalone.bagpack { + @include tab; + + + .coinage { + position: absolute; + bottom: 0; + right: 8px; + height: 38px; + line-height: 38px; + vertical-align: middle; + } + + .capacity { + position: absolute; + top: 0; + left: 0; + right: 0; + } + + .buttons { + position: absolute; + bottom: 4px; + left: 4px; + + button { + display: inline-block; + } + + } + + .inventory-table { + overflow-x: hidden; + overflow-y: auto; + + position: absolute; + left: 4px; + top: 4px; + right: 4px; + bottom: 38px; + + .equipment { + @include equipment.equipment + } + + } +} + diff --git a/src/style/organisms/character-tabs/_skills.scss b/src/style/organisms/character-tabs/_skills.scss index 3a2a7e4f..e9acdc57 100644 --- a/src/style/organisms/character-tabs/_skills.scss +++ b/src/style/organisms/character-tabs/_skills.scss @@ -17,6 +17,13 @@ gap: unset; padding: unset; + fieldset { + margin-top: 0; + margin-bottom: 0; + padding-top: 0; + padding-bottom: 0; + } + } ul { @@ -38,4 +45,18 @@ } } +} + +.mini-skills { + overflow-x: hidden; + overflow-y: auto; + height: 100%; + @include tab; + + container: unset; + + ul { + list-style-type: none; + padding-left: 0; + } } \ No newline at end of file diff --git a/src/style/organisms/character-tabs/_spells.scss b/src/style/organisms/character-tabs/_spells.scss index 8a47be00..93c98bc6 100644 --- a/src/style/organisms/character-tabs/_spells.scss +++ b/src/style/organisms/character-tabs/_spells.scss @@ -27,12 +27,16 @@ z-index: 2; top: 0; left: -4px; - display: none; path { fill: colour.$zauber-color; } } + + &:hover { + @include colour.svg-highlight; + } + } @@ -44,6 +48,8 @@ span { position: relative; z-index: 1; + + @include colour.highlight; } } @@ -59,9 +65,9 @@ border-top-right-radius: 8px; border-bottom-right-radius: 8px; position: absolute; - top: 2px; left: 12px; bottom: 2px; + height: 2px; right: 33%; z-index: 0; pointer-events: none; @@ -87,4 +93,61 @@ padding: 0 4px; } } +} + +.mini-spells { + height: 100%; + display: flex; + flex-direction: column; + + .table-header { + display: grid; + grid-template-columns: 1fr 100px 30px; + padding: 0 8px; + font-weight: bold; + } + + .scrollable-table { + overflow-x: hidden; + overflow-y: auto; + + thead { + display: none; + + } + + + tr { + + td:nth-child(1) { + width: 32px; + } + + td:nth-child(3) { + width: 100px; + } + + td:nth-child(4) { + width: 30px; + } + + .spell.rollable { + position: relative; + + svg { + + position: absolute; + width: 32px; + height: 32px; + z-index: 2; + top: 0; + left: -4px; + + path { + fill: colour.$zauber-color; + } + } + } + } + } } \ No newline at end of file diff --git a/src/style/styles.scss b/src/style/styles.scss index e78462f6..070b26d5 100644 --- a/src/style/styles.scss +++ b/src/style/styles.scss @@ -35,4 +35,5 @@ @use "organisms/battle-dialog"; @use "organisms/liturgy-sheet"; @use "organisms/dialog"; -@use "organisms/deity-sheet"; \ No newline at end of file +@use "organisms/deity-sheet"; +@use "organisms/item-browser-dialog"; \ No newline at end of file diff --git a/src/system.json b/src/system.json index 94324e87..2c5045b7 100644 --- a/src/system.json +++ b/src/system.json @@ -2,11 +2,12 @@ "id": "DSA_4-1", "title": "Das Schwarze Auge 4.1", "description": "Noch ein Spielsystem für Das Schwarze Auge 4.1", - "version": "0.5.1-rc1", + "version": "0.0.1", "compatibility": { "minimum": 12, "verified": 13 }, + "dependencies": [], "authors": [ { "name": "GrandpaPoppy" @@ -340,7 +341,16 @@ } } }, - "socket": false, + "socket": true, + "relationships": { + "requires": [ + { + "id": "socketlib", + "type": "module", + "manifest": "https://github.com/farling42/foundryvtt-socketlib/releases/latest/download/module.json" + } + ] + }, "initiative": "1d6", "grid": { "distance": 1, @@ -349,5 +359,5 @@ "primaryTokenAttribute": "lep.aktuell", "url": "https://git.macniel.online/macniel/foundry-dsa41-game", "manifest": "https://git.macniel.online/macniel/foundry-dsa41-game/raw/branch/main/src/system.json", - "download": "https://git.macniel.online/macniel/foundry-dsa41-game/releases/download/0.5.1-rc1/release.zip" + "download": "https://git.macniel.online/macniel/foundry-dsa41-game/releases/download/0.0.1/release.zip" } diff --git a/src/templates/actor/character/main-sheet.hbs b/src/templates/actor/character/main-sheet.hbs index e4420fc3..611361ed 100644 --- a/src/templates/actor/character/main-sheet.hbs +++ b/src/templates/actor/character/main-sheet.hbs @@ -32,12 +32,14 @@ {{#if ausdauer}} {{/if}} @@ -62,25 +64,34 @@

{{this.using}} ({{this.name}})

{{#if this.at}} - \ No newline at end of file diff --git a/src/templates/actor/character/tab-combat.hbs b/src/templates/actor/character/tab-health.hbs similarity index 72% rename from src/templates/actor/character/tab-combat.hbs rename to src/templates/actor/character/tab-health.hbs index 67511b0f..dab36be2 100644 --- a/src/templates/actor/character/tab-combat.hbs +++ b/src/templates/actor/character/tab-health.hbs @@ -1,7 +1,6 @@ -
- +
@@ -43,35 +42,54 @@
{{#if (not trefferzonen)}}
- + {{#each this.woundsFilled}} {{#if this}} -
{{@index}}
{{else}} -
{{@index}}
+
{{@index}}
{{/if}} {{/each}}
{{/if}} + {{#if withErschoepfung}} +
+ + {{#each this.erschoepfungFilled}} + {{#if this}} +
{{@index}}
+ {{else}} +
{{@index}}
+ {{/if}} + {{/each}} -
-

Aktionen im Kampf

-
-
- Angriff - variabel -
-
- Verteidigen - variabel -
- - {{#each this.actions}} - {{> "systems/DSA_4-1/templates/ui/partial-action-button.hbs" this}} - {{/each}} + {{#if (gt this.ueberanstrengung 0)}} +
{{system.erschoepfung.aktuell}}
+ {{else}} +
+ {{/if}}
+ {{/if}} + +
+ +
    + {{#each effects}} +
  • {{this}}
  • + {{/each}} +
{{#if (or trefferzonen zonenruestung)}} @@ -111,5 +129,4 @@
{{/if}} -
diff --git a/src/templates/actor/character/tab-set.hbs b/src/templates/actor/character/tab-set.hbs index 43b5bb2c..cde2bd41 100644 --- a/src/templates/actor/character/tab-set.hbs +++ b/src/templates/actor/character/tab-set.hbs @@ -1,29 +1,25 @@ -
+
+ + {{#each this.slots}} +
+ {{/each}} + {{!-- + {{#if (eq ../actor.system.setEquipped @index)}} + + {{else}} + + {{/if}} -
- - - + --}} - {{#each this.slots}} -
- {{/each}} - {{#if (eq ../actor.system.setEquipped @index)}} - - {{else}} - - {{/if}} - -
- -
\ No newline at end of file + \ No newline at end of file diff --git a/src/templates/actor/character/tab-skills.hbs b/src/templates/actor/character/tab-skills.hbs index 26c619c2..49203fec 100644 --- a/src/templates/actor/character/tab-skills.hbs +++ b/src/templates/actor/character/tab-skills.hbs @@ -2,8 +2,9 @@ data-tab="{{tabs.skills.id}}" data-group="{{tabs.skills.group}}">
-
-

Kampftalente

+
+
+

Kampftalente

    {{#each skills.Kampf}}
  • @@ -11,9 +12,11 @@
  • {{/each}}
+
-
-

Körperliche Talente

+
+
+

Körperliche Talente

  • {{#each skills.Körperlich}} @@ -22,9 +25,11 @@
  • {{/each}}
+
-
-

Gesellschaftliche Talente

+ -
-

Natur Talente

+
+
+

Natur Talente

  • {{#each skills.Natur}} @@ -44,9 +51,11 @@
  • {{/each}}
+
-
-

Wissenstalente

+
+
+

Wissenstalente

  • {{#each skills.Wissen}} @@ -55,9 +64,11 @@
  • {{/each}}
+
-
-

Schriften & Sprachen

+
+
+

Schriften & Sprachen

  • {{#each skills.Schriften}} @@ -71,9 +82,11 @@
  • {{/each}}
+
-
-

Handwerkliche Talente

+
+
+

Handwerkliche Talente

  • {{#each skills.Handwerk}} @@ -82,6 +95,7 @@
  • {{/each}}
+
\ No newline at end of file diff --git a/src/templates/actor/character/tab-spells.hbs b/src/templates/actor/character/tab-spells.hbs index 46ec9463..42ca511e 100644 --- a/src/templates/actor/character/tab-spells.hbs +++ b/src/templates/actor/character/tab-spells.hbs @@ -25,6 +25,7 @@ Probe ZfW Merkmale + @@ -33,7 +34,7 @@ {{> 'systems/DSA_4-1/templates/ui/partial-die.hbs' }} - + {{this.name}} {{this.eigenschaft1}} {{this.eigenschaft2}} @@ -43,6 +44,15 @@ + +
+ {{#if this.fav}} + + {{else}} + + {{/if}} +
+ {{/each}} diff --git a/src/templates/actor/merchant/tab-services.hbs b/src/templates/actor/merchant/tab-services.hbs index 6f69dd1d..ca964624 100644 --- a/src/templates/actor/merchant/tab-services.hbs +++ b/src/templates/actor/merchant/tab-services.hbs @@ -40,7 +40,8 @@ {{this.name}} {{currency this.price}} {{this.availability}} + (lte this.availability 0)}}infinite{{/if}}">{{#unless + (lte this.availability 0)}}{{this.availability}}{{/unless}} {{/if}} diff --git a/src/templates/dialog/combat-action.hbs b/src/templates/dialog/combat-action.hbs index e39875e7..8fdad05a 100644 --- a/src/templates/dialog/combat-action.hbs +++ b/src/templates/dialog/combat-action.hbs @@ -5,7 +5,7 @@