Compare commits

...

11 commits

Author SHA1 Message Date
WBHarry
3dc8379ff5 Critical first 👀 2026-06-21 01:05:25 +02:00
WBHarry
629268caf9 . 2026-06-21 01:00:47 +02:00
WBHarry
1c6c3e7736 Neater code for critical 2026-06-21 01:00:08 +02:00
WBHarry
4459ee00e0 Better TagTeamDialog finalise solution 2026-06-21 00:50:04 +02:00
WBHarry
5299beed8b Changed to use ResourceUpdateMap in TagTeamDialog 2026-06-21 00:32:07 +02:00
WBHarry
25a64b1332 Merged with main 2026-06-21 00:17:50 +02:00
WBHarry
29be8c1395
Removed the unused ResourceMap total property (#2024)
Some checks are pending
Project CI / build (24.x) (push) Waiting to run
2026-06-21 00:13:45 +02:00
WBHarry
de7c08ed64 Added an enable/disable checkbox for TagTeamRoll hope cost 2026-06-20 23:19:26 +02:00
Carlos Fernandez
6b80a6243c
Support drag resort on features in actor setting sheets (#2023) 2026-06-20 19:55:51 +02:00
Carlos Fernandez
6f1da42735
Make dragging features work more seamlessly (#2016) 2026-06-20 19:48:12 +02:00
Carlos Fernandez
b64e600a6b
Fix old armorscore AEs clobbering data (#2018) 2026-06-20 19:44:39 +02:00
19 changed files with 166 additions and 138 deletions

View file

@ -486,13 +486,13 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
const resourceMap = new ResourceUpdateMap(actor); const resourceMap = new ResourceUpdateMap(actor);
if (totalRoll.isCritical) { if (totalRoll.isCritical) {
resourceMap.addResources([ resourceMap.addResources([
{ key: 'stress', value: -1, total: 1 }, { key: 'stress', value: -1 },
{ key: 'hope', value: 1, total: 1 } { key: 'hope', value: 1 }
]); ]);
} else if (totalRoll.withHope) { } else if (totalRoll.withHope) {
resourceMap.addResources([{ key: 'hope', value: 1, total: 1 }]); resourceMap.addResources([{ key: 'hope', value: 1 }]);
} else { } else {
resourceMap.addResources([{ key: 'fear', value: 1, total: 1 }]); resourceMap.addResources([{ key: 'fear', value: 1 }]);
} }
resourceMap.updateResources(); resourceMap.updateResources();

View file

@ -1,3 +1,4 @@
import { ResourceUpdateMap } from '../../data/action/baseAction.mjs';
import { MemberData } from '../../data/tagTeamData.mjs'; import { MemberData } from '../../data/tagTeamData.mjs';
import { getCritDamageBonus, shouldUseHopeFearAutomation } from '../../helpers/utils.mjs'; import { getCritDamageBonus, shouldUseHopeFearAutomation } from '../../helpers/utils.mjs';
import { emitGMUpdate, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; import { emitGMUpdate, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';
@ -9,6 +10,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
constructor(party) { constructor(party) {
super({ id: `TagTeamDialog-${party.id}` }); super({ id: `TagTeamDialog-${party.id}` });
this.usesTagTeamHopeCost = true;
this.party = party; this.party = party;
this.partyMembers = party.system.partyMembers this.partyMembers = party.system.partyMembers
.filter(x => Party.DICE_ROLL_ACTOR_TYPES.includes(x.type)) .filter(x => Party.DICE_ROLL_ACTOR_TYPES.includes(x.type))
@ -20,7 +22,9 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
owned: member.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER) owned: member.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER)
})); }));
this.initiator = { cost: 3 }; this.initiator = { cost:
this.party.system.schema.fields.tagTeam.fields.initiator.fields.cost.initial
};
this.openForAllPlayers = true; this.openForAllPlayers = true;
this.tabGroups.application = Object.keys(party.system.tagTeam.members).length this.tabGroups.application = Object.keys(party.system.tagTeam.members).length
@ -94,9 +98,13 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
?.addEventListener('input', this.updateInitiatorMemberField.bind(this)); ?.addEventListener('input', this.updateInitiatorMemberField.bind(this));
htmlElement htmlElement
.querySelector('.initiator-cost-field') .querySelector('.initiator-cost-input')
?.addEventListener('input', this.updateInitiatorCostField.bind(this)); ?.addEventListener('input', this.updateInitiatorCostField.bind(this));
htmlElement
.querySelector('.initiator-cost-enabled-checkbox')
?.addEventListener('change', this.toggleInitiatorCostEnabled.bind(this));
htmlElement htmlElement
.querySelector('.openforall-field') .querySelector('.openforall-field')
?.addEventListener('change', this.updateOpenForAllField.bind(this)); ?.addEventListener('change', this.updateOpenForAllField.bind(this));
@ -156,6 +164,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
.map(x => ({ value: x.id, label: x.name })); .map(x => ({ value: x.id, label: x.name }));
partContext.initiatorDisabled = !selectedMembers.length; partContext.initiatorDisabled = !selectedMembers.length;
partContext.openForAllPlayers = this.openForAllPlayers; partContext.openForAllPlayers = this.openForAllPlayers;
partContext.usesTagTeamHopeCost = this.usesTagTeamHopeCost;
break; break;
case 'tagTeamRoll': case 'tagTeamRoll':
@ -397,6 +406,13 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
this.render(); this.render();
} }
toggleInitiatorCostEnabled(_event) {
this.usesTagTeamHopeCost = !this.usesTagTeamHopeCost;
this.initiator.cost = this.usesTagTeamHopeCost ?
this.party.system.schema.fields.tagTeam.fields.initiator.fields.cost.initial : 0;
this.render();
}
updateOpenForAllField(event) { updateOpenForAllField(event) {
this.openForAllPlayers = event.target.checked; this.openForAllPlayers = event.target.checked;
this.render(); this.render();
@ -751,35 +767,38 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
await cls.create(msgData); await cls.create(msgData);
/* Handle resource updates from the finished TagTeamRoll */ /* Handle resource updates from the finished TagTeamRoll */
if (shouldUseHopeFearAutomation({ gmAsPlayer: true })) {
const tagTeamData = this.party.system.tagTeam; const tagTeamData = this.party.system.tagTeam;
const fearUpdate = { key: 'fear', value: null, total: null, enabled: true };
for (let memberId in tagTeamData.members) { const actorResourceMaps = Object.keys(tagTeamData.members).reduce((acc, key) => {
const resourceUpdates = []; acc[key] = new ResourceUpdateMap(game.actors.get(key));
const rollGivesHope = finalRoll.isCritical || finalRoll.withHope; return acc;
if (memberId === tagTeamData.initiator.memberId) { }, {});
const value = tagTeamData.initiator.cost
? rollGivesHope if (shouldUseHopeFearAutomation({ gmAsPlayer: true })) {
? 1 - tagTeamData.initiator.cost const fearResourceMap = actorResourceMaps[tagTeamData.initiator.memberId];
: -tagTeamData.initiator.cost for (const memberId in tagTeamData.members) {
: 1; const resourceMap = actorResourceMaps[memberId];
resourceUpdates.push({ key: 'hope', value: value, total: -value, enabled: true }); if (finalRoll.isCritical) {
} else if (rollGivesHope) { resourceMap.addResources([
resourceUpdates.push({ key: 'hope', value: 1, total: -1, enabled: true }); { key: 'stress', value: -1, enabled: true },
{ key: 'hope', value: 1, enabled: true }
]);
} else if (finalRoll.withHope) {
resourceMap.addResources([{ key: 'hope', value: 1, enabled: true }]);
} else if (finalRoll.withFear) {
fearResourceMap.addResources([{ key: 'fear', value: 1, enabled: true }]);
}
} }
if (finalRoll.isCritical) resourceUpdates.push({ key: 'stress', value: -1, total: 1, enabled: true });
if (finalRoll.withFear) {
fearUpdate.value = fearUpdate.value === null ? 1 : fearUpdate.value + 1;
fearUpdate.total = fearUpdate.total === null ? -1 : fearUpdate.total - 1;
} }
game.actors.get(memberId).modifyResource(resourceUpdates); /* Even with Hope/Fear automation off, the hope cost of performing the TagTeamRoll can still optionally be subtracted */
if (tagTeamData.initiator.cost) {
const resourceMap = actorResourceMaps[tagTeamData.initiator.memberId];
resourceMap.addResources([{ key: 'hope', value: -tagTeamData.initiator.cost, enabled: true }]);
} }
if (fearUpdate.value) { for (const resourceMap of Object.values(actorResourceMaps))
mainActor.modifyResource([fearUpdate]); resourceMap.updateResources();
}
}
/* Fin */ /* Fin */
this.cancelRoll({ confirm: false }); this.cancelRoll({ confirm: false });

View file

@ -54,7 +54,7 @@ export default class DHAdversarySettings extends DHBaseActorSettings {
async _prepareContext(options) { async _prepareContext(options) {
const context = await super._prepareContext(options); const context = await super._prepareContext(options);
const featureForms = ['passive', 'action', 'reaction']; const featureForms = Object.keys(CONFIG.DH.ITEM.featureForm);
context.features = context.document.system.features.sort((a, b) => context.features = context.document.system.features.sort((a, b) =>
a.system.featureForm !== b.system.featureForm a.system.featureForm !== b.system.featureForm
? featureForms.indexOf(a.system.featureForm) - featureForms.indexOf(b.system.featureForm) ? featureForms.indexOf(a.system.featureForm) - featureForms.indexOf(b.system.featureForm)
@ -97,32 +97,4 @@ export default class DHAdversarySettings extends DHBaseActorSettings {
await this.actor.update({ [`system.experiences.${target.dataset.experience}`]: _del }); await this.actor.update({ [`system.experiences.${target.dataset.experience}`]: _del });
} }
async _onDragStart(event) {
const featureItem = event.currentTarget.closest('.feature-item');
if (featureItem) {
const feature = this.actor.items.get(featureItem.id);
const featureData = { type: 'Item', uuid: feature.uuid, fromInternal: true };
event.dataTransfer.setData('text/plain', JSON.stringify(featureData));
event.dataTransfer.setDragImage(featureItem.querySelector('img'), 60, 0);
}
}
async _onDrop(event) {
event.stopPropagation();
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
const item = await fromUuid(data.uuid);
if (item?.type === 'feature') {
if (data.fromInternal && item.parent?.uuid === this.actor.uuid) {
return;
}
const itemData = item.toObject();
delete itemData._id;
await this.actor.createEmbeddedDocuments('Item', [itemData]);
}
}
} }

View file

@ -15,7 +15,7 @@ export default class DHEnvironmentSettings extends DHBaseActorSettings {
dragDrop: [ dragDrop: [
{ dragSelector: null, dropSelector: '.category-container' }, { dragSelector: null, dropSelector: '.category-container' },
{ dragSelector: null, dropSelector: '.tab.features' }, { dragSelector: null, dropSelector: '.tab.features' },
{ dragSelector: '.feature-item', dropSelector: null } { dragSelector: '.feature-item, .inventory-item[data-type="adversary"]', dropSelector: null }
] ]
}; };
@ -110,33 +110,30 @@ export default class DHEnvironmentSettings extends DHBaseActorSettings {
} }
async _onDragStart(event) { async _onDragStart(event) {
const featureItem = event.currentTarget.closest('.feature-item'); const element = event.currentTarget.closest('.inventory-item[data-type=adversary]');
if (element) {
if (featureItem) { const adversaryData = { type: 'Actor', uuid: element.dataset.itemUuid };
const feature = this.actor.items.get(featureItem.id); event.dataTransfer.setData('text/plain', JSON.stringify(adversaryData));
const featureData = { type: 'Item', uuid: feature.uuid, fromInternal: true }; event.dataTransfer.setDragImage(element, 60, 0);
event.dataTransfer.setData('text/plain', JSON.stringify(featureData)); } else {
event.dataTransfer.setDragImage(featureItem.querySelector('img'), 60, 0); return super._onDragStart(event);
} }
} }
async _onDrop(event) { async _onDrop(event) {
event.stopPropagation(); event.stopPropagation();
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event); const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
const item = await fromUuid(data.uuid); const doc = await fromUuid(data.uuid);
if (data.fromInternal && item?.parent?.uuid === this.actor.uuid) return; if (doc?.type === 'adversary' && event.target.closest('.category-container')) {
if (item.type === 'adversary' && event.target.closest('.category-container')) {
const target = event.target.closest('.category-container'); const target = event.target.closest('.category-container');
const path = `system.potentialAdversaries.${target.dataset.potentialAdversary}.adversaries`; const path = `system.potentialAdversaries.${target.dataset.potentialAdversary}.adversaries`;
const current = foundry.utils.getProperty(this.actor, path).map(x => x.uuid); const current = foundry.utils.getProperty(this.actor, path).map(x => x.uuid);
await this.actor.update({ if (!current.includes(doc.uuid)) {
[path]: [...current, item.uuid] await this.actor.update({ [path]: [...current, doc.uuid] });
});
this.render();
} else if (item.type === 'feature' && event.target.closest('.tab.features')) {
await this.actor.createEmbeddedDocuments('Item', [item]);
this.render();
} }
return;
}
return super._onDrop(event);
} }
} }

