From e22928b59ec6b7e3408858c10f0b8eab351e4c0f Mon Sep 17 00:00:00 2001 From: Nikhil Nagarajan Date: Wed, 7 Jan 2026 13:03:22 -0500 Subject: [PATCH 1/4] Improved Schema and now it works --- module/data/action/summonAction.mjs | 24 +++----- module/data/fields/action/summonField.mjs | 75 ++++++++--------------- 2 files changed, 36 insertions(+), 63 deletions(-) diff --git a/module/data/action/summonAction.mjs b/module/data/action/summonAction.mjs index 56e0446c..68f93510 100644 --- a/module/data/action/summonAction.mjs +++ b/module/data/action/summonAction.mjs @@ -1,20 +1,20 @@ +import DHSummonField from '../fields/action/summonField.mjs'; import DHBaseAction from './baseAction.mjs'; +import DHSummonDialog from '../../applications/dialogs/summonDialog.mjs'; export default class DHSummonAction extends DHBaseAction { static defineSchema() { - const fields = foundry.data.fields; return { ...super.defineSchema(), - summon: new fields.ArrayField(new fields.SchemaField({ - actorUUID: new fields.DocumentUUIDField({ type: 'Actor', required: true }), - count: new fields.NumberField({ required: true, default: 1, min: 1, integer: true }) - }), { required: false, initial: [] }) + summon: new DHSummonField({ required: false, initial: [] }) }; } + static extraSchemas=[...super.extraSchemas,'summon']; + get defaultValues() { return { - summon: { actorUUID: "", count: 1 } + summon: [] }; } @@ -30,12 +30,6 @@ export default class DHSummonAction extends DHBaseAction { return game.user.can('TOKEN_CREATE'); } - //Accessor for summon manager for performing the summon action - get summonManager() { - return game.dh.summon; //incomplete implementation - } - - //Logic to perform the summon action - incomplete implementation async _performAction(event, ...args) { const validSummons = this.summon.filter(entry => entry.actorUUID); if (validSummons.length === 0) { @@ -50,5 +44,7 @@ export default class DHSummonAction extends DHBaseAction { console.log(`- Ready to summon ${entry.count}x [${actor?.name || "Unknown Actor"}]`); } console.groupEnd(); - } -} + + //Todo: Open Summon Dialog + } +} \ No newline at end of file diff --git a/module/data/fields/action/summonField.mjs b/module/data/fields/action/summonField.mjs index 7bcd1145..1abb31f4 100644 --- a/module/data/fields/action/summonField.mjs +++ b/module/data/fields/action/summonField.mjs @@ -1,61 +1,38 @@ -import DHSummonDialog from '../../../applications/dialogs/summonDialog.mjs'; - const fields = foundry.data.fields; -export default class DHSummonField extends fields.SchemaField { +export default class DHSummonField extends fields.ArrayField { /** * Action Workflow order */ static order = 120; constructor(options = {}, context = {}) { - const summonFields = { - summon: new fields.ArrayField(new fields.SchemaField({ - actorUUID: new fields.DocumentUUIDField({ - type: 'Actor', - required: true }), - count: new fields.NumberField({ - required: true, - default: 1, - min: 1, - integer: true }) - }), { required: false, initial: [] }) - }; + const summonFields = new fields.SchemaField({ + actorUUID: new fields.DocumentUUIDField({ + type: 'Actor', + required: true + }), + count: new fields.NumberField({ + required: true, + default: 1, + min: 1, + integer: true + }) + }) super(summonFields, options, context); + + // summon: new fields.ArrayField(new fields.SchemaField({ + // actorUUID: new fields.DocumentUUIDField({ + // type: 'Actor', + // required: true }), + // count: new fields.NumberField({ + // required: true, + // default: 1, + // min: 1, + // integer: true }) + // }), { required: false, initial: [] }) + // }; + // super(summonFields, options, context); } - /** - * Summon Action Workflow part. - * Must be called within Action context or similar. - * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. - */ - static async execute(config) { - const selected = await DHSummonDialog.configure(config, this.item); - if (!selected) return false; - return await DHSummonField.summon.call(this, selected); - } - /** - * Update Action Workflow config object. - * Must be called within Action context. - * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. - */ - prepareConfig(config) { - if (!canvas.scene){ - ui.notifications.warn(game.i18n.localize("DAGGERHEART.ACTIONS.TYPES.summon.error")); - return false; - } - return true; - } - /** - * Logic to perform the summon action - incomplete implementation - */ - - get defaultValues() { - return { - summon: { actorUUID: "", count: 1 } - }; - } - get canSummon() { - return game.user.can('TOKEN_CREATE'); - } } \ No newline at end of file From f5e00dedb2265a18df756c95ecc9b0c7a4eed540 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Wed, 7 Jan 2026 19:14:17 +0100 Subject: [PATCH 2/4] . --- module/data/action/summonAction.mjs | 73 ++++++++++------------- module/data/fields/action/summonField.mjs | 40 +++++-------- 2 files changed, 47 insertions(+), 66 deletions(-) diff --git a/module/data/action/summonAction.mjs b/module/data/action/summonAction.mjs index 68f93510..d03c766b 100644 --- a/module/data/action/summonAction.mjs +++ b/module/data/action/summonAction.mjs @@ -1,50 +1,41 @@ -import DHSummonField from '../fields/action/summonField.mjs'; import DHBaseAction from './baseAction.mjs'; -import DHSummonDialog from '../../applications/dialogs/summonDialog.mjs'; export default class DHSummonAction extends DHBaseAction { - static defineSchema() { - return { - ...super.defineSchema(), - summon: new DHSummonField({ required: false, initial: [] }) - }; - } + static extraSchemas = [...super.extraSchemas, 'summon']; - static extraSchemas=[...super.extraSchemas,'summon']; + // get defaultValues() { + // return { + // summon: [] + // }; + // } - get defaultValues() { - return { - summon: [] - }; - } + // async trigger(event, ...args) { + // if (!this.canSummon || !canvas.scene){ + // ui.notifications.warn(game.i18n.localize("DAGGERHEART.ACTIONS.TYPES.summon.error")); + // return; + // } + // await this._performAction(); + // } - async trigger(event, ...args) { - if (!this.canSummon || !canvas.scene){ - ui.notifications.warn(game.i18n.localize("DAGGERHEART.ACTIONS.TYPES.summon.error")); - return; - } - await this._performAction(); - } + // get canSummon() { + // return game.user.can('TOKEN_CREATE'); + // } - get canSummon() { - return game.user.can('TOKEN_CREATE'); - } + // async _performAction(event, ...args) { + // const validSummons = this.summon.filter(entry => entry.actorUUID); + // if (validSummons.length === 0) { + // ui.notifications.warn("No actors configured for this Summon action."); + // return; + // } + // // FOR NOW: Just log the data to prove the schema working or not + // console.group("Summon Action Triggered"); - async _performAction(event, ...args) { - const validSummons = this.summon.filter(entry => entry.actorUUID); - if (validSummons.length === 0) { - ui.notifications.warn("No actors configured for this Summon action."); - return; - } - // FOR NOW: Just log the data to prove the schema working or not - console.group("Summon Action Triggered"); + // for (const entry of validSummons) { + // const actor = await fromUuid(entry.actorUUID); + // console.log(`- Ready to summon ${entry.count}x [${actor?.name || "Unknown Actor"}]`); + // } + // console.groupEnd(); - for (const entry of validSummons) { - const actor = await fromUuid(entry.actorUUID); - console.log(`- Ready to summon ${entry.count}x [${actor?.name || "Unknown Actor"}]`); - } - console.groupEnd(); - - //Todo: Open Summon Dialog - } -} \ No newline at end of file + // //Todo: Open Summon Dialog + // } +} diff --git a/module/data/fields/action/summonField.mjs b/module/data/fields/action/summonField.mjs index 1abb31f4..038cae09 100644 --- a/module/data/fields/action/summonField.mjs +++ b/module/data/fields/action/summonField.mjs @@ -7,32 +7,22 @@ export default class DHSummonField extends fields.ArrayField { static order = 120; constructor(options = {}, context = {}) { - const summonFields = new fields.SchemaField({ - actorUUID: new fields.DocumentUUIDField({ - type: 'Actor', - required: true - }), - count: new fields.NumberField({ - required: true, - default: 1, - min: 1, - integer: true - }) + const summonFields = new fields.SchemaField({ + actorUUID: new fields.DocumentUUIDField({ + type: 'Actor', + required: true + }), + count: new fields.NumberField({ + required: true, + default: 1, + min: 1, + integer: true }) + }); super(summonFields, options, context); - - // summon: new fields.ArrayField(new fields.SchemaField({ - // actorUUID: new fields.DocumentUUIDField({ - // type: 'Actor', - // required: true }), - // count: new fields.NumberField({ - // required: true, - // default: 1, - // min: 1, - // integer: true }) - // }), { required: false, initial: [] }) - // }; - // super(summonFields, options, context); } -} \ No newline at end of file + static async execute(config) { + console.log('something'); + } +} From 506a17bd031ffb2bc894febba2c9be804b5cc193 Mon Sep 17 00:00:00 2001 From: Nikhil Nagarajan Date: Wed, 7 Jan 2026 15:53:25 -0500 Subject: [PATCH 3/4] Dialog created. Tokens not dragged(tbd). --- lang/en.json | 4 ++ module/applications/dialogs/summonDialog.mjs | 54 ++++++++++---------- module/data/action/summonAction.mjs | 36 ------------- module/data/fields/action/summonField.mjs | 25 ++++++++- templates/actionTypes/summon.hbs | 6 --- templates/dialogs/summon/summonDialog.hbs | 18 ++++--- 6 files changed, 66 insertions(+), 77 deletions(-) diff --git a/lang/en.json b/lang/en.json index 7f359f09..80091d1e 100755 --- a/lang/en.json +++ b/lang/en.json @@ -610,6 +610,10 @@ "title": "{name} Resource", "rerollDice": "Reroll Dice" }, + "Summon": { + "title": "Summon Tokens", + "hint": "Drag tokens from the list below into the scene to summon them." + }, "TagTeamSelect": { "title": "Tag Team Roll", "leaderTitle": "Initiating Character", diff --git a/module/applications/dialogs/summonDialog.mjs b/module/applications/dialogs/summonDialog.mjs index c5ab7a3d..9dc189fd 100644 --- a/module/applications/dialogs/summonDialog.mjs +++ b/module/applications/dialogs/summonDialog.mjs @@ -5,44 +5,46 @@ export default class DHSummonDialog extends HandlebarsApplicationMixin(Applicati super(summonData); // Initialize summons and index this.summons = summonData.summons || []; - this.index = 0; } - get_title() { - return game.i18n.localize("DAGGERHEART.DIALOGS.SUMMON.title"); - } + static PARTS = { + main: { template: 'systems/daggerheart/templates/dialogs/summon/summonDialog.hbs' } + }; + static DEFAULT_OPTIONS= { - ...super.DEFAULT_OPTIONS, - template: 'systems/daggerheart/module/applications/dialogs/summon/summonDialog.hbs', - width: 400, - height: 'auto', + tag: 'form', + window: { + title: "DAGGERHEART.APPLICATIONS.Summon.title", + resizable: false + }, + position: { + width: 400, + height: 'auto' + }, classes: ['daggerheart', 'dialog', 'summon-dialog'], - dragDrop: [{ dragSelector: '.summon-token', dropSelector: null, handler:'onDrop'}] + dragDrop: [{dragSelector: '.summon-token'}], }; async _prepareContext() { const context = await super._prepareContext(); - context.summons=this.summons; - context.index=this.index; + context.summons=await Promise.all(this.summons.map(async(entry)=>{ + const actor = await fromUuid(entry.actorUUID); + return { + ...entry, + name: actor?.name || game.i18n.localize("DAGGERHEART.GENERAL.Unknown"), + img: actor?.img || 'icons/svg/mystery-man.svg', + }; + })); return context; } - getData(options={}) { - const data = super.getData(options); - data.summons=this.summons; - data.index=this.index; - return data; - } - async prepareContext() { - const context = await super.prepareContext(); - return context; + _onDragStart(event) { + const uuid = event.currentTarget.dataset.uuid; + if(!uuid) return; + const dragData = { type: 'Actor', uuid: uuid }; + event.dataTransfer.effectAllowed = 'all'; + event.dataTransfer.setData('text/plain', JSON.stringify(dragData)); } - async onDrop(event) {//add to canvas - event.preventDefault(); - const tokenData = JSON.parse(event.dataTransfer.getData('text/plain')); - const position = { x: event.clientX, y: event.clientY }; - await canvas.scene.createEmbeddedDocuments("Token", [tokenData], { temporary: false, x: position.x, y: position.y }); - } } \ No newline at end of file diff --git a/module/data/action/summonAction.mjs b/module/data/action/summonAction.mjs index d03c766b..1505ce2d 100644 --- a/module/data/action/summonAction.mjs +++ b/module/data/action/summonAction.mjs @@ -2,40 +2,4 @@ import DHBaseAction from './baseAction.mjs'; export default class DHSummonAction extends DHBaseAction { static extraSchemas = [...super.extraSchemas, 'summon']; - - // get defaultValues() { - // return { - // summon: [] - // }; - // } - - // async trigger(event, ...args) { - // if (!this.canSummon || !canvas.scene){ - // ui.notifications.warn(game.i18n.localize("DAGGERHEART.ACTIONS.TYPES.summon.error")); - // return; - // } - // await this._performAction(); - // } - - // get canSummon() { - // return game.user.can('TOKEN_CREATE'); - // } - - // async _performAction(event, ...args) { - // const validSummons = this.summon.filter(entry => entry.actorUUID); - // if (validSummons.length === 0) { - // ui.notifications.warn("No actors configured for this Summon action."); - // return; - // } - // // FOR NOW: Just log the data to prove the schema working or not - // console.group("Summon Action Triggered"); - - // for (const entry of validSummons) { - // const actor = await fromUuid(entry.actorUUID); - // console.log(`- Ready to summon ${entry.count}x [${actor?.name || "Unknown Actor"}]`); - // } - // console.groupEnd(); - - // //Todo: Open Summon Dialog - // } } diff --git a/module/data/fields/action/summonField.mjs b/module/data/fields/action/summonField.mjs index 038cae09..49ff1798 100644 --- a/module/data/fields/action/summonField.mjs +++ b/module/data/fields/action/summonField.mjs @@ -1,4 +1,5 @@ const fields = foundry.data.fields; +import DHSummonDialog from '../../../applications/dialogs/summonDialog.mjs'; export default class DHSummonField extends fields.ArrayField { /** @@ -22,7 +23,27 @@ export default class DHSummonField extends fields.ArrayField { super(summonFields, options, context); } - static async execute(config) { - console.log('something'); + static async execute() { + if(!canvas.scene){ + ui.notifications.warn(game.i18n.localize("DAGGERHEART.ACTIONS.TYPES.summon.error")); + return; + } + const validSummons = this.summon.filter(entry => entry.actorUUID); + if (validSummons.length === 0) { + console.log("No actors configured for this Summon action."); + return; + } + // FOR NOW: Just log the data to prove the schema working or not + console.group("Summon Action Triggered"); + for (const entry of validSummons) { + const actor = await fromUuid(entry.actorUUID); + console.log(`- Ready to summon ${entry.count}x [${actor?.name || "Unknown Actor"}]`); + } + console.groupEnd(); + //Open Summon Dialog + const summonData = { summons: validSummons }; + console.log(summonData); + const dialog = new DHSummonDialog(summonData); + dialog.render(true); } } diff --git a/templates/actionTypes/summon.hbs b/templates/actionTypes/summon.hbs index d313fb4c..276045a8 100644 --- a/templates/actionTypes/summon.hbs +++ b/templates/actionTypes/summon.hbs @@ -3,12 +3,6 @@ {{localize "DAGGERHEART.ACTIONS.TYPES.summon.name"}}

{{localize "DAGGERHEART.ACTIONS.Settings.summon.hint"}}

- {{!-- {{#each source as |entry index|}} -
- {{formField ../fields.actorUUID label="DAGGERHEART.ACTIONS.Settings.summon.actor" value=entry.actorUUID name=(concat "summon." index ".actorUUID") localize=true classes="summon-actor-drop"}} - {{formField ../fields.count label="DAGGERHEART.ACTIONS.Settings.summon.count" value=entry.count name=(concat "summon." index ".count") localize=true}} -
- {{/each}} --}}
diff --git a/templates/dialogs/summon/summonDialog.hbs b/templates/dialogs/summon/summonDialog.hbs index 9e03e129..61817b93 100644 --- a/templates/dialogs/summon/summonDialog.hbs +++ b/templates/dialogs/summon/summonDialog.hbs @@ -1,24 +1,28 @@
- {{localize "DAGGERHEART.DIALOGS.Summon.title"}} + {{localize "DAGGERHEART.APPLICATIONS.Summon.title"}} -

{{localize "DAGGERHEART.DIALOGS.Summon.hint"}}

+

{{localize "DAGGERHEART.APPLICATIONS.Summon.hint"}}

- {{#each summons as |entry index|}} -
    + +
      + {{#each summons as |entry index|}} +
    • {{#if (gte entry.count 1)}}

      - {{entry.name}} (x{{entry.count}}) + {{entry.name}}

      + Count: {{entry.count}} {{else}}

      {{entry.name}}

      {{/if}}
      -
    - {{/each}} + + {{/each}} +
\ No newline at end of file From f47a869af35461022a9bb7b27f3cb05f888b4bbc Mon Sep 17 00:00:00 2001 From: Nikhil Nagarajan Date: Wed, 7 Jan 2026 18:02:49 -0500 Subject: [PATCH 4/4] Bare minimum implementation --- module/data/fields/action/summonField.mjs | 63 +++++++++++++++++++---- 1 file changed, 54 insertions(+), 9 deletions(-) diff --git a/module/data/fields/action/summonField.mjs b/module/data/fields/action/summonField.mjs index 49ff1798..6ba1f8a2 100644 --- a/module/data/fields/action/summonField.mjs +++ b/module/data/fields/action/summonField.mjs @@ -33,17 +33,62 @@ export default class DHSummonField extends fields.ArrayField { console.log("No actors configured for this Summon action."); return; } - // FOR NOW: Just log the data to prove the schema working or not - console.group("Summon Action Triggered"); + for (const entry of validSummons) { const actor = await fromUuid(entry.actorUUID); - console.log(`- Ready to summon ${entry.count}x [${actor?.name || "Unknown Actor"}]`); } - console.groupEnd(); - //Open Summon Dialog - const summonData = { summons: validSummons }; - console.log(summonData); - const dialog = new DHSummonDialog(summonData); - dialog.render(true); + + // //Open Summon Dialog + // const summonData = { summons: validSummons }; + // console.log(summonData); + // const dialog = new DHSummonDialog(summonData); + // dialog.render(true); + + // Create folder and add tokens to actor folder + const rootFolderName = game.i18n.localize("DAGGERHEART.APPLICATIONS.Summon.title"); + let rootFolder = game.folders.find(f => f.name === rootFolderName && f.type === 'Actor'); + if (!rootFolder) { + rootFolder = await Folder.create({ + name: rootFolderName, + type: 'Actor', + }); + } + const parentName = this.item.name ?? "Unkown Feature"; + const actionName = this.name ?? "Unkown Action"; + const subFolderName = `${parentName} - ${actionName}`; + + let subFolder = game.folders.find(f => f.name === subFolderName && f.type === 'Actor' && f.folder?.id === rootFolder.id); + if (!subFolder) { + subFolder = await Folder.create({ + name: subFolderName, + type: 'Actor', + folder: rootFolder.id + }); + const actorsToSummon = []; + for (const entry of validSummons) { + const sourceActor = await fromUuid(entry.actorUUID); + if (!sourceActor) { + console.warn('DHSummonField: Could not find actor for UUID', entry.actorUUID); + continue; + } + const actorData = sourceActor.toObject(); + delete actorData._id; // Remove _id to create a new Actor + actorData.folder = subFolder.id; + + for (let i = 0; i < entry.count; i++) { + const newActor = foundry.utils.deepClone(actorData); + if (entry.count > 1) { + newActor.name = `${actorData.name} ${i + 1}`; + } + actorsToSummon.push(newActor); + } + } + if (actorsToSummon.length > 0) { + await Actor.createDocuments(actorsToSummon); + ui.notifications.info(`Summoned ${actorsToSummon.length} actors successfully in folder ${subFolder.name}.`); + } + } else { + ui.notifications.info(`Summon actors already exist in folder ${subFolder.name}.`); + } } }