From a57cf98aa177da55387cff70268ad9fcc23df425 Mon Sep 17 00:00:00 2001 From: macniel Date: Sun, 26 Oct 2025 17:44:08 +0100 Subject: [PATCH] implements lootable creatures --- gulpfile.mjs | 1 + src/main.mjs | 70 +++++++++++++ src/module/data/creature.mjs | 10 +- src/module/sheets/characterSheet.mjs | 32 +++--- src/module/sheets/creatureSheet.mjs | 97 ++++++++++++++++++- src/module/sheets/merchantSheet.mjs | 3 +- src/style/molecules/_equipment.scss | 54 +++++++++++ src/style/organisms/_creature-sheet.scss | 11 +++ .../organisms/character-tabs/_inventory.scss | 52 +--------- src/templates/actor/creature/tab-loot.hbs | 7 ++ .../actor/creature/tab-visibility.hbs | 18 ++++ 11 files changed, 281 insertions(+), 74 deletions(-) create mode 100644 src/style/molecules/_equipment.scss create mode 100644 src/templates/actor/creature/tab-loot.hbs create mode 100644 src/templates/actor/creature/tab-visibility.hbs diff --git a/gulpfile.mjs b/gulpfile.mjs index 414fbd13..22138b5f 100644 --- a/gulpfile.mjs +++ b/gulpfile.mjs @@ -50,6 +50,7 @@ const convert = function (from, to, ofType, overwrite = true) { if (statSync(join(source, file)).isDirectory()) { filewalker(join(source, file)); } else { + console.debug("processing file", join(source, file)) let originalSource = JSON.parse(readFileSync(join(source, file), {encoding: "utf8"})); let id = randomID(); diff --git a/src/main.mjs b/src/main.mjs index eee9babc..c2795c7b 100644 --- a/src/main.mjs +++ b/src/main.mjs @@ -88,6 +88,76 @@ Hooks.once("init", () => { decimals: 0 } + const setMovementSpeeds = () => { + const movementActions = CONFIG.Token.movement.actions + + for (const key of ["swim", "climb", "crawl", "walk", "drive", "ride", "fly"]) { + delete movementActions[key]?.getCostFunction + } + + movementActions.climb.canSelect = (token) => { + const actor = token.actor | null; + console.log + return (actor.type === "Character" && actor.system.itemTypes["Skill"].find(p => p.name === "Klettern")?.system.taw > 0) || actor.type === "Creature" + } + + movementActions.crawl.canSelect = (token) => { + const actor = token.actor | null; + return actor.type === "Character" || actor.type === "Creature" + } + + movementActions.walk.canSelect = (token) => { + const actor = token.actor | null; + return actor.type === "Character" || actor.type === "Creature" + } + + movementActions.swim = { + label: "TOKEN.MOVEMENT.ACTIONS.swim.label", + icon: "fa-solid fa-swim", + order: 0, + canSelect: (token) => { + const actor = token.actor | null; + return actor.type === "Character" || actor.type === "Creature" + }, + deriveTerrainDifficulty: () => 1, + } + + movementActions.drive = { + label: "TOKEN.MOVEMENT.ACTIONS.drive.label", + icon: "fa-solid fa-car-side", + order: 0, + canSelect: (token) => { + const actor = token.actor | null; + return (actor.type === "Character" && actor.system.itemTypes["Skill"].find(p => p.name === "Fahrzeuge lenken")?.system.taw > 0) || actor.type === "Creature" + }, + deriveTerrainDifficulty: () => 1, + } + + movementActions.ride = { + label: "TOKEN.MOVEMENT.ACTIONS.ride.label", + icon: "fa-solid fa-horse", + order: 0, + canSelect: (token) => { + const actor = token.actor | null; + return (actor.type === "Character" && actor.system.itemTypes["Skill"].find(p => p.name === "Reiten")?.system.taw > 0) || actor.type === "Creature" + }, + deriveTerrainDifficulty: () => 1, + } + + movementActions.fly = { + label: "TOKEN.MOVEMENT.ACTIONS.fly.label", + icon: "fa-solid fa-wings", + order: 0, + canSelect: (token) => { + const actor = token.actor | null; + return (actor.type === "Character" && actor.system.itemTypes["Skill"].find(p => p.name === "Fliegen")?.system.taw > 0) || actor.type === "Creature" + }, + deriveTerrainDifficulty: () => 1, + } + + } + setMovementSpeeds() + console.log("DSA 4.1 is ready for development!") foundry.documents.collections.Actors.registerSheet('dsa41.character', CharacterSheet, { diff --git a/src/module/data/creature.mjs b/src/module/data/creature.mjs index 4725e155..698a2163 100644 --- a/src/module/data/creature.mjs +++ b/src/module/data/creature.mjs @@ -1,5 +1,5 @@ const { - SchemaField, NumberField, StringField, HTMLField, ArrayField + SchemaField, BooleanField, NumberField, StringField, HTMLField, ArrayField } = foundry.data.fields; export class CreatureDataModel extends foundry.abstract.TypeDataModel { @@ -24,7 +24,13 @@ export class CreatureDataModel extends foundry.abstract.TypeDataModel { tp: new StringField(), name: new StringField(), }), {min: 0}), - description: new HTMLField() + description: new HTMLField(), + visibility: new SchemaField({ + meta: new BooleanField(), + attacks: new BooleanField(), + description: new BooleanField(), + loot: new BooleanField(), + }) } } diff --git a/src/module/sheets/characterSheet.mjs b/src/module/sheets/characterSheet.mjs index 85dacd8b..d25b600a 100644 --- a/src/module/sheets/characterSheet.mjs +++ b/src/module/sheets/characterSheet.mjs @@ -220,19 +220,19 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { } - _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) - Equipment._getTabConfig(tabs, this) - Skills._getTabConfig(tabs, this) - Spells._getTabConfig(tabs, this) - Liturgies._getTabConfig(tabs, this) - Effects._getTabConfig(tabs, this) - return tabs - } + _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) + Equipment._getTabConfig(tabs, this) + Skills._getTabConfig(tabs, this) + Spells._getTabConfig(tabs, this) + Liturgies._getTabConfig(tabs, this) + Effects._getTabConfig(tabs, this) + return tabs + } async _preparePartContext(partId, context) { switch (partId) { @@ -494,8 +494,10 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { async _onDrop(event) { const data = TextEditor.implementation.getDragEventData(event); const actor = this.actor; - const allowed = Hooks.call("dropActorSheetData", actor, this, data); - if (allowed === false) return; + //const allowed = Hooks.call("dropActorSheetData", actor, this, data); + //if (allowed === false) return; + + console.log("looted or dropped", data) // Dropped Documents const documentClass = foundry.utils.getDocumentClass(data.type); diff --git a/src/module/sheets/creatureSheet.mjs b/src/module/sheets/creatureSheet.mjs index 93d21402..f726e00d 100644 --- a/src/module/sheets/creatureSheet.mjs +++ b/src/module/sheets/creatureSheet.mjs @@ -24,11 +24,7 @@ export class CreatureSheet extends HandlebarsApplicationMixin(ActorSheetV2) { static TABS = { sheet: { - tabs: [ - {id: 'meta', group: 'sheet', label: 'Meta'}, - {id: 'attacks', group: 'sheet', label: 'Attacken'}, - {id: 'description', group: 'sheet', label: 'Beschreibung'}, - ], + tabs: [], initial: 'meta' } } @@ -46,7 +42,31 @@ export class CreatureSheet extends HandlebarsApplicationMixin(ActorSheetV2) { }, description: { template: `systems/DSA_4-1/templates/actor/creature/tab-description.hbs` + }, + loot: { + template: `systems/DSA_4-1/templates/actor/creature/tab-loot.hbs` + }, + visibility: { + template: `systems/DSA_4-1/templates/actor/creature/tab-visibility.hbs` + }, + } + + _getTabsConfig(group) { + const tabs = foundry.utils.deepClone(super._getTabsConfig(group)) + const tabDef = [ + {id: 'meta', group: 'sheet', label: 'Meta'}, + {id: 'attacks', group: 'sheet', label: 'Attacken'}, + {id: 'description', group: 'sheet', label: 'Beschreibung'}, + {id: 'loot', group: 'sheet', label: 'Loot'}, + {id: 'visibility', group: 'sheet', label: 'Sichtbarkeit'}, + ] + + for (const tab of tabDef) { + if (game.user.isGM || this.document.system.visibility[tab.id]) { + tabs.tabs.push(tab) + } } + return tabs } /** @@ -125,7 +145,74 @@ export class CreatureSheet extends HandlebarsApplicationMixin(ActorSheetV2) { }) }) + context.metaRevealed = actorData.system.visibility.meta + context.attacksRevealed = actorData.system.visibility.attacks + context.descriptionRevealed = actorData.system.visibility.description + context.lootRevealed = actorData.system.visibility.loot + context.inventoryItems = [] + + actorData.items.forEach((item, index) => { + if (item.type === "Equipment") { + context.inventoryItems.push({ + index: index, + id: item._id, + quantity: item.system.quantity, + name: item.name, + icon: item.img + }) + } + }) + + return context; } + + _canDragDrop(event, options) { + return game.user.isGM + } + + _canDrag(event, options) { + return true + } + + async _onDrop(event) { + + const data = TextEditor.implementation.getDragEventData(event); + const actor = this.actor; + //const allowed = Hooks.call("dropActorSheetData", actor, this, data); + // if (allowed === false) return; + console.log("dropped") + // Dropped Documents + const documentClass = foundry.utils.getDocumentClass(data.type); + if (documentClass) { + const document = await documentClass.fromDropData(data); + + if (document.type === "Equipment") { + // No duplication by moving items from one actor to another + if (document.parent && document.parent !== this.actor) { + document.parent.items.get(document._id).delete() + } + + await this._onDropDocument(event, document); + } + } + } + + _onRender(context, options) { + new foundry.applications.ux.DragDrop.implementation({ + dropSelector: ".window-content", + dragSelector: ".equipment", + permissions: { + drop: this._canDragDrop.bind(this), + drag: this._canDrag.bind(this) + }, + callbacks: { + dragstart: this._onDragStart.bind(this), + drop: this._onDrop.bind(this) + } + }).bind(this.element); + + } + } diff --git a/src/module/sheets/merchantSheet.mjs b/src/module/sheets/merchantSheet.mjs index 362db271..e37ba245 100644 --- a/src/module/sheets/merchantSheet.mjs +++ b/src/module/sheets/merchantSheet.mjs @@ -222,8 +222,7 @@ export class MerchantSheet extends HandlebarsApplicationMixin(ActorSheetV2) { } _canDragDrop(event, options) { - console.log(event, options) - return true + return game.user.isGM } async _onDrop(event) { diff --git a/src/style/molecules/_equipment.scss b/src/style/molecules/_equipment.scss new file mode 100644 index 00000000..13a6c94d --- /dev/null +++ b/src/style/molecules/_equipment.scss @@ -0,0 +1,54 @@ +@use "../atoms/assets"; + +@mixin equipment { + position: relative; + height: 32px; + display: grid; + grid-template-columns: 32px 24px 1fr 48px; + grid-template-rows: 1fr; + padding: 2px 0 0 2px; + margin: 4px 0 0 4px; + gap: 8px; + background: assets.$tab-pane-background; + + .icon { + width: 32px; + padding: 0; + } + + .name, .weight { + height: 32px; + line-height: 32px; + vertical-align: middle; + } + + input.quantity { + padding: 0; + border: unset; + border-radius: 0; + height: 24px; + width: 24px; + margin: 4px 0; + text-align: right; + background: unset; + box-shadow: unset; + + &:hover { + text-decoration: underline; + } + } + + transition: box-shadow 0.2s, border 0.2s, margin 0.2s, padding 0.2s; + + &:hover { + .name { + text-shadow: 0 0 10px rgb(255 0 0); + } + + border: 1px solid #ccc; + box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.5); + margin: -4px 4px 4px -4px; + padding: 0 0 0 0; + z-index: 2; + } +} \ No newline at end of file diff --git a/src/style/organisms/_creature-sheet.scss b/src/style/organisms/_creature-sheet.scss index 707971cd..407cc09e 100644 --- a/src/style/organisms/_creature-sheet.scss +++ b/src/style/organisms/_creature-sheet.scss @@ -1,6 +1,7 @@ @use "../atoms/colours" as colour; @use 'sass:color'; @use "../atoms/numbers"; +@use "../molecules/equipment"; .dsa41.sheet.actor.creature { @@ -163,6 +164,16 @@ padding: 16px; } + .tab.loot { + + div { + .equipment { + @include equipment.equipment + } + } + + } + } } diff --git a/src/style/organisms/character-tabs/_inventory.scss b/src/style/organisms/character-tabs/_inventory.scss index 9a804269..2a6ad5ca 100644 --- a/src/style/organisms/character-tabs/_inventory.scss +++ b/src/style/organisms/character-tabs/_inventory.scss @@ -1,4 +1,5 @@ @use "../../atoms/assets"; +@use "../../molecules/equipment"; @mixin tab { @@ -49,56 +50,7 @@ flex-direction: column; .equipment { - position: relative; - height: 32px; - display: grid; - grid-template-columns: 32px 24px 1fr 48px; - grid-template-rows: 1fr; - padding: 2px 0 0 2px; - margin: 4px 0 0 4px; - gap: 8px; - background: assets.$tab-pane-background; - - .icon { - width: 32px; - padding: 0; - } - - .name, .weight { - height: 32px; - line-height: 32px; - vertical-align: middle; - } - - input.quantity { - padding: 0; - border: unset; - border-radius: 0; - height: 24px; - width: 24px; - margin: 4px 0; - text-align: right; - background: unset; - box-shadow: unset; - - &:hover { - text-decoration: underline; - } - } - - transition: box-shadow 0.2s, border 0.2s, margin 0.2s, padding 0.2s; - - &:hover { - .name { - text-shadow: 0 0 10px rgb(255 0 0); - } - - border: 1px solid #ccc; - box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.5); - margin: -4px 4px 4px -4px; - padding: 0 0 0 0; - z-index: 2; - } + @include equipment.equipment } } } diff --git a/src/templates/actor/creature/tab-loot.hbs b/src/templates/actor/creature/tab-loot.hbs new file mode 100644 index 00000000..9cde2fc7 --- /dev/null +++ b/src/templates/actor/creature/tab-loot.hbs @@ -0,0 +1,7 @@ +
+
+ {{> "systems/DSA_4-1/templates/ui/partial-equipment-button.hbs" inventoryItems}} +
+
\ No newline at end of file diff --git a/src/templates/actor/creature/tab-visibility.hbs b/src/templates/actor/creature/tab-visibility.hbs new file mode 100644 index 00000000..776b7b1f --- /dev/null +++ b/src/templates/actor/creature/tab-visibility.hbs @@ -0,0 +1,18 @@ +
+
+
+ Folgendes für alle sichtbar machen + + + + + + + +
+
+
\ No newline at end of file