View file

@ -52,34 +52,4 @@ export default class DHNPCSettings extends DHBaseActorSettings {
return context; return context;
} }
/* -------------------------------------------- */
async _onDragStart(event) {
const featureItem = event.currentTarget.closest('.feature-item');
if (featureItem) {
const feature = this.actor.items.get(featureItem.id);
const featureData = { type: 'Item', uuid: feature.uuid, fromInternal: true };
event.dataTransfer.setData('text/plain', JSON.stringify(featureData));
event.dataTransfer.setDragImage(featureItem.querySelector('img'), 60, 0);
}
}
async _onDrop(event) {
event.stopPropagation();
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
const item = await fromUuid(data.uuid);
if (item?.type === 'feature') {
if (data.fromInternal && item.parent?.uuid === this.actor.uuid) {
return;
}
const itemData = item.toObject();
delete itemData._id;
await this.actor.createEmbeddedDocuments('Item', [itemData]);
}
}
} }

View file

@ -103,7 +103,7 @@ export default class AdversarySheet extends DHBaseActorSheet {
context.resources.stress.emptyPips = context.resources.stress.emptyPips =
context.resources.stress.max < maxResource ? maxResource - context.resources.stress.max : 0; context.resources.stress.max < maxResource ? maxResource - context.resources.stress.max : 0;
const featureForms = ['passive', 'action', 'reaction']; const featureForms = Object.keys(CONFIG.DH.ITEM.featureForm);
context.features = this.document.system.features.sort((a, b) => context.features = this.document.system.features.sort((a, b) =>
a.system.featureForm !== b.system.featureForm a.system.featureForm !== b.system.featureForm
? featureForms.indexOf(a.system.featureForm) - featureForms.indexOf(b.system.featureForm) ? featureForms.indexOf(a.system.featureForm) - featureForms.indexOf(b.system.featureForm)

View file

@ -809,7 +809,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
/* This could be avoided by baking config.costs into config.resourceUpdates. Didn't feel like messing with it at the time */ /* This could be avoided by baking config.costs into config.resourceUpdates. Didn't feel like messing with it at the time */
const costResources = const costResources =
result.costs?.filter(x => x.enabled).map(cost => ({ ...cost, value: -cost.value, total: -cost.total })) || result.costs?.filter(x => x.enabled).map(cost => ({ ...cost, value: -cost.value })) ||
{}; {};
result.resourceUpdates.addResources(costResources); result.resourceUpdates.addResources(costResources);
await result.resourceUpdates.updateResources(); await result.resourceUpdates.updateResources();

View file

@ -78,7 +78,6 @@ export default class DhpEnvironment extends DHBaseActorSheet {
switch (partId) { switch (partId) {
case 'header': case 'header':
await this._prepareHeaderContext(context, options); await this._prepareHeaderContext(context, options);
break; break;
case 'features': case 'features':
await this._prepareFeaturesContext(context, options); await this._prepareFeaturesContext(context, options);

View file

@ -1,13 +1,15 @@
import DHApplicationMixin from './application-mixin.mjs'; import DHApplicationMixin from './application-mixin.mjs';
const { DocumentSheetV2 } = foundry.applications.api; const { ActorSheetV2 } = foundry.applications.sheets;
/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */ /**
* @typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction
*/
/** /**
* Base settings sheet for Daggerheart actors. * Base settings sheet for Daggerheart actors.
* @extends {DHApplicationMixin<DocumentSheetV2>} * @extends {DHApplicationMixin<ActorSheetV2>}
*/ */
export default class DHBaseActorSettings extends DHApplicationMixin(DocumentSheetV2) { export default class DHBaseActorSettings extends DHApplicationMixin(ActorSheetV2) {
/**@inheritdoc */ /**@inheritdoc */
static DEFAULT_OPTIONS = { static DEFAULT_OPTIONS = {
classes: ['dialog'], classes: ['dialog'],
@ -34,7 +36,7 @@ export default class DHBaseActorSettings extends DHApplicationMixin(DocumentShee
return options; return options;
} }
/**@returns {foundry.documents.Actor} */ /** @returns {foundry.documents.Actor} */
get actor() { get actor() {
return this.document; return this.document;
} }
@ -73,4 +75,29 @@ export default class DHBaseActorSettings extends DHApplicationMixin(DocumentShee
return context; return context;
} }
async _onDragStart(event) {
const featureItemEl = event.currentTarget.closest('.feature-item');
const feature = this.actor.items.get(featureItemEl?.dataset.itemId);
if (feature && event.target.closest('.tab.features')) {
const featureData = { ...feature.toDragData(), fromInternal: true };
event.dataTransfer.setData('text/plain', JSON.stringify(featureData));
event.dataTransfer.setDragImage(featureItemEl.querySelector('img'), 60, 0);
}
}
async _onDrop(event) {
event.stopPropagation();
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
const item = await fromUuid(data.uuid);
if (item?.type === 'feature') {
if (data.fromInternal && item.parent?.uuid === this.actor.uuid) {
return super._onDrop(event);
}
const itemData = item.toObject();
delete itemData._id;
await this.actor.createEmbeddedDocuments('Item', [itemData]);
}
}
} }

View file

@ -381,12 +381,35 @@ export default function DHApplicationMixin(Base) {
* @protected * @protected
*/ */
_onDrop(event) { _onDrop(event) {
// Fallback to super, but note that config sheets don't have this option // Potentially handle subclasses that dont descend from actor/item sheet.
// We still need this to avoid setting apps having issues
event.stopPropagation(); event.stopPropagation();
return super._onDrop?.(event); return super._onDrop?.(event);
} }
/** @inheritdoc */
_onSortItem(event, item) {
// If we are dragging a feature past its allowed feature form, put it in the front or in the back
const doc = this.actor.items.get(item.id);
const dropTargetEl = event.target.closest('[data-item-id]');
const dropTarget = this.actor.items.get(dropTargetEl?.dataset.itemId);
if (doc?.type === 'feature' && dropTarget?.type === 'feature' && doc.system.featureForm !== dropTarget.system.featureForm) {
const siblings = this.actor.itemTypes.feature
.filter(f => f.system.featureForm === doc.system.featureForm)
.sort((a, b) => a.sort - b.sort);
if (siblings.length > 1) {
const featureForms = Object.keys(CONFIG.DH.ITEM.featureForm);
const thisFeatureIdx = featureForms.indexOf(doc.system.featureForm);
const targetFeatureIdx = featureForms.indexOf(dropTarget.system.featureForm);
const target = targetFeatureIdx < thisFeatureIdx ? siblings[0] : siblings.at(-1);
const sortUpdates = foundry.utils.performIntegerSort(doc, { target, siblings });
const updateData = sortUpdates.map(u => ({ ...u.update, _id: u.target._id }));
return this.actor.updateEmbeddedDocuments('Item', updateData);
}
}
return super._onSortItem?.(event, item);
}
/* -------------------------------------------- */ /* -------------------------------------------- */
/* Context Menu */ /* Context Menu */
/* -------------------------------------------- */ /* -------------------------------------------- */

View file

@ -459,8 +459,7 @@ export class ResourceUpdateMap extends Map {
} else if (!existing?.clear) { } else if (!existing?.clear) {
this.set(resource.key, { this.set(resource.key, {
...existing, ...existing,
value: existing.value + (resource.value ?? 0), value: existing.value + (resource.value ?? 0)
total: existing.total + (resource.total ?? 0)
}); });
} }
} }

View file

@ -315,7 +315,12 @@ export default class DhCharacter extends DhCreature {
label: 'DAGGERHEART.ACTORS.Character.defaultDisadvantageDice' label: 'DAGGERHEART.ACTORS.Character.defaultDisadvantageDice'
}) })
}) })
}) }),
/** Accumulated armor score from all sources */
armorScore: new fields.SchemaField({
value: new fields.NumberField(),
max: new fields.NumberField()
}, { persisted: false })
}; };
} }

View file

@ -332,15 +332,15 @@ export default class DualityRoll extends D20Roll {
const fear = const fear =
(config.roll.result.duality === -1 ? 1 : 0) - (config.rerolledRoll.result.duality === -1 ? 1 : 0); (config.roll.result.duality === -1 ? 1 : 0) - (config.rerolledRoll.result.duality === -1 ? 1 : 0);
if (hope !== 0) updates.push({ key: 'hope', value: hope, total: -1 * hope, enabled: true }); if (hope !== 0) updates.push({ key: 'hope', value: hope, enabled: true });
if (stress !== 0) updates.push({ key: 'stress', value: -1 * stress, total: stress, enabled: true }); if (stress !== 0) updates.push({ key: 'stress', value: -1 * stress, enabled: true });
if (fear !== 0) updates.push({ key: 'fear', value: fear, total: -1 * fear, enabled: true }); if (fear !== 0) updates.push({ key: 'fear', value: fear, enabled: true });
} }
} else { } else {
if (config.roll.isCritical || config.roll.result.duality === 1) if (config.roll.isCritical || config.roll.result.duality === 1)
updates.push({ key: 'hope', value: 1, total: -1, enabled: true }); updates.push({ key: 'hope', value: 1, enabled: true });
if (config.roll.isCritical) updates.push({ key: 'stress', value: -1, total: 1, enabled: true }); if (config.roll.isCritical) updates.push({ key: 'stress', value: -1, enabled: true });
if (config.roll.result.duality === -1) updates.push({ key: 'fear', value: 1, total: -1, enabled: true }); if (config.roll.result.duality === -1) updates.push({ key: 'fear', value: 1, enabled: true });
} }
if (updates.length) { if (updates.length) {

View file

@ -9,9 +9,9 @@ export function updateResourcesForDualityReroll(oldDuality, newDuality, actor) {
const stress = (newDuality === 0 ? 1 : 0) - (oldDuality === 0 ? 1 : 0); const stress = (newDuality === 0 ? 1 : 0) - (oldDuality === 0 ? 1 : 0);
const fear = (newDuality === -1 ? 1 : 0) - (oldDuality === -1 ? 1 : 0); const fear = (newDuality === -1 ? 1 : 0) - (oldDuality === -1 ? 1 : 0);
if (hope !== 0) updates.push({ key: 'hope', value: hope, total: -1 * hope, enabled: true }); if (hope !== 0) updates.push({ key: 'hope', value: hope, enabled: true });
if (stress !== 0) updates.push({ key: 'stress', value: -1 * stress, total: stress, enabled: true }); if (stress !== 0) updates.push({ key: 'stress', value: -1 * stress, enabled: true });
if (fear !== 0) updates.push({ key: 'fear', value: fear, total: -1 * fear, enabled: true }); if (fear !== 0) updates.push({ key: 'fear', value: fear, enabled: true });
const resourceUpdates = new ResourceUpdateMap(actor); const resourceUpdates = new ResourceUpdateMap(actor);
resourceUpdates.addResources(updates); resourceUpdates.addResources(updates);

View file

@ -88,9 +88,21 @@
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
gap: 8px; gap: 8px;
&.inactive { .inactive {
opacity: 0.4; opacity: 0.4;
} }
.initiator-cost-fields {
display: flex;
flex-direction: column;
align-items: flex-start;
.initiator-cost-inputs {
display: grid;
grid-template-columns: auto 1fr;
align-items: center;
}
}
} }
footer { footer {

View file

@ -26,10 +26,15 @@
</select> </select>
</div> </div>
</div> </div>
<div class="form-group"> <div class="initiator-cost-fields">
<label>{{localize "DAGGERHEART.APPLICATIONS.TagTeamSelect.FIELDS.initiator.cost.label"}}</label> <label>{{localize "DAGGERHEART.APPLICATIONS.TagTeamSelect.FIELDS.initiator.cost.label"}}</label>
<div class="form-fields"> <div class="initiator-cost-inputs">
<input type="text" data-dtype="Number" value="{{initiator.cost}}" class="initiator-cost-field" {{#if initiatorDisabled}}disabled{{/if}} /> <input type="checkbox" class="initiator-cost-enabled-checkbox {{#if initiatorDisabled}}inactive{{/if}}" {{checked usesTagTeamHopeCost}} {{#if initiatorDisabled}}disabled{{/if}} />
<input
type="text" data-dtype="Number" value="{{initiator.cost}}"
class="initiator-cost-input {{#if (or initiatorDisabled (not usesTagTeamHopeCost))}}inactive{{/if}}"
{{#if (or initiatorDisabled (not usesTagTeamHopeCost))}}disabled{{/if}}
/>
</div> </div>
</div> </div>
</div> </div>

View file

@ -10,7 +10,7 @@
<legend>{{localize tabs.features.label}}</legend> <legend>{{localize tabs.features.label}}</legend>
<ul class="feature-list"> <ul class="feature-list">
{{#each @root.features as |feature|}} {{#each @root.features as |feature|}}
<li class="feature-item" id="{{feature.id}}" draggable="true"> <li class="feature-item" data-item-id="{{feature.id}}" draggable="true">
<img src="{{feature.img}}" alt=""> <img src="{{feature.img}}" alt="">
<div class="label"> <div class="label">
<span>{{feature.name}}</span> <span>{{feature.name}}</span>

View file

@ -10,7 +10,7 @@
<legend>{{localize tabs.features.label}}</legend> <legend>{{localize tabs.features.label}}</legend>
<ul class="feature-list"> <ul class="feature-list">
{{#each @root.features as |feature|}} {{#each @root.features as |feature|}}
<li class="feature-item" id="{{feature.id}}"> <li class="feature-item" data-item-id="{{feature.id}}">
<img src="{{feature.img}}" alt=""> <img src="{{feature.img}}" alt="">
<div class="label"> <div class="label">
<span>{{feature.name}}</span> <span>{{feature.name}}</span>

View file

@ -10,7 +10,7 @@
<legend>{{localize tabs.features.label}}</legend> <legend>{{localize tabs.features.label}}</legend>
<ul class="feature-list"> <ul class="feature-list">
{{#each @root.features as |feature|}} {{#each @root.features as |feature|}}
<li class="feature-item" id="{{feature.id}}" draggable="true"> <li class="feature-item" data-item-id="{{feature.id}}" draggable="true">
<img src="{{feature.img}}" alt=""> <img src="{{feature.img}}" alt="">
<div class="label"> <div class="label">
<span>{{feature.name}}</span> <span>{{feature.name}}</span>