Compare commits

..

No commits in common. "69da660bfa7c0fd96f69217c866a82920f266d0c" and "ae211cda3012085a766d35d8959059b98c320a13" have entirely different histories.

9 changed files with 45 additions and 162 deletions

View file

@ -71,9 +71,7 @@
"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": {
@ -2182,10 +2180,6 @@
"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",
@ -2833,8 +2827,7 @@
"deleteItem": "Delete Item", "deleteItem": "Delete Item",
"immune": "Immune", "immune": "Immune",
"middleClick": "[Middle Click] Keep tooltip view", "middleClick": "[Middle Click] Keep tooltip view",
"tokenSize": "The token size used on the canvas", "tokenSize": "The token size used on the canvas"
"previewTokenHelp": "Left-click to place, right-click to cancel"
} }
} }
} }

View file

@ -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 = event.target.value; data.summon[index].count = Number.parseInt(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) });
} }

View file

@ -1,12 +1,4 @@
export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
/** @inheritdoc */
async _draw(options) {
await super._draw(options);
if (this.document.flags.daggerheart?.createPlacement)
this.previewHelp ||= this.addChild(this.#drawPreviewHelp());
}
/** @inheritDoc */ /** @inheritDoc */
async _drawEffects() { async _drawEffects() {
this.effects.renderable = false; this.effects.renderable = false;
@ -77,25 +69,4 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
bar.position.set(0, posY); bar.position.set(0, posY);
return true; return true;
} }
/**
* Draw a helptext for previews as a text object
* @returns {PreciseText} The Text object for the preview helper
*/
#drawPreviewHelp() {
const { uiScale } = canvas.dimensions;
const textStyle = CONFIG.canvasTextStyle.clone();
textStyle.fontSize = 18;
textStyle.wordWrapWidth = this.w * 2.5;
textStyle.fontStyle = 'italic';
const helpText = new PreciseText(
`(${game.i18n.localize('DAGGERHEART.UI.Tooltip.previewTokenHelp')})`,
textStyle
);
helpText.anchor.set(helpText.width / 900, 1);
helpText.scale.set(uiScale, uiScale);
return helpText;
}
} }

View file

@ -163,13 +163,15 @@ 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 actorData; return {
...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: {}
};
} }
/** /**

View file

@ -1,5 +1,3 @@
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 {
@ -14,9 +12,11 @@ export default class DHSummonField extends fields.ArrayField {
type: 'Actor', type: 'Actor',
required: true required: true
}), }),
count: new FormulaField({ count: new fields.NumberField({
required: true, required: true,
default: '1' default: 1,
min: 1,
integer: true
}) })
}); });
super(summonFields, options, context); super(summonFields, options, context);
@ -33,57 +33,37 @@ export default class DHSummonField extends fields.ArrayField {
return; return;
} }
const rolls = [];
const summonData = []; const summonData = [];
for (const summon of this.summon) { for (const summon of this.summon) {
let count = summon.count; /* Possibly check for any available instances in the world with prepared images */
const roll = new Roll(summon.count); let actor = await foundry.utils.fromUuid(summon.actorUUID);
if (!roll.isDeterministic) {
await roll.evaluate(); /* Check for any available instances of the actor present in the world if we're missing artwork in the compendium */
if (game.modules.get('dice-so-nice')?.active) rolls.push(roll); const dataType = game.system.api.data.actors[`Dh${actor.type.capitalize()}`];
count = roll.total; if (actor.inCompendium && dataType && actor.img === dataType.DEFAULT_ICON) {
const worldActorCopy = game.actors.find(x => x.name === actor.name);
actor = worldActorCopy ?? actor;
} }
const actor = DHSummonField.getWorldActor(await foundry.utils.fromUuid(summon.actorUUID)); summonData.push({ actor, count: summon.count });
/* 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 });
} }
if (rolls.length) await Promise.all(rolls.map(roll => game.dice3d.showForRoll(roll, game.user, true))); const handleSummon = async summonIndex => {
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?.minimize(); summon.count--;
DHSummonField.handleSummon(summonData, this.actor); if (summon.count === 0) {
} summonIndex++;
if (summonIndex === summonData.length) return;
}
/* Check for any available instances of the actor present in the world if we're missing artwork in the compendium */ handleSummon(summonIndex);
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;
}
return baseActor; handleSummon(0);
}
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);
} }
} }

View file

@ -267,8 +267,7 @@ 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 : '')

View file

@ -39,11 +39,10 @@ export default class DhTokenManager {
canvas.app.view.addEventListener('contextmenu', this.#activePreview.events.contextmenu); canvas.app.view.addEventListener('contextmenu', this.#activePreview.events.contextmenu);
} }
/* Currently intended for using as a preview of where to create a token. (note the flag) */ async createPreviewAsync(actor, tokenData) {
async createPreviewAsync(actor, tokenData = {}) {
return new Promise(resolve => { return new Promise(resolve => {
this.#resolve = resolve; this.#resolve = resolve;
this.createPreview(actor, { ...tokenData, flags: { daggerheart: { createPlacement: true } } }); this.createPreview(actor, tokenData);
}); });
} }
@ -94,11 +93,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 result = await canvas.scene.createEmbeddedDocuments('Token', [ const token = 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 && result.length) this.#resolve(result[0]); if (this.#resolve) this.#resolve(token);
} }
} }

View file

@ -98,51 +98,6 @@
.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 {

View file

@ -8,22 +8,6 @@
</div> </div>
<i class="fa-solid fa-chevron-down"></i> <i class="fa-solid fa-chevron-down"></i>
</summary> </summary>
<div class="description"> <div class="description">{{{description}}}</div>
{{{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>