mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-11 19:25:21 +01:00
Added optional summon render to chat message
This commit is contained in:
parent
47b9be0f01
commit
69da660bfa
8 changed files with 128 additions and 43 deletions
|
|
@ -71,7 +71,9 @@
|
||||||
"name": "Summon",
|
"name": "Summon",
|
||||||
"tooltip": "Create tokens in the scene.",
|
"tooltip": "Create tokens in the scene.",
|
||||||
"error": "You do not have permission to summon tokens or there is no active scene.",
|
"error": "You do not have permission to summon tokens or there is no active scene.",
|
||||||
"invalidDrop": "You can only drop Actor entities to summon."
|
"invalidDrop": "You can only drop Actor entities to summon.",
|
||||||
|
"chatMessageTitle": "Test2",
|
||||||
|
"chatMessageHeaderTitle": "Summoning"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Config": {
|
"Config": {
|
||||||
|
|
@ -2180,6 +2182,10 @@
|
||||||
"stress": "Stress",
|
"stress": "Stress",
|
||||||
"subclasses": "Subclasses",
|
"subclasses": "Subclasses",
|
||||||
"success": "Success",
|
"success": "Success",
|
||||||
|
"summon": {
|
||||||
|
"single": "Summon",
|
||||||
|
"plural": "Summons"
|
||||||
|
},
|
||||||
"take": "Take",
|
"take": "Take",
|
||||||
"Target": {
|
"Target": {
|
||||||
"single": "Target",
|
"single": "Target",
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
context.source = this.action.toObject(true);
|
context.source = this.action.toObject(true);
|
||||||
|
|
||||||
context.summons = [];
|
context.summons = [];
|
||||||
for (const summon of context.source.summon) {
|
for (const summon of context.source.summon ?? []) {
|
||||||
const actor = await foundry.utils.fromUuid(summon.actorUUID);
|
const actor = await foundry.utils.fromUuid(summon.actorUUID);
|
||||||
context.summons.push({ actor, count: summon.count });
|
context.summons.push({ actor, count: summon.count });
|
||||||
}
|
}
|
||||||
|
|
@ -261,7 +261,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
const wrapper = event.target.closest('.summon-count-wrapper');
|
const wrapper = event.target.closest('.summon-count-wrapper');
|
||||||
const index = wrapper.dataset.index;
|
const index = wrapper.dataset.index;
|
||||||
const data = this.action.toObject();
|
const data = this.action.toObject();
|
||||||
data.summon[index].count = Number.parseInt(event.target.value);
|
data.summon[index].count = event.target.value;
|
||||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -163,15 +163,13 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
*/
|
*/
|
||||||
getRollData(data = {}) {
|
getRollData(data = {}) {
|
||||||
const actorData = this.actor ? this.actor.getRollData(false) : {};
|
const actorData = this.actor ? this.actor.getRollData(false) : {};
|
||||||
|
actorData.result = data.roll?.total ?? 1;
|
||||||
|
actorData.scale = data.costs?.length // Right now only return the first scalable cost.
|
||||||
|
? (data.costs.find(c => c.scalable)?.total ?? 1)
|
||||||
|
: 1;
|
||||||
|
actorData.roll = {};
|
||||||
|
|
||||||
return {
|
return actorData;
|
||||||
...actorData,
|
|
||||||
result: data.roll?.total ?? 1,
|
|
||||||
scale: data.costs?.length // Right now only return the first scalable cost.
|
|
||||||
? (data.costs.find(c => c.scalable)?.total ?? 1)
|
|
||||||
: 1,
|
|
||||||
roll: {}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import FormulaField from '../formulaField.mjs';
|
||||||
|
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
export default class DHSummonField extends fields.ArrayField {
|
export default class DHSummonField extends fields.ArrayField {
|
||||||
|
|
@ -12,11 +14,9 @@ export default class DHSummonField extends fields.ArrayField {
|
||||||
type: 'Actor',
|
type: 'Actor',
|
||||||
required: true
|
required: true
|
||||||
}),
|
}),
|
||||||
count: new fields.NumberField({
|
count: new FormulaField({
|
||||||
required: true,
|
required: true,
|
||||||
default: 1,
|
default: '1'
|
||||||
min: 1,
|
|
||||||
integer: true
|
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
super(summonFields, options, context);
|
super(summonFields, options, context);
|
||||||
|
|
@ -33,38 +33,57 @@ export default class DHSummonField extends fields.ArrayField {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.actor.sheet?.minimize();
|
const rolls = [];
|
||||||
const summonData = [];
|
const summonData = [];
|
||||||
for (const summon of this.summon) {
|
for (const summon of this.summon) {
|
||||||
/* Possibly check for any available instances in the world with prepared images */
|
let count = summon.count;
|
||||||
let actor = await foundry.utils.fromUuid(summon.actorUUID);
|
const roll = new Roll(summon.count);
|
||||||
|
if (!roll.isDeterministic) {
|
||||||
/* Check for any available instances of the actor present in the world if we're missing artwork in the compendium */
|
await roll.evaluate();
|
||||||
const dataType = game.system.api.data.actors[`Dh${actor.type.capitalize()}`];
|
if (game.modules.get('dice-so-nice')?.active) rolls.push(roll);
|
||||||
if (actor.inCompendium && dataType && actor.img === dataType.DEFAULT_ICON) {
|
count = roll.total;
|
||||||
const worldActorCopy = game.actors.find(x => x.name === actor.name);
|
|
||||||
actor = worldActorCopy ?? actor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
summonData.push({ actor, count: summon.count });
|
const actor = DHSummonField.getWorldActor(await foundry.utils.fromUuid(summon.actorUUID));
|
||||||
|
/* Extending summon data in memory so it's available in actionField.toChat. Think it's harmless, but ugly. Could maybe find a better way. */
|
||||||
|
summon.rolledCount = count;
|
||||||
|
summon.actor = actor.toObject();
|
||||||
|
|
||||||
|
summonData.push({ actor, count: count });
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSummon = async summonIndex => {
|
if (rolls.length) await Promise.all(rolls.map(roll => game.dice3d.showForRoll(roll, game.user, true)));
|
||||||
const summon = summonData[summonIndex];
|
|
||||||
const result = await CONFIG.ux.TokenManager.createPreviewAsync(summon.actor, {
|
|
||||||
name: `${summon.actor.prototypeToken.name}${summon.count > 1 ? ` (${summon.count}x)` : ''}`
|
|
||||||
});
|
|
||||||
if (!result) return this.actor.sheet?.maximize();
|
|
||||||
|
|
||||||
summon.count--;
|
this.actor.sheet?.minimize();
|
||||||
if (summon.count === 0) {
|
DHSummonField.handleSummon(summonData, this.actor);
|
||||||
summonIndex++;
|
}
|
||||||
if (summonIndex === summonData.length) return this.actor.sheet?.maximize();
|
|
||||||
}
|
|
||||||
|
|
||||||
handleSummon(summonIndex);
|
/* Check for any available instances of the actor present in the world if we're missing artwork in the compendium */
|
||||||
};
|
static getWorldActor(baseActor) {
|
||||||
|
const dataType = game.system.api.data.actors[`Dh${baseActor.type.capitalize()}`];
|
||||||
|
if (baseActor.inCompendium && dataType && baseActor.img === dataType.DEFAULT_ICON) {
|
||||||
|
const worldActorCopy = game.actors.find(x => x.name === baseActor.name);
|
||||||
|
return worldActorCopy ?? baseActor;
|
||||||
|
}
|
||||||
|
|
||||||
handleSummon(0);
|
return baseActor;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async handleSummon(summonData, actionActor, summonIndex = 0) {
|
||||||
|
const summon = summonData[summonIndex];
|
||||||
|
const result = await CONFIG.ux.TokenManager.createPreviewAsync(summon.actor, {
|
||||||
|
name: `${summon.actor.prototypeToken.name}${summon.count > 1 ? ` (${summon.count}x)` : ''}`
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result) return actionActor.sheet?.maximize();
|
||||||
|
summon.actor = result.actor;
|
||||||
|
|
||||||
|
summon.count--;
|
||||||
|
if (summon.count === 0) {
|
||||||
|
summonIndex++;
|
||||||
|
if (summonIndex === summonData.length) return actionActor.sheet?.maximize();
|
||||||
|
}
|
||||||
|
|
||||||
|
DHSummonField.handleSummon(summonData, actionActor, summonIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -267,7 +267,8 @@ export function ActionMixin(Base) {
|
||||||
action: {
|
action: {
|
||||||
name: this.name,
|
name: this.name,
|
||||||
img: this.baseAction ? this.parent.parent.img : this.img,
|
img: this.baseAction ? this.parent.parent.img : this.img,
|
||||||
tags: this.tags ? this.tags : ['Spell', 'Arcana', 'Lv 10']
|
tags: this.tags ? this.tags : ['Spell', 'Arcana', 'Lv 10'],
|
||||||
|
summon: this.summon
|
||||||
},
|
},
|
||||||
itemOrigin: this.item,
|
itemOrigin: this.item,
|
||||||
description: this.description || (this.item instanceof Item ? this.item.system.description : '')
|
description: this.description || (this.item instanceof Item ? this.item.system.description : '')
|
||||||
|
|
|
||||||
|
|
@ -94,11 +94,11 @@ export default class DhTokenManager {
|
||||||
? await game.system.api.documents.DhpActor.create(this.#actor.toObject())
|
? await game.system.api.documents.DhpActor.create(this.#actor.toObject())
|
||||||
: this.#actor;
|
: this.#actor;
|
||||||
const tokenData = await actor.getTokenDocument();
|
const tokenData = await actor.getTokenDocument();
|
||||||
const token = await canvas.scene.createEmbeddedDocuments('Token', [
|
const result = await canvas.scene.createEmbeddedDocuments('Token', [
|
||||||
{ ...tokenData, x: this.#activePreview.document.x, y: this.#activePreview.document.y }
|
{ ...tokenData, x: this.#activePreview.document.x, y: this.#activePreview.document.y }
|
||||||
]);
|
]);
|
||||||
|
|
||||||
this.#activePreview = undefined;
|
this.#activePreview = undefined;
|
||||||
if (this.#resolve) this.#resolve(token);
|
if (this.#resolve && result.length) this.#resolve(result[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,51 @@
|
||||||
|
|
||||||
.description {
|
.description {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
|
|
||||||
|
.summons-header {
|
||||||
|
font-size: var(--font-size-14);
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
span::before,
|
||||||
|
span::after {
|
||||||
|
color: @dark-blue;
|
||||||
|
}
|
||||||
|
&:before {
|
||||||
|
background: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, @dark-blue 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
background: linear-gradient(90deg, @dark-blue 0%, rgba(0, 0, 0, 0) 100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.summons-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
.summon-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.summon-label-container {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ability-card-footer {
|
.ability-card-footer {
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,22 @@
|
||||||
</div>
|
</div>
|
||||||
<i class="fa-solid fa-chevron-down"></i>
|
<i class="fa-solid fa-chevron-down"></i>
|
||||||
</summary>
|
</summary>
|
||||||
<div class="description">{{{description}}}</div>
|
<div class="description">
|
||||||
|
{{{description}}}
|
||||||
|
{{#if action.summon}}
|
||||||
|
<div class="summons-header"><span>{{localize "DAGGERHEART.GENERAL.summon.plural"}}</span></div>
|
||||||
|
<div class="summons-container">
|
||||||
|
{{#each action.summon}}
|
||||||
|
<div class="summon-container">
|
||||||
|
<div class="summon-label-container">
|
||||||
|
<img src="{{this.actor.img}}" />
|
||||||
|
<label>{{this.actor.name}}</label>
|
||||||
|
</div>
|
||||||
|
<span># {{this.rolledCount}}</span>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue