diff --git a/daggerheart.mjs b/daggerheart.mjs
index 240d8704..43aafce4 100644
--- a/daggerheart.mjs
+++ b/daggerheart.mjs
@@ -343,6 +343,17 @@ Hooks.on(CONFIG.DH.HOOKS.hooksConfig.tagTeamStart, async data => {
}
});
+Hooks.on(CONFIG.DH.HOOKS.hooksConfig.groupRollStart, async data => {
+ if (data.openForAllPlayers && data.partyId) {
+ const party = game.actors.get(data.partyId);
+ if (!party) return;
+
+ const dialog = new game.system.api.applications.dialogs.GroupRollDialog(party);
+ dialog.tabGroups.application = 'groupRoll';
+ await dialog.render({ force: true });
+ }
+});
+
const updateActorsRangeDependentEffects = async token => {
const rangeMeasurement = game.settings.get(
CONFIG.DH.id,
diff --git a/lang/en.json b/lang/en.json
index d19dfb58..65323790 100755
--- a/lang/en.json
+++ b/lang/en.json
@@ -131,6 +131,7 @@
"attackName": "Attack Name",
"criticalThreshold": "Critical Threshold",
"includeBase": { "label": "Include Item Damage" },
+ "groupAttack": { "label": "Group Attack" },
"multiplier": "Multiplier",
"saveHint": "Set a default Trait to enable Reaction Roll. It can be changed later in Reaction Roll Dialog.",
"resultBased": {
@@ -318,6 +319,21 @@
}
},
"newAdversary": "New Adversary"
+ },
+ "Party": {
+ "Subtitle": {
+ "character": "{community} {ancestry} | {subclass} {class}",
+ "companion": "Companion of {partner}"
+ },
+ "RemoveConfirmation": {
+ "title": "Remove member {name}",
+ "text": "Are you sure you want to remove {name} from the party?"
+ },
+ "Thresholds": {
+ "minor": "MIN",
+ "major": "MAJ",
+ "severe": "SEV"
+ }
}
},
"APPLICATIONS": {
@@ -353,7 +369,7 @@
"selectSecondaryWeapon": "Select Secondary Weapon",
"selectSubclass": "Select Subclass",
"setupSkipTitle": "Skipping Character Setup",
- "setupSkipContent": "You are skipping the Character Setup by adding this manually. The character setup is the blinking arrows in the top-right. Are you sure you want to continue?",
+ "setupSkipContent": "You are skipping the Character Setup by adding this manually. The character setup is the blinking button in the top-right. Are you sure you want to continue?",
"startingItems": "Starting Items",
"story": "Story",
"storyExplanation": "Select which background and connection prompts you want to copy into your character's background.",
@@ -717,6 +733,17 @@
"selectRoll": "Select which roll value to be used for the Tag Team"
}
},
+ "GroupRollSelect": {
+ "title": "Group Roll",
+ "leader": "Leader",
+ "leaderRoll": "Leader Roll",
+ "openDialogForAll": "Open Dialog For All",
+ "startGroupRoll": "Start Group Roll",
+ "cancelGroupRoll": "Cancel",
+ "finishGroupRoll": "Finish Group Roll",
+ "cancelConfirmTitle": "Cancel Group Roll",
+ "cancelConfirmText": "Are you sure you want to cancel the Group Roll? This will close it for all other players too."
+ },
"TokenConfig": {
"actorSizeUsed": "Actor size is set, determining the dimensions"
}
@@ -2857,6 +2884,7 @@
"system": "Dice Preset",
"font": "Font",
"critical": "Duality Critical Animation",
+ "muted": "Muted",
"diceAppearance": "Dice Appearance",
"animations": "Animations",
"defaultAnimations": "Set Animations As Player Defaults",
@@ -2965,18 +2993,6 @@
"immunityTo": "Immunity: {immunities}"
},
"featureTitle": "Class Feature",
- "groupRoll": {
- "title": "Group Roll",
- "leader": "Leader",
- "partyTeam": "Party Team",
- "team": "Team",
- "selectLeader": "Select a Leader",
- "selectMember": "Select a Member",
- "rerollTitle": "Reroll Group Roll",
- "rerollContent": "Are you sure you want to reroll your {trait} roll?",
- "rerollTooltip": "Reroll",
- "wholePartySelected": "The whole party is selected"
- },
"healingRoll": {
"title": "Heal - {damage}",
"heal": "Heal",
@@ -3142,7 +3158,8 @@
"tokenActorsMissing": "[{names}] missing Actors",
"domainTouchRequirement": "This domain card requires {nr} {domain} cards in the loadout to be used",
"knowTheTide": "Know The Tide gained a token",
- "lackingItemTransferPermission": "User {user} lacks owner permission needed to transfer items to {target}"
+ "lackingItemTransferPermission": "User {user} lacks owner permission needed to transfer items to {target}",
+ "noTokenTargeted": "No token is targeted"
},
"Progress": {
"migrationLabel": "Performing system migration. Please wait and do not close Foundry."
diff --git a/module/applications/dialogs/_module.mjs b/module/applications/dialogs/_module.mjs
index a479100a..c866f1cd 100644
--- a/module/applications/dialogs/_module.mjs
+++ b/module/applications/dialogs/_module.mjs
@@ -13,7 +13,7 @@ export { default as OwnershipSelection } from './ownershipSelection.mjs';
export { default as RerollDamageDialog } from './rerollDamageDialog.mjs';
export { default as ResourceDiceDialog } from './resourceDiceDialog.mjs';
export { default as ActionSelectionDialog } from './actionSelectionDialog.mjs';
-export { default as GroupRollDialog } from './group-roll-dialog.mjs';
export { default as TagTeamDialog } from './tagTeamDialog.mjs';
+export { default as GroupRollDialog } from './groupRollDialog.mjs';
export { default as RiskItAllDialog } from './riskItAllDialog.mjs';
export { default as CompendiumBrowserSettingsDialog } from './CompendiumBrowserSettings.mjs';
diff --git a/module/applications/dialogs/damageDialog.mjs b/module/applications/dialogs/damageDialog.mjs
index 97f1c538..46d3d41f 100644
--- a/module/applications/dialogs/damageDialog.mjs
+++ b/module/applications/dialogs/damageDialog.mjs
@@ -22,6 +22,7 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
},
actions: {
toggleSelectedEffect: this.toggleSelectedEffect,
+ updateGroupAttack: this.updateGroupAttack,
toggleCritical: this.toggleCritical,
submitRoll: this.submitRoll
},
@@ -64,15 +65,40 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
context.hasSelectedEffects = Boolean(Object.keys(this.selectedEffects).length);
context.selectedEffects = this.selectedEffects;
+ context.damageOptions = this.config.damageOptions;
+ context.rangeOptions = CONFIG.DH.GENERAL.groupAttackRange;
+
return context;
}
static updateRollConfiguration(_event, _, formData) {
- const { ...rest } = foundry.utils.expandObject(formData.object);
- foundry.utils.mergeObject(this.config.roll, rest.roll);
- foundry.utils.mergeObject(this.config.modifiers, rest.modifiers);
- this.config.selectedMessageMode = rest.selectedMessageMode;
+ const data = foundry.utils.expandObject(formData.object);
+ foundry.utils.mergeObject(this.config.roll, data.roll);
+ foundry.utils.mergeObject(this.config.modifiers, data.modifiers);
+ this.config.selectedMessageMode = data.selectedMessageMode;
+ if (data.damageOptions) {
+ const numAttackers = data.damageOptions.groupAttack?.numAttackers;
+ if (typeof numAttackers !== 'number' || numAttackers % 1 !== 0) {
+ data.damageOptions.groupAttack.numAttackers = null;
+ }
+
+ foundry.utils.mergeObject(this.config.damageOptions, data.damageOptions);
+ }
+
+ this.render();
+ }
+
+ static updateGroupAttack() {
+ const targets = Array.from(game.user.targets);
+ if (targets.length === 0)
+ return ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.noTokenTargeted'));
+
+ const actorId = this.roll.data.parent.id;
+ const range = this.config.damageOptions.groupAttack.range;
+ const groupAttackTokens = game.system.api.fields.ActionFields.DamageField.getGroupAttackTokens(actorId, range);
+
+ this.config.damageOptions.groupAttack.numAttackers = groupAttackTokens.length;
this.render();
}
diff --git a/module/applications/dialogs/group-roll-dialog.mjs b/module/applications/dialogs/group-roll-dialog.mjs
deleted file mode 100644
index 8a3c43d6..00000000
--- a/module/applications/dialogs/group-roll-dialog.mjs
+++ /dev/null
@@ -1,204 +0,0 @@
-import autocomplete from 'autocompleter';
-import { abilities } from '../../config/actorConfig.mjs';
-
-const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
-
-export default class GroupRollDialog extends HandlebarsApplicationMixin(ApplicationV2) {
- constructor(actors) {
- super();
- this.actors = actors;
- this.actorLeader = {};
- this.actorsMembers = [];
- }
-
- get title() {
- return 'Group Roll';
- }
-
- static DEFAULT_OPTIONS = {
- tag: 'form',
- classes: ['daggerheart', 'views', 'dh-style', 'dialog', 'group-roll'],
- position: { width: 'auto', height: 'auto' },
- window: {
- title: 'DAGGERHEART.UI.Chat.groupRoll.title'
- },
- actions: {
- roll: GroupRollDialog.#roll,
- removeLeader: GroupRollDialog.#removeLeader,
- removeMember: GroupRollDialog.#removeMember
- },
- form: { handler: this.updateData, submitOnChange: true, closeOnSubmit: false }
- };
-
- static PARTS = {
- application: {
- id: 'group-roll',
- template: 'systems/daggerheart/templates/dialogs/group-roll/group-roll.hbs'
- }
- };
-
- _attachPartListeners(partId, htmlElement, options) {
- super._attachPartListeners(partId, htmlElement, options);
- const leaderChoices = this.actors.filter(x => this.actorsMembers.every(member => member.actor?.id !== x.id));
- const memberChoices = this.actors.filter(
- x => this.actorLeader?.actor?.id !== x.id && this.actorsMembers.every(member => member.actor?.id !== x.id)
- );
-
- htmlElement.querySelectorAll('.leader-change-input').forEach(element => {
- autocomplete({
- input: element,
- fetch: function (text, update) {
- if (!text) {
- update(leaderChoices);
- } else {
- text = text.toLowerCase();
- var suggestions = leaderChoices.filter(n => n.name.toLowerCase().includes(text));
- update(suggestions);
- }
- },
- render: function (actor, search) {
- const actorName = game.i18n.localize(actor.name);
- const matchIndex = actorName.toLowerCase().indexOf(search);
-
- const beforeText = actorName.slice(0, matchIndex);
- const matchText = actorName.slice(matchIndex, matchIndex + search.length);
- const after = actorName.slice(matchIndex + search.length, actorName.length);
- const img = document.createElement('img');
- img.src = actor.img;
-
- const element = document.createElement('li');
- element.appendChild(img);
-
- const label = document.createElement('span');
- label.innerHTML =
- `${beforeText}${matchText ? `${matchText} ` : ''}${after}`.replaceAll(
- ' ',
- ' '
- );
- element.appendChild(label);
-
- return element;
- },
- renderGroup: function (label) {
- const itemElement = document.createElement('div');
- itemElement.textContent = game.i18n.localize(label);
- return itemElement;
- },
- onSelect: actor => {
- element.value = actor.uuid;
- this.actorLeader = { actor: actor, trait: 'agility', difficulty: 0 };
- this.render();
- },
- click: e => e.fetch(),
- customize: function (_input, _inputRect, container) {
- container.style.zIndex = foundry.applications.api.ApplicationV2._maxZ;
- },
- minLength: 0
- });
- });
-
- htmlElement.querySelectorAll('.team-push-input').forEach(element => {
- autocomplete({
- input: element,
- fetch: function (text, update) {
- if (!text) {
- update(memberChoices);
- } else {
- text = text.toLowerCase();
- var suggestions = memberChoices.filter(n => n.name.toLowerCase().includes(text));
- update(suggestions);
- }
- },
- render: function (actor, search) {
- const actorName = game.i18n.localize(actor.name);
- const matchIndex = actorName.toLowerCase().indexOf(search);
-
- const beforeText = actorName.slice(0, matchIndex);
- const matchText = actorName.slice(matchIndex, matchIndex + search.length);
- const after = actorName.slice(matchIndex + search.length, actorName.length);
- const img = document.createElement('img');
- img.src = actor.img;
-
- const element = document.createElement('li');
- element.appendChild(img);
-
- const label = document.createElement('span');
- label.innerHTML =
- `${beforeText}${matchText ? `${matchText} ` : ''}${after}`.replaceAll(
- ' ',
- ' '
- );
- element.appendChild(label);
-
- return element;
- },
- renderGroup: function (label) {
- const itemElement = document.createElement('div');
- itemElement.textContent = game.i18n.localize(label);
- return itemElement;
- },
- onSelect: actor => {
- element.value = actor.uuid;
- this.actorsMembers.push({ actor: actor, trait: 'agility', difficulty: 0 });
- this.render({ force: true });
- },
- click: e => e.fetch(),
- customize: function (_input, _inputRect, container) {
- container.style.zIndex = foundry.applications.api.ApplicationV2._maxZ;
- },
- minLength: 0
- });
- });
- }
-
- async _prepareContext(_options) {
- const context = await super._prepareContext(_options);
- context.leader = this.actorLeader;
- context.members = this.actorsMembers;
- context.traitList = abilities;
-
- context.allSelected = this.actorsMembers.length + (this.actorLeader?.actor ? 1 : 0) === this.actors.length;
- context.rollDisabled = context.members.length === 0 || !this.actorLeader?.actor;
-
- return context;
- }
-
- static updateData(event, _, formData) {
- const { actorLeader, actorsMembers } = foundry.utils.expandObject(formData.object);
- this.actorLeader = foundry.utils.mergeObject(this.actorLeader, actorLeader);
- this.actorsMembers = foundry.utils.mergeObject(this.actorsMembers, actorsMembers);
- this.render(true);
- }
-
- static async #removeLeader(_, button) {
- this.actorLeader = null;
- this.render();
- }
-
- static async #removeMember(_, button) {
- this.actorsMembers = this.actorsMembers.filter(m => m.actor.uuid !== button.dataset.memberUuid);
- this.render();
- }
-
- static async #roll() {
- const cls = getDocumentClass('ChatMessage');
- const systemData = {
- leader: this.actorLeader,
- members: this.actorsMembers
- };
- const msg = {
- type: 'groupRoll',
- user: game.user.id,
- speaker: cls.getSpeaker(),
- title: game.i18n.localize('DAGGERHEART.UI.Chat.groupRoll.title'),
- system: systemData,
- content: await foundry.applications.handlebars.renderTemplate(
- 'systems/daggerheart/templates/ui/chat/groupRoll.hbs',
- { system: systemData }
- )
- };
-
- cls.create(msg);
- this.close();
- }
-}
diff --git a/module/applications/dialogs/groupRollDialog.mjs b/module/applications/dialogs/groupRollDialog.mjs
new file mode 100644
index 00000000..2a7be791
--- /dev/null
+++ b/module/applications/dialogs/groupRollDialog.mjs
@@ -0,0 +1,527 @@
+import { ResourceUpdateMap } from '../../data/action/baseAction.mjs';
+import { emitAsGM, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';
+import Party from '../sheets/actors/party.mjs';
+
+const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
+
+export default class GroupRollDialog extends HandlebarsApplicationMixin(ApplicationV2) {
+ constructor(party) {
+ super();
+
+ this.party = party;
+ this.partyMembers = party.system.partyMembers
+ .filter(x => Party.DICE_ROLL_ACTOR_TYPES.includes(x.type))
+ .map(member => ({
+ ...member.toObject(),
+ uuid: member.uuid,
+ id: member.id,
+ selected: true,
+ owned: member.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER)
+ }));
+
+ this.leader = null;
+ this.openForAllPlayers = true;
+
+ this.tabGroups.application = Object.keys(party.system.groupRoll.participants).length
+ ? 'groupRoll'
+ : 'initialization';
+
+ Hooks.on(socketEvent.Refresh, this.groupRollRefresh.bind());
+ }
+
+ get title() {
+ return game.i18n.localize('DAGGERHEART.APPLICATIONS.GroupRollSelect.title');
+ }
+
+ static DEFAULT_OPTIONS = {
+ tag: 'form',
+ id: 'GroupRollDialog',
+ classes: ['daggerheart', 'views', 'dh-style', 'dialog', 'group-roll-dialog'],
+ position: { width: 550, height: 'auto' },
+ actions: {
+ toggleSelectMember: this.#toggleSelectMember,
+ startGroupRoll: this.#startGroupRoll,
+ makeRoll: this.#makeRoll,
+ removeRoll: this.#removeRoll,
+ rerollDice: this.#rerollDice,
+ makeLeaderRoll: this.#makeLeaderRoll,
+ removeLeaderRoll: this.#removeLeaderRoll,
+ rerollLeaderDice: this.#rerollLeaderDice,
+ markSuccessfull: this.#markSuccessfull,
+ cancelRoll: this.#onCancelRoll,
+ finishRoll: this.#finishRoll
+ },
+ form: { handler: this.updateData, submitOnChange: true, closeOnSubmit: false }
+ };
+
+ static PARTS = {
+ initialization: {
+ id: 'initialization',
+ template: 'systems/daggerheart/templates/dialogs/groupRollDialog/initialization.hbs'
+ },
+ leader: {
+ id: 'leader',
+ template: 'systems/daggerheart/templates/dialogs/groupRollDialog/leader.hbs'
+ },
+ groupRoll: {
+ id: 'groupRoll',
+ template: 'systems/daggerheart/templates/dialogs/groupRollDialog/groupRoll.hbs'
+ },
+ footer: {
+ id: 'footer',
+ template: 'systems/daggerheart/templates/dialogs/groupRollDialog/footer.hbs'
+ }
+ };
+
+ /** @inheritdoc */
+ static TABS = {
+ application: {
+ tabs: [{ id: 'initialization' }, { id: 'groupRoll' }]
+ }
+ };
+
+ _attachPartListeners(partId, htmlElement, options) {
+ super._attachPartListeners(partId, htmlElement, options);
+
+ htmlElement
+ .querySelector('.main-character-field')
+ ?.addEventListener('input', this.updateLeaderField.bind(this));
+ }
+
+ _configureRenderParts(options) {
+ const { initialization, leader, groupRoll, footer } = super._configureRenderParts(options);
+ const augmentedParts = { initialization };
+ for (const memberKey of Object.keys(this.party.system.groupRoll.aidingCharacters)) {
+ augmentedParts[memberKey] = {
+ id: memberKey,
+ template: 'systems/daggerheart/templates/dialogs/groupRollDialog/groupRollMember.hbs'
+ };
+ }
+
+ augmentedParts.leader = leader;
+ augmentedParts.groupRoll = groupRoll;
+ augmentedParts.footer = footer;
+
+ return augmentedParts;
+ }
+
+ /**@inheritdoc */
+ async _onRender(context, options) {
+ await super._onRender(context, options);
+
+ if (this.element.querySelector('.team-container')) return;
+
+ if (this.tabGroups.application !== this.constructor.PARTS.initialization.id) {
+ const initializationPart = this.element.querySelector('.initialization-container');
+ initializationPart.insertAdjacentHTML('afterend', '
');
+ initializationPart.insertAdjacentHTML(
+ 'afterend',
+ `${game.i18n.localize('Aiding Characters')}
`
+ );
+
+ const teamContainer = this.element.querySelector('.team-container');
+ for (const memberContainer of this.element.querySelectorAll('.team-member-container'))
+ teamContainer.appendChild(memberContainer);
+ }
+ }
+
+ async _prepareContext(_options) {
+ const context = await super._prepareContext(_options);
+
+ context.isGM = game.user.isGM;
+ context.isEditable = this.getIsEditable();
+ context.fields = this.party.system.schema.fields.groupRoll.fields;
+ context.data = this.party.system.groupRoll;
+ context.traitOptions = CONFIG.DH.ACTOR.abilities;
+ context.members = {};
+ context.allHaveRolled = Object.keys(context.data.participants).every(key => {
+ const data = context.data.participants[key];
+ return Boolean(data.rollData);
+ });
+
+ return context;
+ }
+
+ async _preparePartContext(partId, context, options) {
+ const partContext = await super._preparePartContext(partId, context, options);
+ partContext.partId = partId;
+
+ switch (partId) {
+ case 'initialization':
+ partContext.groupRollFields = this.party.system.schema.fields.groupRoll.fields;
+ partContext.memberSelection = this.partyMembers;
+
+ const selectedMembers = partContext.memberSelection.filter(x => x.selected);
+
+ partContext.selectedLeader = this.leader;
+ partContext.selectedLeaderOptions = selectedMembers
+ .filter(actor => actor.owned)
+ .map(x => ({ value: x.id, label: x.name }));
+ partContext.selectedLeaderDisabled = !selectedMembers.length;
+
+ partContext.canStartGroupRoll = selectedMembers.length > 1 && this.leader?.memberId;
+ partContext.openForAllPlayers = this.openForAllPlayers;
+ break;
+ case 'leader':
+ partContext.leader = this.getRollCharacterData(this.party.system.groupRoll.leader);
+ break;
+ case 'groupRoll':
+ const leader = this.party.system.groupRoll.leader;
+ partContext.hasRolled =
+ leader?.rollData ||
+ Object.values(this.party.system.groupRoll?.aidingCharacters ?? {}).some(
+ x => x.successfull !== null
+ );
+ const { modifierTotal, modifiers } = Object.values(this.party.system.groupRoll.aidingCharacters).reduce(
+ (acc, curr) => {
+ const modifier = curr.successfull === true ? 1 : curr.successfull === false ? -1 : null;
+ if (modifier) {
+ acc.modifierTotal += modifier;
+ acc.modifiers.push(modifier);
+ }
+
+ return acc;
+ },
+ { modifierTotal: 0, modifiers: [] }
+ );
+ const leaderTotal = leader?.rollData ? leader.roll.total : null;
+ partContext.groupRoll = {
+ totalLabel: leader?.rollData
+ ? game.i18n.format('DAGGERHEART.GENERAL.withThing', {
+ thing: leader.roll.totalLabel
+ })
+ : null,
+ totalDualityClass: leader?.roll?.isCritical ? 'critical' : leader?.roll?.withHope ? 'hope' : 'fear',
+ total: leaderTotal + modifierTotal,
+ leaderTotal: leaderTotal,
+ modifiers
+ };
+ break;
+ case 'footer':
+ partContext.canFinishRoll =
+ Boolean(this.party.system.groupRoll.leader?.rollData) &&
+ Object.values(this.party.system.groupRoll.aidingCharacters).every(x => x.successfull !== null);
+ break;
+ }
+
+ if (Object.keys(this.party.system.groupRoll.aidingCharacters).includes(partId)) {
+ const characterData = this.party.system.groupRoll.aidingCharacters[partId];
+ partContext.members[partId] = this.getRollCharacterData(characterData, partId);
+ }
+
+ return partContext;
+ }
+
+ getRollCharacterData(data, partId) {
+ if (!data) return {};
+
+ const actor = game.actors.get(data.id);
+
+ return {
+ ...data,
+ roll: data.roll,
+ isEditable: actor.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER),
+ key: partId,
+ readyToRoll: Boolean(data.rollChoice),
+ hasRolled: Boolean(data.rollData)
+ };
+ }
+
+ static async updateData(event, _, formData) {
+ const partyData = foundry.utils.expandObject(formData.object);
+
+ this.updatePartyData(partyData, this.getUpdatingParts(event.target));
+ }
+
+ async updatePartyData(update, updatingParts, options = { render: true }) {
+ if (!game.users.activeGM)
+ return ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.gmRequired'));
+
+ const gmUpdate = async update => {
+ await this.party.update(update);
+ this.render({ parts: updatingParts });
+ game.socket.emit(`system.${CONFIG.DH.id}`, {
+ action: socketEvent.Refresh,
+ data: { refreshType: RefreshType.GroupRoll, action: 'refresh', parts: updatingParts }
+ });
+ };
+
+ await emitAsGM(
+ GMUpdateEvent.UpdateDocument,
+ gmUpdate,
+ update,
+ this.party.uuid,
+ options.render ? { refreshType: RefreshType.GroupRoll, action: 'refresh', parts: updatingParts } : undefined
+ );
+ }
+
+ getUpdatingParts(target) {
+ const { initialization, leader, groupRoll, footer } = this.constructor.PARTS;
+ const isInitialization = this.tabGroups.application === initialization.id;
+ const updatingMember = target.closest('.team-member-container')?.dataset?.memberKey;
+ const updatingLeader = target.closest('.main-character-outer-container');
+
+ return [
+ ...(isInitialization ? [initialization.id] : []),
+ ...(updatingMember ? [updatingMember] : []),
+ ...(updatingLeader ? [leader.id] : []),
+ ...(!isInitialization ? [groupRoll.id, footer.id] : [])
+ ];
+ }
+
+ getIsEditable() {
+ return this.party.system.partyMembers.some(actor => {
+ const selected = Boolean(this.party.system.groupRoll.participants[actor.id]);
+ return selected && actor.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER);
+ });
+ }
+
+ groupRollRefresh = ({ refreshType, action, parts }) => {
+ if (refreshType !== RefreshType.GroupRoll) return;
+
+ switch (action) {
+ case 'startGroupRoll':
+ this.tabGroups.application = 'groupRoll';
+ break;
+ case 'refresh':
+ this.render({ parts });
+ break;
+ case 'close':
+ this.close();
+ break;
+ }
+ };
+
+ async close(options = {}) {
+ /* Opt out of Foundry's standard behavior of closing all application windows marked as UI when Escape is pressed */
+ if (options.closeKey) return;
+
+ Hooks.off(socketEvent.Refresh, this.groupRollRefresh);
+ return super.close(options);
+ }
+
+ //#region Initialization
+ static #toggleSelectMember(_, button) {
+ const member = this.partyMembers.find(x => x.id === button.dataset.id);
+ member.selected = !member.selected;
+ this.render();
+ }
+
+ updateLeaderField(event) {
+ if (!this.leader) this.leader = {};
+ this.leader.memberId = event.target.value;
+ this.render();
+ }
+
+ static async #startGroupRoll() {
+ const leader = this.partyMembers.find(x => x.id === this.leader.memberId);
+ const aidingCharacters = this.partyMembers.reduce((acc, curr) => {
+ if (curr.selected && curr.id !== this.leader.memberId)
+ acc[curr.id] = { id: curr.id, name: curr.name, img: curr.img };
+
+ return acc;
+ }, {});
+
+ await this.party.update({
+ 'system.groupRoll': _replace(
+ new game.system.api.data.GroupRollData({
+ ...this.party.system.groupRoll.toObject(),
+ leader: { id: leader.id, name: leader.name, img: leader.img },
+ aidingCharacters
+ })
+ )
+ });
+
+ const hookData = { openForAllPlayers: this.openForAllPlayers, partyId: this.party.id };
+ Hooks.callAll(CONFIG.DH.HOOKS.hooksConfig.groupRollStart, hookData);
+ game.socket.emit(`system.${CONFIG.DH.id}`, {
+ action: socketEvent.GroupRollStart,
+ data: hookData
+ });
+
+ this.render();
+ }
+ //#endregion
+
+ async makeRoll(button, characterData, path) {
+ const actor = game.actors.find(x => x.id === characterData.id);
+ if (!actor) return;
+
+ const result = await actor.rollTrait(characterData.rollChoice, {
+ skips: {
+ createMessage: true,
+ resources: true,
+ triggers: true
+ }
+ });
+
+ if (!result) return;
+ if (!game.modules.get('dice-so-nice')?.active) foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice });
+
+ const rollData = result.messageRoll.toJSON();
+ delete rollData.options.messageRoll;
+ this.updatePartyData(
+ {
+ [path]: rollData
+ },
+ this.getUpdatingParts(button)
+ );
+ }
+
+ static async #makeRoll(_event, button) {
+ const { member } = button.dataset;
+ const character = this.party.system.groupRoll.aidingCharacters[member];
+ this.makeRoll(button, character, `system.groupRoll.aidingCharacters.${member}.rollData`);
+ }
+
+ static async #makeLeaderRoll(_event, button) {
+ const character = this.party.system.groupRoll.leader;
+ this.makeRoll(button, character, 'system.groupRoll.leader.rollData');
+ }
+
+ async removeRoll(button, path) {
+ this.updatePartyData(
+ {
+ [path]: {
+ rollData: null,
+ rollChoice: null,
+ selected: false,
+ successfull: null
+ }
+ },
+ this.getUpdatingParts(button)
+ );
+ }
+
+ static async #removeRoll(_event, button) {
+ this.removeRoll(button, `system.groupRoll.aidingCharacters.${button.dataset.member}`);
+ }
+
+ static async #removeLeaderRoll(_event, button) {
+ this.removeRoll(button, 'system.groupRoll.leader');
+ }
+
+ async rerollDice(button, data, path) {
+ const { diceType } = button.dataset;
+
+ const dieIndex = diceType === 'hope' ? 0 : diceType === 'fear' ? 1 : 2;
+ const newRoll = game.system.api.dice.DualityRoll.fromData(data.rollData);
+ const dice = newRoll.dice[dieIndex];
+ await dice.reroll(`/r1=${dice.total}`, {
+ liveRoll: {
+ roll: newRoll,
+ isReaction: true
+ }
+ });
+ const rollData = newRoll.toJSON();
+ this.updatePartyData(
+ {
+ [path]: rollData
+ },
+ this.getUpdatingParts(button)
+ );
+ }
+
+ static async #rerollDice(_, button) {
+ const { member } = button.dataset;
+ this.rerollDice(
+ button,
+ this.party.system.groupRoll.aidingCharacters[member],
+ `system.groupRoll.aidingCharacters.${member}.rollData`
+ );
+ }
+
+ static async #rerollLeaderDice(_, button) {
+ this.rerollDice(button, this.party.system.groupRoll.leader, `system.groupRoll.leader.rollData`);
+ }
+
+ static #markSuccessfull(_event, button) {
+ const previousValue = this.party.system.groupRoll.aidingCharacters[button.dataset.member].successfull;
+ const newValue = Boolean(button.dataset.successfull === 'true');
+ this.updatePartyData(
+ {
+ [`system.groupRoll.aidingCharacters.${button.dataset.member}.successfull`]:
+ previousValue === newValue ? null : newValue
+ },
+ this.getUpdatingParts(button)
+ );
+ }
+
+ static async #onCancelRoll(_event, _button, options = { confirm: true }) {
+ this.cancelRoll(options);
+ }
+
+ async cancelRoll(options = { confirm: true }) {
+ if (options.confirm) {
+ const confirmed = await foundry.applications.api.DialogV2.confirm({
+ window: {
+ title: game.i18n.localize('DAGGERHEART.APPLICATIONS.GroupRollSelect.cancelConfirmTitle')
+ },
+ content: game.i18n.localize('DAGGERHEART.APPLICATIONS.GroupRollSelect.cancelConfirmText')
+ });
+
+ if (!confirmed) return;
+ }
+
+ await this.updatePartyData(
+ {
+ 'system.groupRoll': {
+ leader: null,
+ aidingCharacters: _replace({})
+ }
+ },
+ [],
+ { render: false }
+ );
+
+ this.close();
+ game.socket.emit(`system.${CONFIG.DH.id}`, {
+ action: socketEvent.Refresh,
+ data: { refreshType: RefreshType.GroupRoll, action: 'close' }
+ });
+ }
+
+ static async #finishRoll() {
+ const totalRoll = this.party.system.groupRoll.leader.roll;
+ for (const character of Object.values(this.party.system.groupRoll.aidingCharacters)) {
+ totalRoll.terms.push(new foundry.dice.terms.OperatorTerm({ operator: character.successfull ? '+' : '-' }));
+ totalRoll.terms.push(new foundry.dice.terms.NumericTerm({ number: 1 }));
+ }
+
+ await totalRoll._evaluate();
+
+ const systemData = totalRoll.options;
+ const actor = game.actors.get(this.party.system.groupRoll.leader.id);
+
+ const cls = getDocumentClass('ChatMessage'),
+ msgData = {
+ type: 'dualityRoll',
+ user: game.user.id,
+ title: game.i18n.localize('DAGGERHEART.APPLICATIONS.GroupRollSelect.title'),
+ speaker: cls.getSpeaker({ actor }),
+ system: systemData,
+ rolls: [JSON.stringify(totalRoll)],
+ sound: null,
+ flags: { core: { RollTable: true } }
+ };
+
+ await cls.create(msgData);
+
+ const resourceMap = new ResourceUpdateMap(actor);
+ if (totalRoll.isCritical) {
+ resourceMap.addResources([
+ { key: 'stress', value: -1, total: 1 },
+ { key: 'hope', value: 1, total: 1 }
+ ]);
+ } else if (totalRoll.withHope) {
+ resourceMap.addResources([{ key: 'hope', value: 1, total: 1 }]);
+ } else {
+ resourceMap.addResources([{ key: 'fear', value: 1, total: 1 }]);
+ }
+
+ resourceMap.updateResources();
+
+ /* Fin */
+ this.cancelRoll({ confirm: false });
+ }
+}
diff --git a/module/applications/dialogs/tagTeamDialog.mjs b/module/applications/dialogs/tagTeamDialog.mjs
index 5236afb8..054331b5 100644
--- a/module/applications/dialogs/tagTeamDialog.mjs
+++ b/module/applications/dialogs/tagTeamDialog.mjs
@@ -366,8 +366,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
let rollIsSelected = false;
for (const member of Object.values(members)) {
const rollFinished = Boolean(member.rollData);
- const damageFinished =
- member.rollData?.options?.hasDamage !== undefined ? member.rollData.options.damage : true;
+ const damageFinished = member.rollData?.options?.hasDamage ? Boolean(member.rollData.options.damage) : true;
rollsAreFinished = rollsAreFinished && rollFinished && damageFinished;
rollIsSelected = rollIsSelected || member.selected;
diff --git a/module/applications/sheets/actors/party.mjs b/module/applications/sheets/actors/party.mjs
index c5e77112..d4545f63 100644
--- a/module/applications/sheets/actors/party.mjs
+++ b/module/applications/sheets/actors/party.mjs
@@ -1,10 +1,9 @@
import DHBaseActorSheet from '../api/base-actor.mjs';
-import { getDocFromElement } from '../../../helpers/utils.mjs';
+import { getDocFromElement, sortBy } from '../../../helpers/utils.mjs';
import { ItemBrowser } from '../../ui/itemBrowser.mjs';
import FilterMenu from '../../ux/filter-menu.mjs';
import DaggerheartMenu from '../../sidebar/tabs/daggerheartMenu.mjs';
import { socketEvent } from '../../../systemRegistration/socket.mjs';
-import GroupRollDialog from '../../dialogs/group-roll-dialog.mjs';
import DhpActor from '../../../documents/actor.mjs';
export default class Party extends DHBaseActorSheet {
@@ -18,13 +17,14 @@ export default class Party extends DHBaseActorSheet {
static DEFAULT_OPTIONS = {
classes: ['party'],
position: {
- width: 550,
+ width: 600,
height: 900
},
window: {
resizable: true
},
actions: {
+ openDocument: Party.#openDocument,
deletePartyMember: Party.#deletePartyMember,
deleteItem: Party.#deleteItem,
toggleHope: Party.#toggleHope,
@@ -45,10 +45,6 @@ export default class Party extends DHBaseActorSheet {
header: { template: 'systems/daggerheart/templates/sheets/actors/party/header.hbs' },
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
partyMembers: { template: 'systems/daggerheart/templates/sheets/actors/party/party-members.hbs' },
- resources: {
- template: 'systems/daggerheart/templates/sheets/actors/party/resources.hbs',
- scrollable: ['']
- },
/* NOT YET IMPLEMENTED */
// projects: {
// template: 'systems/daggerheart/templates/sheets/actors/party/projects.hbs',
@@ -66,7 +62,6 @@ export default class Party extends DHBaseActorSheet {
primary: {
tabs: [
{ id: 'partyMembers' },
- { id: 'resources' },
/* NOT YET IMPLEMENTED */
// { id: 'projects' },
{ id: 'inventory' },
@@ -96,6 +91,8 @@ export default class Party extends DHBaseActorSheet {
case 'header':
await this._prepareHeaderContext(context, options);
break;
+ case 'partyMembers':
+ await this._prepareMembersContext(context, options);
case 'notes':
await this._prepareNotesContext(context, options);
break;
@@ -119,6 +116,60 @@ export default class Party extends DHBaseActorSheet {
relativeTo: this.document
});
context.tagTeamActive = Boolean(this.document.system.tagTeam.initiator);
+ context.groupRollActive = Boolean(this.document.system.groupRoll.leader);
+ }
+
+ async _prepareMembersContext(context, _options) {
+ context.partyMembers = [];
+ const traits = ['agility', 'strength', 'finesse', 'instinct', 'presence', 'knowledge'];
+ for (const actor of this.document.system.partyMembers) {
+ const weapons = [];
+ if (actor.type === 'character') {
+ if (actor.system.usedUnarmed) {
+ weapons.push(actor.system.usedUnarmed);
+ }
+ const equipped = actor.items.filter(i => i.system.equipped && i.type === 'weapon');
+ weapons.push(...sortBy(equipped, i => (i.system.secondary ? 1 : 0)));
+ }
+
+ context.partyMembers.push({
+ uuid: actor.uuid,
+ img: actor.img,
+ name: actor.name,
+ subtitle: (() => {
+ if (!['character', 'companion'].includes(actor.type)) {
+ return game.i18n.format(`TYPES.Actor.${actor.type}`);
+ }
+
+ const { value: classItem, subclass } = actor.system.class ?? {};
+ const partner = actor.system.partner;
+ const ancestry = actor.system.ancestry;
+ const community = actor.system.community;
+ if (partner || (classItem && subclass && ancestry && community)) {
+ return game.i18n.format(`DAGGERHEART.ACTORS.Party.Subtitle.${actor.type}`, {
+ class: classItem?.name,
+ subclass: subclass?.name,
+ partner: partner?.name,
+ ancestry: ancestry?.name,
+ community: community?.name
+ });
+ }
+ })(),
+ type: actor.type,
+ resources: actor.system.resources,
+ armorScore: actor.system.armorScore,
+ damageThresholds: actor.system.damageThresholds,
+ evasion: actor.system.evasion,
+ difficulty: actor.system.difficulty,
+ traits: actor.system.traits
+ ? traits.map(t => ({
+ label: game.i18n.localize(`DAGGERHEART.CONFIG.Traits.${t}.short`),
+ value: actor.system.traits[t].value
+ }))
+ : null,
+ weapons
+ });
+ }
}
/**
@@ -149,6 +200,12 @@ export default class Party extends DHBaseActorSheet {
}
}
+ static async #openDocument(_, target) {
+ const uuid = target.dataset.uuid;
+ const document = await foundry.utils.fromUuid(uuid);
+ document?.sheet?.render(true);
+ }
+
/**
* Toggles a hope resource value.
* @type {ApplicationClickAction}
@@ -190,7 +247,7 @@ export default class Party extends DHBaseActorSheet {
* @type {ApplicationClickAction}
*/
static async #toggleArmorSlot(_, target) {
- const actor = game.actors.get(target.dataset.actorId);
+ const actor = await foundry.utils.fromUuid(target.dataset.actorId);
const { value, max } = actor.system.armorScore;
const inputValue = Number.parseInt(target.dataset.value);
const newValue = value >= inputValue ? inputValue - 1 : inputValue;
@@ -261,9 +318,7 @@ export default class Party extends DHBaseActorSheet {
}
static async #groupRoll(_params) {
- new GroupRollDialog(
- this.document.system.partyMembers.filter(x => Party.DICE_ROLL_ACTOR_TYPES.includes(x.type))
- ).render({ force: true });
+ new game.system.api.applications.dialogs.GroupRollDialog(this.document).render({ force: true });
}
/* -------------------------------------------- */
@@ -425,25 +480,23 @@ export default class Party extends DHBaseActorSheet {
}
static async #deletePartyMember(event, target) {
- const doc = await getDocFromElement(target.closest('.inventory-item'));
-
+ const doc = await foundry.utils.fromUuid(target.closest('[data-uuid]')?.dataset.uuid);
if (!event.shiftKey) {
const confirmed = await foundry.applications.api.DialogV2.confirm({
window: {
- title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', {
- type: game.i18n.localize('TYPES.Actor.adversary'),
+ title: game.i18n.format('DAGGERHEART.ACTORS.Party.RemoveConfirmation.title', {
name: doc.name
})
},
- content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { name: doc.name })
+ content: game.i18n.format('DAGGERHEART.ACTORS.Party.RemoveConfirmation.text', { name: doc.name })
});
if (!confirmed) return;
}
const currentMembers = this.document.system.partyMembers.map(x => x.uuid);
- const newMemberdList = currentMembers.filter(uuid => uuid !== doc.uuid);
- await this.document.update({ 'system.partyMembers': newMemberdList });
+ const newMembersList = currentMembers.filter(uuid => uuid !== doc.uuid);
+ await this.document.update({ 'system.partyMembers': newMembersList });
}
static async #deleteItem(event, target) {
diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs
index 8cbacb09..59939963 100644
--- a/module/applications/ui/chatLog.mjs
+++ b/module/applications/ui/chatLog.mjs
@@ -1,8 +1,6 @@
-import { abilities } from '../../config/actorConfig.mjs';
import { enrichedDualityRoll } from '../../enrichers/DualityRollEnricher.mjs';
import { enrichedFateRoll, getFateTypeData } from '../../enrichers/FateRollEnricher.mjs';
import { getCommandTarget, rollCommandToJSON } from '../../helpers/utils.mjs';
-import { emitAsGM, GMUpdateEvent } from '../../systemRegistration/socket.mjs';
export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLog {
constructor(options) {
@@ -150,18 +148,6 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
html.querySelectorAll('.reroll-button').forEach(element =>
element.addEventListener('click', event => this.rerollEvent(event, message))
);
- html.querySelectorAll('.group-roll-button').forEach(element =>
- element.addEventListener('click', event => this.groupRollButton(event, message))
- );
- html.querySelectorAll('.group-roll-reroll').forEach(element =>
- element.addEventListener('click', event => this.groupRollReroll(event, message))
- );
- html.querySelectorAll('.group-roll-success').forEach(element =>
- element.addEventListener('click', event => this.groupRollSuccessEvent(event, message))
- );
- html.querySelectorAll('.group-roll-header-expand-section').forEach(element =>
- element.addEventListener('click', this.groupRollExpandSection)
- );
html.querySelectorAll('.risk-it-all-button').forEach(element =>
element.addEventListener('click', event => this.riskItAllClearStressAndHitPoints(event, data))
);
@@ -305,174 +291,6 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
}
}
- async groupRollButton(event, message) {
- const path = event.currentTarget.dataset.path;
- const isLeader = path === 'leader';
- const { actor: actorData, trait } = foundry.utils.getProperty(message.system, path);
- const actor = game.actors.get(actorData._id);
-
- if (!actor) {
- return ui.notifications.error(
- game.i18n.format('DAGGERHEART.UI.Notifications.documentIsMissing', {
- documentType: game.i18n.localize('TYPES.Actor.character')
- })
- );
- }
-
- if (!actor.testUserPermission(game.user, 'OWNER')) {
- return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.noActorOwnership'));
- }
-
- const traitLabel = game.i18n.localize(abilities[trait].label);
- const config = {
- event: event,
- title: `${game.i18n.localize('DAGGERHEART.GENERAL.dualityRoll')}: ${actor.name}`,
- headerTitle: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
- ability: traitLabel
- }),
- roll: {
- trait: trait,
- advantage: 0,
- modifiers: [{ label: traitLabel, value: actor.system.traits[trait].value }]
- },
- hasRoll: true,
- skips: {
- createMessage: true,
- resources: !isLeader,
- updateCountdowns: !isLeader
- }
- };
- const result = await actor.diceRoll({
- ...config,
- headerTitle: `${game.i18n.localize('DAGGERHEART.GENERAL.dualityRoll')}: ${actor.name}`,
- title: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
- ability: traitLabel
- })
- });
-
- if (!result) return;
-
- const newMessageData = foundry.utils.deepClone(message.system);
- foundry.utils.setProperty(newMessageData, `${path}.result`, result.roll);
- const renderData = { system: new game.system.api.models.chatMessages.config.groupRoll(newMessageData) };
-
- const updatedContent = await foundry.applications.handlebars.renderTemplate(
- 'systems/daggerheart/templates/ui/chat/groupRoll.hbs',
- { ...renderData, user: game.user }
- );
- const mess = game.messages.get(message._id);
-
- await emitAsGM(
- GMUpdateEvent.UpdateDocument,
- mess.update.bind(mess),
- {
- ...renderData,
- content: updatedContent
- },
- mess.uuid
- );
- }
-
- async groupRollReroll(event, message) {
- const path = event.currentTarget.dataset.path;
- const { actor: actorData, trait } = foundry.utils.getProperty(message.system, path);
- const actor = game.actors.get(actorData._id);
-
- if (!actor.testUserPermission(game.user, 'OWNER')) {
- return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.noActorOwnership'));
- }
-
- const traitLabel = game.i18n.localize(abilities[trait].label);
-
- const config = {
- event: event,
- title: `${game.i18n.localize('DAGGERHEART.GENERAL.dualityRoll')}: ${actor.name}`,
- headerTitle: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
- ability: traitLabel
- }),
- roll: {
- trait: trait,
- advantage: 0,
- modifiers: [{ label: traitLabel, value: actor.system.traits[trait].value }]
- },
- hasRoll: true,
- skips: {
- createMessage: true,
- updateCountdowns: true
- }
- };
- const result = await actor.diceRoll({
- ...config,
- headerTitle: `${game.i18n.localize('DAGGERHEART.GENERAL.dualityRoll')}: ${actor.name}`,
- title: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
- ability: traitLabel
- })
- });
-
- const newMessageData = foundry.utils.deepClone(message.system);
- foundry.utils.setProperty(newMessageData, `${path}.result`, { ...result.roll, rerolled: true });
- const renderData = { system: new game.system.api.models.chatMessages.config.groupRoll(newMessageData) };
-
- const updatedContent = await foundry.applications.handlebars.renderTemplate(
- 'systems/daggerheart/templates/ui/chat/groupRoll.hbs',
- { ...renderData, user: game.user }
- );
- const mess = game.messages.get(message._id);
- await emitAsGM(
- GMUpdateEvent.UpdateDocument,
- mess.update.bind(mess),
- {
- ...renderData,
- content: updatedContent
- },
- mess.uuid
- );
- }
-
- async groupRollSuccessEvent(event, message) {
- if (!game.user.isGM) {
- return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.gmOnly'));
- }
-
- const { path, success } = event.currentTarget.dataset;
- const { actor: actorData } = foundry.utils.getProperty(message.system, path);
- const actor = game.actors.get(actorData._id);
-
- if (!actor.testUserPermission(game.user, 'OWNER')) {
- return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.noActorOwnership'));
- }
-
- const newMessageData = foundry.utils.deepClone(message.system);
- foundry.utils.setProperty(newMessageData, `${path}.manualSuccess`, Boolean(success));
- const renderData = { system: new game.system.api.models.chatMessages.config.groupRoll(newMessageData) };
-
- const updatedContent = await foundry.applications.handlebars.renderTemplate(
- 'systems/daggerheart/templates/ui/chat/groupRoll.hbs',
- { ...renderData, user: game.user }
- );
- const mess = game.messages.get(message._id);
- await emitAsGM(
- GMUpdateEvent.UpdateDocument,
- mess.update.bind(mess),
- {
- ...renderData,
- content: updatedContent
- },
- mess.uuid
- );
- }
-
- async groupRollExpandSection(event) {
- event.target
- .closest('.group-roll-header-expand-section')
- .querySelectorAll('i')
- .forEach(element => {
- element.classList.toggle('fa-angle-up');
- element.classList.toggle('fa-angle-down');
- });
- event.target.closest('.group-roll-section').querySelector('.group-roll-content').classList.toggle('closed');
- }
-
async riskItAllClearStressAndHitPoints(event, data) {
const resourceValue = event.target.dataset.resourceValue;
const actor = game.actors.get(event.target.dataset.actorId);
diff --git a/module/applications/ui/countdowns.mjs b/module/applications/ui/countdowns.mjs
index 0f83a05f..79a59a07 100644
--- a/module/applications/ui/countdowns.mjs
+++ b/module/applications/ui/countdowns.mjs
@@ -137,6 +137,8 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
}
static #getPlayerOwnership(user, setting, countdown) {
+ if (user.isGM) return CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER;
+
const playerOwnership = countdown.ownership[user.id];
return playerOwnership === undefined || playerOwnership === CONST.DOCUMENT_OWNERSHIP_LEVELS.INHERIT
? setting.defaultOwnership
diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs
index f3484e43..4a3d672d 100644
--- a/module/config/generalConfig.mjs
+++ b/module/config/generalConfig.mjs
@@ -70,6 +70,14 @@ export const range = {
}
};
+export const groupAttackRange = {
+ melee: range.melee,
+ veryClose: range.veryClose,
+ close: range.close,
+ far: range.far,
+ veryFar: range.veryFar
+};
+
/* circle|cone|rect|ray used to be CONST.MEASURED_TEMPLATE_TYPES. Hardcoded for now */
export const templateTypes = {
CIRCLE: 'circle',
@@ -484,7 +492,7 @@ export const defaultRestOptions = {
value: {
custom: {
enabled: true,
- formula: '@system.armorScore'
+ formula: '@system.armorScore.max'
}
}
}
@@ -708,14 +716,14 @@ const getDiceSoNiceSFX = sfxOptions => {
if (sfxOptions.critical && criticalAnimationData.class) {
return {
specialEffect: criticalAnimationData.class,
- options: {}
+ options: { ...criticalAnimationData.options }
};
}
if (sfxOptions.higher && sfxOptions.data.higher) {
return {
specialEffect: sfxOptions.data.higher.class,
- options: {}
+ options: { ...sfxOptions.data.higher.options }
};
}
diff --git a/module/config/hooksConfig.mjs b/module/config/hooksConfig.mjs
index 8d04be6d..c0930d90 100644
--- a/module/config/hooksConfig.mjs
+++ b/module/config/hooksConfig.mjs
@@ -1,5 +1,6 @@
export const hooksConfig = {
effectDisplayToggle: 'DHEffectDisplayToggle',
lockedTooltipDismissed: 'DHLockedTooltipDismissed',
- tagTeamStart: 'DHTagTeamRollStart'
+ tagTeamStart: 'DHTagTeamRollStart',
+ groupRollStart: 'DHGroupRollStart'
};
diff --git a/module/data/_module.mjs b/module/data/_module.mjs
index 0e7e295e..cd691ee1 100644
--- a/module/data/_module.mjs
+++ b/module/data/_module.mjs
@@ -4,6 +4,7 @@ export { default as DhRollTable } from './rollTable.mjs';
export { default as RegisteredTriggers } from './registeredTriggers.mjs';
export { default as CompendiumBrowserSettings } from './compendiumBrowserSettings.mjs';
export { default as TagTeamData } from './tagTeamData.mjs';
+export { default as GroupRollData } from './groupRollData.mjs';
export { default as SpotlightTracker } from './spotlightTracker.mjs';
export * as countdowns from './countdowns.mjs';
diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs
index 1f75d382..0992350b 100644
--- a/module/data/action/baseAction.mjs
+++ b/module/data/action/baseAction.mjs
@@ -280,6 +280,26 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
}
};
+ if (this.damage) {
+ config.isDirect = this.damage.direct;
+
+ const groupAttackTokens = this.damage.groupAttack
+ ? game.system.api.fields.ActionFields.DamageField.getGroupAttackTokens(
+ this.actor.id,
+ this.damage.groupAttack
+ )
+ : null;
+
+ config.damageOptions = {
+ groupAttack: this.damage.groupAttack
+ ? {
+ numAttackers: Math.max(groupAttackTokens.length, 1),
+ range: this.damage.groupAttack
+ }
+ : null
+ };
+ }
+
DHBaseAction.applyKeybindings(config);
return config;
}
diff --git a/module/data/activeEffect/changeTypes/armor.mjs b/module/data/activeEffect/changeTypes/armor.mjs
index 713ef03d..217ff9dd 100644
--- a/module/data/activeEffect/changeTypes/armor.mjs
+++ b/module/data/activeEffect/changeTypes/armor.mjs
@@ -82,7 +82,7 @@ export default class ArmorChange extends foundry.abstract.DataModel {
{
...change,
key: 'system.damageThresholds.major',
- type: CONFIG.DH.GENERAL.activeEffectModes.override.id,
+ type: CONFIG.DH.GENERAL.activeEffectModes.add.id,
priority: 50,
value: major
},
@@ -96,7 +96,7 @@ export default class ArmorChange extends foundry.abstract.DataModel {
{
...change,
key: 'system.damageThresholds.severe',
- type: CONFIG.DH.GENERAL.activeEffectModes.override.id,
+ type: CONFIG.DH.GENERAL.activeEffectModes.add.id,
priority: 50,
value: severe
},
@@ -111,6 +111,8 @@ export default class ArmorChange extends foundry.abstract.DataModel {
};
get isSuppressed() {
+ if (!this.parent.parent?.actor) return false;
+
switch (this.value.interaction) {
case CONFIG.DH.GENERAL.activeEffectArmorInteraction.active.id:
return !this.parent.parent?.actor.system.armor;
diff --git a/module/data/actor/party.mjs b/module/data/actor/party.mjs
index 2c797803..ec1beb99 100644
--- a/module/data/actor/party.mjs
+++ b/module/data/actor/party.mjs
@@ -1,6 +1,7 @@
import BaseDataActor from './base.mjs';
import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs';
import TagTeamData from '../tagTeamData.mjs';
+import GroupRollData from '../groupRollData.mjs';
export default class DhParty extends BaseDataActor {
/**@inheritdoc */
@@ -16,7 +17,8 @@ export default class DhParty extends BaseDataActor {
bags: new fields.NumberField({ initial: 0, integer: true }),
chests: new fields.NumberField({ initial: 0, integer: true })
}),
- tagTeam: new fields.EmbeddedDataField(TagTeamData)
+ tagTeam: new fields.EmbeddedDataField(TagTeamData),
+ groupRoll: new fields.EmbeddedDataField(GroupRollData)
};
}
diff --git a/module/data/chat-message/_modules.mjs b/module/data/chat-message/_modules.mjs
index c671de31..450d1ba2 100644
--- a/module/data/chat-message/_modules.mjs
+++ b/module/data/chat-message/_modules.mjs
@@ -1,6 +1,5 @@
import DHAbilityUse from './abilityUse.mjs';
import DHActorRoll from './actorRoll.mjs';
-import DHGroupRoll from './groupRoll.mjs';
import DHSystemMessage from './systemMessage.mjs';
export const config = {
@@ -9,6 +8,5 @@ export const config = {
damageRoll: DHActorRoll,
dualityRoll: DHActorRoll,
fateRoll: DHActorRoll,
- groupRoll: DHGroupRoll,
systemMessage: DHSystemMessage
};
diff --git a/module/data/chat-message/actorRoll.mjs b/module/data/chat-message/actorRoll.mjs
index 89f34949..eaa1cdc2 100644
--- a/module/data/chat-message/actorRoll.mjs
+++ b/module/data/chat-message/actorRoll.mjs
@@ -48,6 +48,7 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
action: new fields.StringField()
}),
damage: new fields.ObjectField(),
+ damageOptions: new fields.ObjectField(),
costs: new fields.ArrayField(new fields.ObjectField()),
successConsumed: new fields.BooleanField({ initial: false })
};
diff --git a/module/data/chat-message/groupRoll.mjs b/module/data/chat-message/groupRoll.mjs
deleted file mode 100644
index a5308323..00000000
--- a/module/data/chat-message/groupRoll.mjs
+++ /dev/null
@@ -1,39 +0,0 @@
-import { abilities } from '../../config/actorConfig.mjs';
-
-export default class DHGroupRoll extends foundry.abstract.TypeDataModel {
- static defineSchema() {
- const fields = foundry.data.fields;
-
- return {
- leader: new fields.EmbeddedDataField(GroupRollMemberField),
- members: new fields.ArrayField(new fields.EmbeddedDataField(GroupRollMemberField))
- };
- }
-
- get totalModifier() {
- return this.members.reduce((acc, m) => {
- if (m.manualSuccess === null) return acc;
-
- return acc + (m.manualSuccess ? 1 : -1);
- }, 0);
- }
-}
-
-class GroupRollMemberField extends foundry.abstract.DataModel {
- static defineSchema() {
- const fields = foundry.data.fields;
-
- return {
- actor: new fields.ObjectField(),
- trait: new fields.StringField({ choices: abilities }),
- difficulty: new fields.StringField(),
- result: new fields.ObjectField({ nullable: true, initial: null }),
- manualSuccess: new fields.BooleanField({ nullable: true, initial: null })
- };
- }
-
- /* Can be expanded if we handle automation of success/failure */
- get success() {
- return manualSuccess;
- }
-}
diff --git a/module/data/countdowns.mjs b/module/data/countdowns.mjs
index b944bf73..7d27197d 100644
--- a/module/data/countdowns.mjs
+++ b/module/data/countdowns.mjs
@@ -5,10 +5,6 @@ export default class DhCountdowns extends foundry.abstract.DataModel {
const fields = foundry.data.fields;
return {
- /* Outdated and unused. Needed for migration. Remove in next minor version. (1.3) */
- narrative: new fields.EmbeddedDataField(DhCountdownData),
- encounter: new fields.EmbeddedDataField(DhCountdownData),
- /**/
countdowns: new fields.TypedObjectField(new fields.EmbeddedDataField(DhCountdown)),
defaultOwnership: new fields.NumberField({
required: true,
@@ -19,122 +15,6 @@ export default class DhCountdowns extends foundry.abstract.DataModel {
}
}
-/* Outdated and unused. Needed for migration. Remove in next minor version. (1.3) */
-class DhCountdownData extends foundry.abstract.DataModel {
- static defineSchema() {
- const fields = foundry.data.fields;
- return {
- countdowns: new fields.TypedObjectField(new fields.EmbeddedDataField(DhOldCountdown)),
- ownership: new fields.SchemaField({
- default: new fields.NumberField({
- required: true,
- choices: Object.values(CONST.DOCUMENT_OWNERSHIP_LEVELS),
- initial: CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE
- }),
- players: new fields.TypedObjectField(
- new fields.SchemaField({
- type: new fields.NumberField({
- required: true,
- choices: Object.values(CONST.DOCUMENT_OWNERSHIP_LEVELS),
- initial: CONST.DOCUMENT_OWNERSHIP_LEVELS.INHERIT
- })
- })
- )
- }),
- window: new fields.SchemaField({})
- };
- }
-
- get playerOwnership() {
- return Array.from(game.users).reduce((acc, user) => {
- acc[user.id] = {
- value: user.isGM
- ? CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER
- : this.ownership.players[user.id] && this.ownership.players[user.id].type !== -1
- ? this.ownership.players[user.id].type
- : this.ownership.default,
- isGM: user.isGM
- };
-
- return acc;
- }, {});
- }
-}
-
-/* Outdated and unused. Needed for migration. Remove in next minor version. (1.3) */
-class DhOldCountdown extends foundry.abstract.DataModel {
- static defineSchema() {
- const fields = foundry.data.fields;
- return {
- name: new fields.StringField({
- required: true,
- label: 'DAGGERHEART.APPLICATIONS.Countdown.FIELDS.countdowns.element.name.label'
- }),
- img: new fields.FilePathField({
- categories: ['IMAGE'],
- base64: false,
- initial: 'icons/magic/time/hourglass-yellow-green.webp'
- }),
- ownership: new fields.SchemaField({
- default: new fields.NumberField({
- required: true,
- choices: Object.values(CONST.DOCUMENT_OWNERSHIP_LEVELS),
- initial: CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE
- }),
- players: new fields.TypedObjectField(
- new fields.SchemaField({
- type: new fields.NumberField({
- required: true,
- choices: Object.values(CONST.DOCUMENT_OWNERSHIP_LEVELS),
- initial: CONST.DOCUMENT_OWNERSHIP_LEVELS.INHERIT
- })
- })
- )
- }),
- progress: new fields.SchemaField({
- current: new fields.NumberField({
- required: true,
- integer: true,
- initial: 1,
- label: 'DAGGERHEART.APPLICATIONS.Countdown.FIELDS.countdowns.element.progress.current.label'
- }),
- max: new fields.NumberField({
- required: true,
- integer: true,
- initial: 1,
- label: 'DAGGERHEART.APPLICATIONS.Countdown.FIELDS.countdowns.element.progress.max.label'
- }),
- type: new fields.SchemaField({
- value: new fields.StringField({
- required: true,
- choices: CONFIG.DH.GENERAL.countdownProgressionTypes,
- initial: CONFIG.DH.GENERAL.countdownProgressionTypes.custom.id,
- label: 'DAGGERHEART.GENERAL.type'
- }),
- label: new fields.StringField({
- label: 'DAGGERHEART.APPLICATIONS.Countdown.FIELDS.countdowns.element.progress.type.label.label'
- })
- })
- })
- };
- }
-
- get playerOwnership() {
- return Array.from(game.users).reduce((acc, user) => {
- acc[user.id] = {
- value: user.isGM
- ? CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER
- : this.ownership.players[user.id] && this.ownership.players[user.id].type !== -1
- ? this.ownership.players[user.id].type
- : this.ownership.default,
- isGM: user.isGM
- };
-
- return acc;
- }, {});
- }
-}
-
export class DhCountdown extends foundry.abstract.DataModel {
static defineSchema() {
const fields = foundry.data.fields;
diff --git a/module/data/fields/action/countdownField.mjs b/module/data/fields/action/countdownField.mjs
index f49e71ad..719ca749 100644
--- a/module/data/fields/action/countdownField.mjs
+++ b/module/data/fields/action/countdownField.mjs
@@ -57,6 +57,10 @@ export default class CountdownField extends fields.ArrayField {
data.countdowns[foundry.utils.randomID()] = {
...countdown,
+ ownership: game.users.reduce((acc, curr) => {
+ if (!curr.isGM) acc[curr.id] = countdown.defaultOwnership;
+ return acc;
+ }, {}),
progress: {
...countdown.progress,
current: countdownStart,
diff --git a/module/data/fields/action/damageField.mjs b/module/data/fields/action/damageField.mjs
index 5d40a470..7839bf5a 100644
--- a/module/data/fields/action/damageField.mjs
+++ b/module/data/fields/action/damageField.mjs
@@ -18,7 +18,12 @@ export default class DamageField extends fields.SchemaField {
initial: false,
label: 'DAGGERHEART.ACTIONS.Settings.includeBase.label'
}),
- direct: new fields.BooleanField({ initial: false, label: 'DAGGERHEART.CONFIG.DamageType.direct.name' })
+ direct: new fields.BooleanField({ initial: false, label: 'DAGGERHEART.CONFIG.DamageType.direct.name' }),
+ groupAttack: new fields.StringField({
+ choices: CONFIG.DH.GENERAL.groupAttackRange,
+ blank: true,
+ label: 'DAGGERHEART.ACTIONS.Settings.groupAttack.label'
+ })
};
super(damageFields, options, context);
}
@@ -224,6 +229,22 @@ export default class DamageField extends fields.SchemaField {
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damageApply.players)
);
}
+
+ static getGroupAttackTokens(actorId, range) {
+ if (!canvas.scene) return [];
+
+ const targets = Array.from(game.user.targets);
+ const rangeSettings = canvas.scene?.rangeSettings;
+ if (!rangeSettings) return [];
+
+ const maxDistance = rangeSettings[range];
+ return canvas.scene.tokens.filter(x => {
+ if (x.actor?.id !== actorId) return false;
+ if (targets.every(target => x.object.distanceTo(target) > maxDistance)) return false;
+
+ return true;
+ });
+ }
}
export class DHActionDiceData extends foundry.abstract.DataModel {
diff --git a/module/data/fields/actorField.mjs b/module/data/fields/actorField.mjs
index 7a57aa46..ae6f060c 100644
--- a/module/data/fields/actorField.mjs
+++ b/module/data/fields/actorField.mjs
@@ -89,13 +89,13 @@ class ResourcesField extends fields.TypedObjectField {
*/
_getField(path) {
if (path.length === 0) return this;
- const first = path.shift();
- if (first === this.element.name) return this.element_getField(path);
+ const name = path.pop();
+ if (name === this.element.name) return this.element_getField(path);
const resources = CONFIG.DH.RESOURCE[this.actorType].all;
- if (first in resources) {
+ if (name in resources) {
const field = this.element._getField(path);
- field.label = resources[first].label;
+ field.label = resources[name].label;
return field;
}
diff --git a/module/data/groupRollData.mjs b/module/data/groupRollData.mjs
new file mode 100644
index 00000000..78a06b13
--- /dev/null
+++ b/module/data/groupRollData.mjs
@@ -0,0 +1,40 @@
+export default class GroupRollData extends foundry.abstract.DataModel {
+ static defineSchema() {
+ const fields = foundry.data.fields;
+
+ return {
+ leader: new fields.EmbeddedDataField(CharacterData, { nullable: true, initial: null }),
+ aidingCharacters: new fields.TypedObjectField(new fields.EmbeddedDataField(CharacterData))
+ };
+ }
+
+ get participants() {
+ return {
+ ...(this.leader ? { [this.leader.id]: this.leader } : {}),
+ ...this.aidingCharacters
+ };
+ }
+}
+
+export class CharacterData extends foundry.abstract.DataModel {
+ static defineSchema() {
+ const fields = foundry.data.fields;
+
+ return {
+ id: new fields.StringField({ required: true }),
+ name: new fields.StringField({ required: true }),
+ img: new fields.StringField({ required: true }),
+ rollChoice: new fields.StringField({
+ choices: CONFIG.DH.ACTOR.abilities,
+ initial: CONFIG.DH.ACTOR.abilities.agility.id
+ }),
+ rollData: new fields.JSONField({ nullable: true, initial: null }),
+ selected: new fields.BooleanField({ initial: false }),
+ successfull: new fields.BooleanField({ nullable: true, initial: null })
+ };
+ }
+
+ get roll() {
+ return this.rollData ? CONFIG.Dice.daggerheart.DualityRoll.fromData(this.rollData) : null;
+ }
+}
diff --git a/module/data/settings/Appearance.mjs b/module/data/settings/Appearance.mjs
index 9da3afac..4db27be0 100644
--- a/module/data/settings/Appearance.mjs
+++ b/module/data/settings/Appearance.mjs
@@ -8,6 +8,9 @@ export default class DhAppearance extends foundry.abstract.DataModel {
initial: null,
blank: true,
choices: CONFIG.DH.GENERAL.diceSoNiceSFXClasses
+ }),
+ options: new foundry.data.fields.SchemaField({
+ muteSound: new foundry.data.fields.BooleanField()
})
});
diff --git a/module/dice/damageRoll.mjs b/module/dice/damageRoll.mjs
index 1d680a1b..98fd8401 100644
--- a/module/dice/damageRoll.mjs
+++ b/module/dice/damageRoll.mjs
@@ -144,6 +144,7 @@ export default class DamageRoll extends DHRoll {
constructFormula(config) {
this.options.isCritical = config.isCritical;
for (const [index, part] of this.options.roll.entries()) {
+ const isHitpointPart = part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id;
part.roll = new Roll(Roll.replaceFormulaData(part.formula, config.data));
part.roll.terms = Roll.parse(part.roll.formula, config.data);
if (part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) {
@@ -169,7 +170,16 @@ export default class DamageRoll extends DHRoll {
);
}
- if (config.isCritical && part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) {
+ if (config.damageOptions.groupAttack?.numAttackers > 1 && isHitpointPart) {
+ const damageTypes = [foundry.dice.terms.Die, foundry.dice.terms.NumericTerm];
+ for (const term of part.roll.terms) {
+ if (damageTypes.some(type => term instanceof type)) {
+ term.number *= config.damageOptions.groupAttack.numAttackers;
+ }
+ }
+ }
+
+ if (config.isCritical && isHitpointPart) {
const total = part.roll.dice.reduce((acc, term) => acc + term._faces * term._number, 0);
if (total > 0) {
part.roll.terms.push(...this.formatModifier(total));
diff --git a/module/documents/scene.mjs b/module/documents/scene.mjs
index 1c2faa34..59b8091f 100644
--- a/module/documents/scene.mjs
+++ b/module/documents/scene.mjs
@@ -1,6 +1,16 @@
import DHToken from './token.mjs';
export default class DhScene extends Scene {
+ get rangeSettings() {
+ const { custom } = CONFIG.DH.GENERAL.sceneRangeMeasurementSetting;
+ const sceneMeasurements = this.flags.daggerheart?.rangeMeasurement;
+ const globalMeasurements = game.settings.get(
+ CONFIG.DH.id,
+ CONFIG.DH.SETTINGS.gameSettings.variantRules
+ ).rangeMeasurement;
+ return sceneMeasurements?.setting === custom.id ? sceneMeasurements : globalMeasurements;
+ }
+
/** A map of `TokenDocument` IDs embedded in this scene long with new dimensions from actor size-category changes */
#sizeSyncBatch = new Map();
diff --git a/module/enrichers/TemplateEnricher.mjs b/module/enrichers/TemplateEnricher.mjs
index 1a075518..cd0e7d9c 100644
--- a/module/enrichers/TemplateEnricher.mjs
+++ b/module/enrichers/TemplateEnricher.mjs
@@ -118,13 +118,6 @@ const getTemplateDistance = range => {
const rangeNumber = Number(range);
if (!Number.isNaN(rangeNumber)) return rangeNumber;
- const { custom } = CONFIG.DH.GENERAL.sceneRangeMeasurementSetting;
- const sceneMeasurements = canvas.scene?.flags.daggerheart?.rangeMeasurement;
- const globalMeasurements = game.settings.get(
- CONFIG.DH.id,
- CONFIG.DH.SETTINGS.gameSettings.variantRules
- ).rangeMeasurement;
-
- const settings = sceneMeasurements?.setting === custom.id ? sceneMeasurements : globalMeasurements;
- return settings[range];
+ const settings = canvas.scene?.rangeSettings;
+ return settings ? settings[range] : 0;
};
diff --git a/module/systemRegistration/migrations.mjs b/module/systemRegistration/migrations.mjs
index 458ee6ef..b4c446b2 100644
--- a/module/systemRegistration/migrations.mjs
+++ b/module/systemRegistration/migrations.mjs
@@ -1,3 +1,4 @@
+import { defaultRestOptions } from '../config/generalConfig.mjs';
import { RefreshType, socketEvent } from './socket.mjs';
export async function runMigrations() {
@@ -341,6 +342,18 @@ export async function runMigrations() {
lastMigrationVersion = '2.0.0';
}
+
+ if (foundry.utils.isNewerVersion('2.1.0', lastMigrationVersion)) {
+ const downtimeMoves = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew);
+ if (downtimeMoves.restMoves.longRest.moves.repairArmor) {
+ await downtimeMoves.updateSource({
+ 'restMoves.longRest.moves.repairArmor': defaultRestOptions.longRest().repairArmor
+ });
+ game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, downtimeMoves.toObject());
+ }
+
+ lastMigrationVersion = '2.1.0';
+ }
//#endregion
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion, lastMigrationVersion);
diff --git a/module/systemRegistration/socket.mjs b/module/systemRegistration/socket.mjs
index fb152959..8fed346d 100644
--- a/module/systemRegistration/socket.mjs
+++ b/module/systemRegistration/socket.mjs
@@ -18,6 +18,8 @@ export function handleSocketEvent({ action = null, data = {} } = {}) {
case socketEvent.TagTeamStart:
Hooks.callAll(CONFIG.DH.HOOKS.hooksConfig.tagTeamStart, data);
break;
+ case socketEvent.GroupRollStart:
+ Hooks.callAll(CONFIG.DH.HOOKS.hooksConfig.groupRollStart, data);
}
}
@@ -26,7 +28,8 @@ export const socketEvent = {
Refresh: 'DhRefresh',
DhpFearUpdate: 'DhFearUpdate',
DowntimeTrigger: 'DowntimeTrigger',
- TagTeamStart: 'DhTagTeamStart'
+ TagTeamStart: 'DhTagTeamStart',
+ GroupRollStart: 'DhGroupRollStart'
};
export const GMUpdateEvent = {
@@ -41,6 +44,7 @@ export const GMUpdateEvent = {
export const RefreshType = {
Countdown: 'DhCoundownRefresh',
TagTeamRoll: 'DhTagTeamRollRefresh',
+ GroupRoll: 'DhGroupRollRefresh',
EffectsDisplay: 'DhEffectsDisplayRefresh',
Scene: 'DhSceneRefresh',
CompendiumBrowser: 'DhCompendiumBrowserRefresh'
diff --git a/src/packs/adversaries/adversary_Apprentice_Assassin_vNIbYQ4YSzNf0WPE.json b/src/packs/adversaries/adversary_Apprentice_Assassin_vNIbYQ4YSzNf0WPE.json
index 4c63297d..12ec35ae 100644
--- a/src/packs/adversaries/adversary_Apprentice_Assassin_vNIbYQ4YSzNf0WPE.json
+++ b/src/packs/adversaries/adversary_Apprentice_Assassin_vNIbYQ4YSzNf0WPE.json
@@ -131,12 +131,9 @@
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
"anchorX": 0.5,
"anchorY": 0.5,
- "offsetX": 0,
- "offsetY": 0,
"fit": "contain",
"scaleX": 1,
"scaleY": 1,
- "rotation": 0,
"tint": "#ffffff",
"alphaThreshold": 0.75
},
@@ -187,7 +184,7 @@
"saturation": 0,
"contrast": 0
},
- "detectionModes": [],
+ "detectionModes": {},
"occludable": {
"radius": 0
},
@@ -213,7 +210,8 @@
"flags": {},
"randomImg": false,
"appendNumber": false,
- "prependAdjective": false
+ "prependAdjective": false,
+ "depth": 1
},
"items": [
{
@@ -249,33 +247,95 @@
"description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
",
"resource": null,
"actions": {
- "vgguNWz8vG8aoLXR": {
- "type": "effect",
- "_id": "vgguNWz8vG8aoLXR",
+ "SrNyZgPvCXMpbCLG": {
+ "type": "attack",
+ "_id": "SrNyZgPvCXMpbCLG",
"systemPath": "actions",
+ "baseAction": false,
"description": "",
"chatDisplay": true,
+ "originItem": {
+ "type": "itemCollection"
+ },
"actionType": "action",
+ "triggers": [],
"cost": [
{
"scalable": false,
"key": "fear",
"value": 1,
- "step": null
+ "itemId": null,
+ "step": null,
+ "consumeOnSuccess": false
}
],
"uses": {
"value": null,
"max": "",
- "recovery": null
+ "recovery": null,
+ "consumeOnSuccess": false
+ },
+ "damage": {
+ "parts": {
+ "hitPoints": {
+ "applyTo": "hitPoints",
+ "resultBased": false,
+ "value": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": true,
+ "formula": "4"
+ }
+ },
+ "valueAlt": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": false,
+ "formula": ""
+ }
+ },
+ "base": false,
+ "type": [
+ "physical"
+ ]
+ }
+ },
+ "includeBase": false,
+ "direct": false,
+ "groupAttack": "close"
},
- "effects": [],
"target": {
"type": "any",
"amount": null
},
+ "effects": [],
+ "roll": {
+ "type": "attack",
+ "trait": null,
+ "difficulty": null,
+ "bonus": null,
+ "advState": "neutral",
+ "diceRolling": {
+ "multiplier": "prof",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "compare": null,
+ "treshold": null
+ },
+ "useDefault": false
+ },
+ "save": {
+ "trait": null,
+ "difficulty": null,
+ "damageMod": "none"
+ },
"name": "Spend Fear",
- "img": "icons/magic/unholy/orb-hands-pink.webp",
"range": ""
}
},
diff --git a/src/packs/adversaries/adversary_Conscript_99TqczuQipBmaB8i.json b/src/packs/adversaries/adversary_Conscript_99TqczuQipBmaB8i.json
index 5cbc1f82..38e7ceab 100644
--- a/src/packs/adversaries/adversary_Conscript_99TqczuQipBmaB8i.json
+++ b/src/packs/adversaries/adversary_Conscript_99TqczuQipBmaB8i.json
@@ -125,12 +125,9 @@
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
"anchorX": 0.5,
"anchorY": 0.5,
- "offsetX": 0,
- "offsetY": 0,
"fit": "contain",
"scaleX": 1,
"scaleY": 1,
- "rotation": 0,
"tint": "#ffffff",
"alphaThreshold": 0.75
},
@@ -181,7 +178,7 @@
"saturation": 0,
"contrast": 0
},
- "detectionModes": [],
+ "detectionModes": {},
"occludable": {
"radius": 0
},
@@ -207,7 +204,8 @@
"flags": {},
"randomImg": false,
"appendNumber": false,
- "prependAdjective": false
+ "prependAdjective": false,
+ "depth": 1
},
"items": [
{
@@ -242,33 +240,95 @@
"description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
",
"resource": null,
"actions": {
- "cbAvPSIhwBMBTI3D": {
- "type": "effect",
- "_id": "cbAvPSIhwBMBTI3D",
+ "FCeTuf71gCzRiO5N": {
+ "type": "attack",
+ "_id": "FCeTuf71gCzRiO5N",
"systemPath": "actions",
+ "baseAction": false,
"description": "",
"chatDisplay": true,
+ "originItem": {
+ "type": "itemCollection"
+ },
"actionType": "action",
+ "triggers": [],
"cost": [
{
"scalable": false,
"key": "fear",
"value": 1,
- "step": null
+ "itemId": null,
+ "step": null,
+ "consumeOnSuccess": false
}
],
"uses": {
"value": null,
"max": "",
- "recovery": null
+ "recovery": null,
+ "consumeOnSuccess": false
+ },
+ "damage": {
+ "parts": {
+ "hitPoints": {
+ "applyTo": "hitPoints",
+ "resultBased": false,
+ "value": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": true,
+ "formula": "6"
+ }
+ },
+ "valueAlt": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": false,
+ "formula": ""
+ }
+ },
+ "base": false,
+ "type": [
+ "physical"
+ ]
+ }
+ },
+ "includeBase": false,
+ "direct": false,
+ "groupAttack": "close"
},
- "effects": [],
"target": {
"type": "any",
"amount": null
},
+ "effects": [],
+ "roll": {
+ "type": "attack",
+ "trait": null,
+ "difficulty": null,
+ "bonus": null,
+ "advState": "neutral",
+ "diceRolling": {
+ "multiplier": "prof",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "compare": null,
+ "treshold": null
+ },
+ "useDefault": false
+ },
+ "save": {
+ "trait": null,
+ "difficulty": null,
+ "damageMod": "none"
+ },
"name": "Spend Fear",
- "img": "icons/creatures/abilities/tail-strike-bone-orange.webp",
"range": ""
}
},
diff --git a/src/packs/adversaries/adversary_Cult_Initiate_zx99sOGTXicP4SSD.json b/src/packs/adversaries/adversary_Cult_Initiate_zx99sOGTXicP4SSD.json
index 4f04a85a..db26605d 100644
--- a/src/packs/adversaries/adversary_Cult_Initiate_zx99sOGTXicP4SSD.json
+++ b/src/packs/adversaries/adversary_Cult_Initiate_zx99sOGTXicP4SSD.json
@@ -125,12 +125,9 @@
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
"anchorX": 0.5,
"anchorY": 0.5,
- "offsetX": 0,
- "offsetY": 0,
"fit": "contain",
"scaleX": 1,
"scaleY": 1,
- "rotation": 0,
"tint": "#ffffff",
"alphaThreshold": 0.75
},
@@ -181,7 +178,7 @@
"saturation": 0,
"contrast": 0
},
- "detectionModes": [],
+ "detectionModes": {},
"occludable": {
"radius": 0
},
@@ -207,7 +204,8 @@
"flags": {},
"randomImg": false,
"appendNumber": false,
- "prependAdjective": false
+ "prependAdjective": false,
+ "depth": 1
},
"items": [
{
@@ -242,33 +240,95 @@
"description": "Spend a Fear to choose a target and spotlight all Cult @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
",
"resource": null,
"actions": {
- "EH1preaTWBD4rOvx": {
- "type": "effect",
- "_id": "EH1preaTWBD4rOvx",
+ "4M2MvVzEgIQEQHBS": {
+ "type": "attack",
+ "_id": "4M2MvVzEgIQEQHBS",
"systemPath": "actions",
+ "baseAction": false,
"description": "",
"chatDisplay": true,
+ "originItem": {
+ "type": "itemCollection"
+ },
"actionType": "action",
+ "triggers": [],
"cost": [
{
"scalable": false,
"key": "fear",
"value": 1,
- "step": null
+ "itemId": null,
+ "step": null,
+ "consumeOnSuccess": false
}
],
"uses": {
"value": null,
"max": "",
- "recovery": null
+ "recovery": null,
+ "consumeOnSuccess": false
+ },
+ "damage": {
+ "parts": {
+ "hitPoints": {
+ "applyTo": "hitPoints",
+ "resultBased": false,
+ "value": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": true,
+ "formula": "5"
+ }
+ },
+ "valueAlt": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": false,
+ "formula": ""
+ }
+ },
+ "base": false,
+ "type": [
+ "physical"
+ ]
+ }
+ },
+ "includeBase": false,
+ "direct": false,
+ "groupAttack": "close"
},
- "effects": [],
"target": {
- "type": "self",
+ "type": "any",
"amount": null
},
+ "effects": [],
+ "roll": {
+ "type": null,
+ "trait": null,
+ "difficulty": null,
+ "bonus": null,
+ "advState": "neutral",
+ "diceRolling": {
+ "multiplier": "prof",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "compare": null,
+ "treshold": null
+ },
+ "useDefault": false
+ },
+ "save": {
+ "trait": null,
+ "difficulty": null,
+ "damageMod": "none"
+ },
"name": "Spend Fear",
- "img": "icons/creatures/abilities/tail-strike-bone-orange.webp",
"range": ""
}
},
diff --git a/src/packs/adversaries/adversary_Elemental_Spark_P7h54ZePFPHpYwvB.json b/src/packs/adversaries/adversary_Elemental_Spark_P7h54ZePFPHpYwvB.json
index 2c2633ea..5b8fa7b2 100644
--- a/src/packs/adversaries/adversary_Elemental_Spark_P7h54ZePFPHpYwvB.json
+++ b/src/packs/adversaries/adversary_Elemental_Spark_P7h54ZePFPHpYwvB.json
@@ -125,12 +125,9 @@
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
"anchorX": 0.5,
"anchorY": 0.5,
- "offsetX": 0,
- "offsetY": 0,
"fit": "contain",
"scaleX": 1,
"scaleY": 1,
- "rotation": 0,
"tint": "#ffffff",
"alphaThreshold": 0.75
},
@@ -181,7 +178,7 @@
"saturation": 0,
"contrast": 0
},
- "detectionModes": [],
+ "detectionModes": {},
"occludable": {
"radius": 0
},
@@ -207,7 +204,8 @@
"flags": {},
"randomImg": false,
"appendNumber": false,
- "prependAdjective": false
+ "prependAdjective": false,
+ "depth": 1
},
"items": [
{
@@ -242,33 +240,95 @@
"description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
",
"resource": null,
"actions": {
- "vXHZVb0Y7Hqu3uso": {
- "type": "effect",
- "_id": "vXHZVb0Y7Hqu3uso",
+ "S3dYxRclyhYINRi8": {
+ "type": "attack",
+ "_id": "S3dYxRclyhYINRi8",
"systemPath": "actions",
+ "baseAction": false,
"description": "",
"chatDisplay": true,
+ "originItem": {
+ "type": "itemCollection"
+ },
"actionType": "action",
+ "triggers": [],
"cost": [
{
"scalable": false,
"key": "fear",
"value": 1,
- "step": null
+ "itemId": null,
+ "step": null,
+ "consumeOnSuccess": false
}
],
"uses": {
"value": null,
"max": "",
- "recovery": null
+ "recovery": null,
+ "consumeOnSuccess": false
+ },
+ "damage": {
+ "parts": {
+ "hitPoints": {
+ "applyTo": "hitPoints",
+ "resultBased": false,
+ "value": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": true,
+ "formula": "5"
+ }
+ },
+ "valueAlt": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": false,
+ "formula": ""
+ }
+ },
+ "base": false,
+ "type": [
+ "physical"
+ ]
+ }
+ },
+ "includeBase": false,
+ "direct": false,
+ "groupAttack": "close"
},
- "effects": [],
"target": {
- "type": "self",
+ "type": "any",
"amount": null
},
+ "effects": [],
+ "roll": {
+ "type": "attack",
+ "trait": null,
+ "difficulty": null,
+ "bonus": null,
+ "advState": "neutral",
+ "diceRolling": {
+ "multiplier": "prof",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "compare": null,
+ "treshold": null
+ },
+ "useDefault": false
+ },
+ "save": {
+ "trait": null,
+ "difficulty": null,
+ "damageMod": "none"
+ },
"name": "Spend Fear",
- "img": "icons/magic/unholy/orb-hands-pink.webp",
"range": ""
}
},
diff --git a/src/packs/adversaries/adversary_Fallen_Shock_Troop_OsLG2BjaEdTZUJU9.json b/src/packs/adversaries/adversary_Fallen_Shock_Troop_OsLG2BjaEdTZUJU9.json
index 8c0d7b95..484e161a 100644
--- a/src/packs/adversaries/adversary_Fallen_Shock_Troop_OsLG2BjaEdTZUJU9.json
+++ b/src/packs/adversaries/adversary_Fallen_Shock_Troop_OsLG2BjaEdTZUJU9.json
@@ -125,12 +125,9 @@
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
"anchorX": 0.5,
"anchorY": 0.5,
- "offsetX": 0,
- "offsetY": 0,
"fit": "contain",
"scaleX": 1,
"scaleY": 1,
- "rotation": 0,
"tint": "#ffffff",
"alphaThreshold": 0.75
},
@@ -181,7 +178,7 @@
"saturation": 0,
"contrast": 0
},
- "detectionModes": [],
+ "detectionModes": {},
"occludable": {
"radius": 0
},
@@ -207,7 +204,8 @@
"flags": {},
"randomImg": false,
"appendNumber": false,
- "prependAdjective": false
+ "prependAdjective": false,
+ "depth": 1
},
"items": [
{
@@ -320,33 +318,95 @@
"description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
",
"resource": null,
"actions": {
- "QHNRSEQmqOcaoXq4": {
- "type": "effect",
- "_id": "QHNRSEQmqOcaoXq4",
+ "G0DVft7h55pBnwJA": {
+ "type": "attack",
+ "_id": "G0DVft7h55pBnwJA",
"systemPath": "actions",
+ "baseAction": false,
"description": "",
"chatDisplay": true,
+ "originItem": {
+ "type": "itemCollection"
+ },
"actionType": "action",
+ "triggers": [],
"cost": [
{
"scalable": false,
"key": "fear",
"value": 1,
- "step": null
+ "itemId": null,
+ "step": null,
+ "consumeOnSuccess": false
}
],
"uses": {
"value": null,
"max": "",
- "recovery": null
+ "recovery": null,
+ "consumeOnSuccess": false
+ },
+ "damage": {
+ "parts": {
+ "hitPoints": {
+ "applyTo": "hitPoints",
+ "resultBased": false,
+ "value": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": true,
+ "formula": "12"
+ }
+ },
+ "valueAlt": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": false,
+ "formula": ""
+ }
+ },
+ "base": false,
+ "type": [
+ "physical"
+ ]
+ }
+ },
+ "includeBase": false,
+ "direct": false,
+ "groupAttack": "close"
},
- "effects": [],
"target": {
- "type": "self",
+ "type": "any",
"amount": null
},
+ "effects": [],
+ "roll": {
+ "type": "attack",
+ "trait": null,
+ "difficulty": null,
+ "bonus": null,
+ "advState": "neutral",
+ "diceRolling": {
+ "multiplier": "prof",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "compare": null,
+ "treshold": null
+ },
+ "useDefault": false
+ },
+ "save": {
+ "trait": null,
+ "difficulty": null,
+ "damageMod": "none"
+ },
"name": "Spend Fear",
- "img": "icons/magic/unholy/orb-hands-pink.webp",
"range": ""
}
},
diff --git a/src/packs/adversaries/adversary_Giant_Rat_4PfLnaCrOcMdb4dK.json b/src/packs/adversaries/adversary_Giant_Rat_4PfLnaCrOcMdb4dK.json
index 822ee035..746806d9 100644
--- a/src/packs/adversaries/adversary_Giant_Rat_4PfLnaCrOcMdb4dK.json
+++ b/src/packs/adversaries/adversary_Giant_Rat_4PfLnaCrOcMdb4dK.json
@@ -131,12 +131,9 @@
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
"anchorX": 0.5,
"anchorY": 0.5,
- "offsetX": 0,
- "offsetY": 0,
"fit": "contain",
"scaleX": 1,
"scaleY": 1,
- "rotation": 0,
"tint": "#ffffff",
"alphaThreshold": 0.75
},
@@ -187,7 +184,7 @@
"saturation": 0,
"contrast": 0
},
- "detectionModes": [],
+ "detectionModes": {},
"occludable": {
"radius": 0
},
@@ -213,7 +210,8 @@
"flags": {},
"randomImg": false,
"appendNumber": false,
- "prependAdjective": false
+ "prependAdjective": false,
+ "depth": 1
},
"items": [
{
@@ -272,8 +270,38 @@
"recovery": null
},
"damage": {
- "parts": {},
- "includeBase": false
+ "parts": {
+ "hitPoints": {
+ "applyTo": "hitPoints",
+ "resultBased": false,
+ "value": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": true,
+ "formula": "1"
+ }
+ },
+ "valueAlt": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": false,
+ "formula": ""
+ }
+ },
+ "base": false,
+ "type": [
+ "physical"
+ ]
+ }
+ },
+ "includeBase": false,
+ "groupAttack": "close"
},
"target": {
"type": "any",
@@ -300,7 +328,7 @@
"difficulty": null,
"damageMod": "none"
},
- "name": "Attack",
+ "name": "Spend Fear",
"img": "icons/creatures/abilities/tail-strike-bone-orange.webp",
"range": ""
}
diff --git a/src/packs/adversaries/adversary_Giant_Recruit_5s8wSvpyC5rxY5aD.json b/src/packs/adversaries/adversary_Giant_Recruit_5s8wSvpyC5rxY5aD.json
index 376ebace..6611496f 100644
--- a/src/packs/adversaries/adversary_Giant_Recruit_5s8wSvpyC5rxY5aD.json
+++ b/src/packs/adversaries/adversary_Giant_Recruit_5s8wSvpyC5rxY5aD.json
@@ -125,12 +125,9 @@
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
"anchorX": 0.5,
"anchorY": 0.5,
- "offsetX": 0,
- "offsetY": 0,
"fit": "contain",
"scaleX": 1,
"scaleY": 1,
- "rotation": 0,
"tint": "#ffffff",
"alphaThreshold": 0.75
},
@@ -181,7 +178,7 @@
"saturation": 0,
"contrast": 0
},
- "detectionModes": [],
+ "detectionModes": {},
"occludable": {
"radius": 0
},
@@ -207,7 +204,8 @@
"flags": {},
"randomImg": false,
"appendNumber": false,
- "prependAdjective": false
+ "prependAdjective": false,
+ "depth": 1
},
"items": [
{
@@ -242,35 +240,95 @@
"description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
",
"resource": null,
"actions": {
- "DjbPQowW1OdBD9Zn": {
- "type": "effect",
- "_id": "DjbPQowW1OdBD9Zn",
+ "wez1xgy9vScux9wi": {
+ "type": "attack",
+ "_id": "wez1xgy9vScux9wi",
"systemPath": "actions",
+ "baseAction": false,
"description": "",
"chatDisplay": true,
+ "originItem": {
+ "type": "itemCollection"
+ },
"actionType": "action",
+ "triggers": [],
"cost": [
{
+ "consumeOnSuccess": false,
"scalable": false,
"key": "fear",
"value": 1,
- "keyIsID": false,
- "step": null,
- "consumeOnSuccess": false
+ "itemId": null,
+ "step": null
}
],
"uses": {
"value": null,
"max": "",
- "recovery": null
+ "recovery": null,
+ "consumeOnSuccess": false
+ },
+ "damage": {
+ "parts": {
+ "hitPoints": {
+ "applyTo": "hitPoints",
+ "resultBased": false,
+ "value": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": true,
+ "formula": "5"
+ }
+ },
+ "valueAlt": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": false,
+ "formula": ""
+ }
+ },
+ "base": false,
+ "type": [
+ "physical"
+ ]
+ }
+ },
+ "includeBase": false,
+ "direct": false,
+ "groupAttack": "close"
},
- "effects": [],
"target": {
- "type": "self",
+ "type": "any",
"amount": null
},
+ "effects": [],
+ "roll": {
+ "type": "attack",
+ "trait": null,
+ "difficulty": null,
+ "bonus": null,
+ "advState": "neutral",
+ "diceRolling": {
+ "multiplier": "prof",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "compare": null,
+ "treshold": null
+ },
+ "useDefault": false
+ },
+ "save": {
+ "trait": null,
+ "difficulty": null,
+ "damageMod": "none"
+ },
"name": "Spend Fear",
- "img": "icons/creatures/abilities/tail-strike-bone-orange.webp",
"range": ""
}
},
diff --git a/src/packs/adversaries/adversary_Hallowed_Soldier_VENwg7xEFcYObjmT.json b/src/packs/adversaries/adversary_Hallowed_Soldier_VENwg7xEFcYObjmT.json
index 6a131c86..3e1ab854 100644
--- a/src/packs/adversaries/adversary_Hallowed_Soldier_VENwg7xEFcYObjmT.json
+++ b/src/packs/adversaries/adversary_Hallowed_Soldier_VENwg7xEFcYObjmT.json
@@ -125,12 +125,9 @@
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
"anchorX": 0.5,
"anchorY": 0.5,
- "offsetX": 0,
- "offsetY": 0,
"fit": "contain",
"scaleX": 1,
"scaleY": 1,
- "rotation": 0,
"tint": "#ffffff",
"alphaThreshold": 0.75
},
@@ -181,7 +178,7 @@
"saturation": 0,
"contrast": 0
},
- "detectionModes": [],
+ "detectionModes": {},
"occludable": {
"radius": 0
},
@@ -207,7 +204,8 @@
"flags": {},
"randomImg": false,
"appendNumber": false,
- "prependAdjective": false
+ "prependAdjective": false,
+ "depth": 1
},
"items": [
{
@@ -297,33 +295,95 @@
"description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
",
"resource": null,
"actions": {
- "eo7J0v1B5zPHul1M": {
- "type": "effect",
- "_id": "eo7J0v1B5zPHul1M",
+ "irZGPKPpGLA6sP2y": {
+ "type": "attack",
+ "_id": "irZGPKPpGLA6sP2y",
"systemPath": "actions",
+ "baseAction": false,
"description": "",
"chatDisplay": true,
+ "originItem": {
+ "type": "itemCollection"
+ },
"actionType": "action",
+ "triggers": [],
"cost": [
{
"scalable": false,
"key": "fear",
"value": 1,
- "step": null
+ "itemId": null,
+ "step": null,
+ "consumeOnSuccess": false
}
],
"uses": {
"value": null,
"max": "",
- "recovery": null
+ "recovery": null,
+ "consumeOnSuccess": false
+ },
+ "damage": {
+ "parts": {
+ "hitPoints": {
+ "applyTo": "hitPoints",
+ "resultBased": false,
+ "value": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": true,
+ "formula": "10"
+ }
+ },
+ "valueAlt": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": false,
+ "formula": ""
+ }
+ },
+ "base": false,
+ "type": [
+ "physical"
+ ]
+ }
+ },
+ "includeBase": false,
+ "direct": false,
+ "groupAttack": "close"
},
- "effects": [],
"target": {
- "type": "self",
+ "type": "any",
"amount": null
},
+ "effects": [],
+ "roll": {
+ "type": "attack",
+ "trait": null,
+ "difficulty": null,
+ "bonus": null,
+ "advState": "neutral",
+ "diceRolling": {
+ "multiplier": "prof",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "compare": null,
+ "treshold": null
+ },
+ "useDefault": false
+ },
+ "save": {
+ "trait": null,
+ "difficulty": null,
+ "damageMod": "none"
+ },
"name": "Spend Fear",
- "img": "icons/creatures/abilities/tail-strike-bone-orange.webp",
"range": ""
}
},
diff --git a/src/packs/adversaries/adversary_Jagged_Knife_Lackey_C0OMQqV7pN6t7ouR.json b/src/packs/adversaries/adversary_Jagged_Knife_Lackey_C0OMQqV7pN6t7ouR.json
index cfcdea8b..076318c6 100644
--- a/src/packs/adversaries/adversary_Jagged_Knife_Lackey_C0OMQqV7pN6t7ouR.json
+++ b/src/packs/adversaries/adversary_Jagged_Knife_Lackey_C0OMQqV7pN6t7ouR.json
@@ -131,12 +131,9 @@
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
"anchorX": 0.5,
"anchorY": 0.5,
- "offsetX": 0,
- "offsetY": 0,
"fit": "contain",
"scaleX": 1,
"scaleY": 1,
- "rotation": 0,
"tint": "#ffffff",
"alphaThreshold": 0.75
},
@@ -187,7 +184,7 @@
"saturation": 0,
"contrast": 0
},
- "detectionModes": [],
+ "detectionModes": {},
"occludable": {
"radius": 0
},
@@ -213,7 +210,8 @@
"flags": {},
"randomImg": false,
"appendNumber": false,
- "prependAdjective": false
+ "prependAdjective": false,
+ "depth": 1
},
"items": [
{
@@ -251,33 +249,95 @@
"description": "Spend a Fear to choose a target and spotlight all @Lookup[@name] within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
",
"resource": null,
"actions": {
- "aoQDb2m32NDxE6ZP": {
- "type": "effect",
- "_id": "aoQDb2m32NDxE6ZP",
+ "ferZO3BuiP9zU46m": {
+ "type": "attack",
+ "_id": "ferZO3BuiP9zU46m",
"systemPath": "actions",
+ "baseAction": false,
"description": "",
"chatDisplay": true,
+ "originItem": {
+ "type": "itemCollection"
+ },
"actionType": "action",
+ "triggers": [],
"cost": [
{
+ "consumeOnSuccess": false,
"scalable": false,
"key": "fear",
"value": 1,
+ "itemId": null,
"step": null
}
],
"uses": {
"value": null,
"max": "",
- "recovery": null
+ "recovery": null,
+ "consumeOnSuccess": false
+ },
+ "damage": {
+ "parts": {
+ "hitPoints": {
+ "applyTo": "hitPoints",
+ "resultBased": false,
+ "value": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": true,
+ "formula": "2"
+ }
+ },
+ "valueAlt": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": false,
+ "formula": ""
+ }
+ },
+ "base": false,
+ "type": [
+ "physical"
+ ]
+ }
+ },
+ "includeBase": false,
+ "direct": false,
+ "groupAttack": "close"
},
- "effects": [],
"target": {
"type": "any",
"amount": null
},
+ "effects": [],
+ "roll": {
+ "type": "attack",
+ "trait": null,
+ "difficulty": null,
+ "bonus": null,
+ "advState": "neutral",
+ "diceRolling": {
+ "multiplier": "prof",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "compare": null,
+ "treshold": null
+ },
+ "useDefault": false
+ },
+ "save": {
+ "trait": null,
+ "difficulty": null,
+ "damageMod": "none"
+ },
"name": "Spend Fear",
- "img": "icons/creatures/abilities/tail-strike-bone-orange.webp",
"range": ""
}
},
diff --git a/src/packs/adversaries/adversary_Minor_Treant_G62k4oSkhkoXEs2D.json b/src/packs/adversaries/adversary_Minor_Treant_G62k4oSkhkoXEs2D.json
index b2217e66..163556e9 100644
--- a/src/packs/adversaries/adversary_Minor_Treant_G62k4oSkhkoXEs2D.json
+++ b/src/packs/adversaries/adversary_Minor_Treant_G62k4oSkhkoXEs2D.json
@@ -125,12 +125,9 @@
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
"anchorX": 0.5,
"anchorY": 0.5,
- "offsetX": 0,
- "offsetY": 0,
"fit": "contain",
"scaleX": 1,
"scaleY": 1,
- "rotation": 0,
"tint": "#ffffff",
"alphaThreshold": 0.75
},
@@ -181,7 +178,7 @@
"saturation": 0,
"contrast": 0
},
- "detectionModes": [],
+ "detectionModes": {},
"occludable": {
"radius": 0
},
@@ -207,7 +204,8 @@
"flags": {},
"randomImg": false,
"appendNumber": false,
- "prependAdjective": false
+ "prependAdjective": false,
+ "depth": 1
},
"items": [
{
@@ -245,33 +243,95 @@
"description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
",
"resource": null,
"actions": {
- "xTMNAHcoErKuR6TZ": {
- "type": "effect",
- "_id": "xTMNAHcoErKuR6TZ",
+ "xFlhxnQWmVvDqQ55": {
+ "type": "attack",
+ "_id": "xFlhxnQWmVvDqQ55",
"systemPath": "actions",
+ "baseAction": false,
"description": "",
"chatDisplay": true,
+ "originItem": {
+ "type": "itemCollection"
+ },
"actionType": "action",
+ "triggers": [],
"cost": [
{
"scalable": false,
"key": "fear",
"value": 1,
- "step": null
+ "itemId": null,
+ "step": null,
+ "consumeOnSuccess": false
}
],
"uses": {
"value": null,
"max": "",
- "recovery": null
+ "recovery": null,
+ "consumeOnSuccess": false
+ },
+ "damage": {
+ "parts": {
+ "hitPoints": {
+ "applyTo": "hitPoints",
+ "resultBased": false,
+ "value": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": true,
+ "formula": "4"
+ }
+ },
+ "valueAlt": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": false,
+ "formula": ""
+ }
+ },
+ "base": false,
+ "type": [
+ "physical"
+ ]
+ }
+ },
+ "includeBase": false,
+ "direct": false,
+ "groupAttack": "close"
},
- "effects": [],
"target": {
- "type": "self",
+ "type": "any",
"amount": null
},
+ "effects": [],
+ "roll": {
+ "type": "attack",
+ "trait": null,
+ "difficulty": null,
+ "bonus": null,
+ "advState": "neutral",
+ "diceRolling": {
+ "multiplier": "prof",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "compare": null,
+ "treshold": null
+ },
+ "useDefault": false
+ },
+ "save": {
+ "trait": null,
+ "difficulty": null,
+ "damageMod": "none"
+ },
"name": "Spend Fear",
- "img": "icons/creatures/abilities/tail-strike-bone-orange.webp",
"range": ""
}
},
diff --git a/src/packs/adversaries/adversary_Outer_Realms_Thrall_moJhHgKqTKPS2WYS.json b/src/packs/adversaries/adversary_Outer_Realms_Thrall_moJhHgKqTKPS2WYS.json
index 5a7a605a..0bb3a44c 100644
--- a/src/packs/adversaries/adversary_Outer_Realms_Thrall_moJhHgKqTKPS2WYS.json
+++ b/src/packs/adversaries/adversary_Outer_Realms_Thrall_moJhHgKqTKPS2WYS.json
@@ -125,12 +125,9 @@
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
"anchorX": 0.5,
"anchorY": 0.5,
- "offsetX": 0,
- "offsetY": 0,
"fit": "contain",
"scaleX": 1,
"scaleY": 1,
- "rotation": 0,
"tint": "#ffffff",
"alphaThreshold": 0.75
},
@@ -181,7 +178,7 @@
"saturation": 0,
"contrast": 0
},
- "detectionModes": [],
+ "detectionModes": {},
"occludable": {
"radius": 0
},
@@ -207,7 +204,8 @@
"flags": {},
"randomImg": false,
"appendNumber": false,
- "prependAdjective": false
+ "prependAdjective": false,
+ "depth": 1
},
"items": [
{
@@ -242,33 +240,95 @@
"description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
",
"resource": null,
"actions": {
- "tvQetauskZoHDR5y": {
- "type": "effect",
- "_id": "tvQetauskZoHDR5y",
+ "6VKv71tPUIGGIfkZ": {
+ "type": "attack",
+ "_id": "6VKv71tPUIGGIfkZ",
"systemPath": "actions",
+ "baseAction": false,
"description": "",
"chatDisplay": true,
+ "originItem": {
+ "type": "itemCollection"
+ },
"actionType": "action",
+ "triggers": [],
"cost": [
{
+ "consumeOnSuccess": false,
"scalable": false,
"key": "fear",
"value": 1,
+ "itemId": null,
"step": null
}
],
"uses": {
"value": null,
"max": "",
- "recovery": null
+ "recovery": null,
+ "consumeOnSuccess": false
+ },
+ "damage": {
+ "parts": {
+ "hitPoints": {
+ "applyTo": "hitPoints",
+ "resultBased": false,
+ "value": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": true,
+ "formula": "11"
+ }
+ },
+ "valueAlt": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": false,
+ "formula": ""
+ }
+ },
+ "base": false,
+ "type": [
+ "physical"
+ ]
+ }
+ },
+ "includeBase": false,
+ "direct": false,
+ "groupAttack": "close"
},
- "effects": [],
"target": {
- "type": "self",
+ "type": "any",
"amount": null
},
+ "effects": [],
+ "roll": {
+ "type": "attack",
+ "trait": null,
+ "difficulty": null,
+ "bonus": null,
+ "advState": "neutral",
+ "diceRolling": {
+ "multiplier": "prof",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "compare": null,
+ "treshold": null
+ },
+ "useDefault": false
+ },
+ "save": {
+ "trait": null,
+ "difficulty": null,
+ "damageMod": "none"
+ },
"name": "Spend Fear",
- "img": "icons/creatures/abilities/tail-strike-bone-orange.webp",
"range": ""
}
},
diff --git a/src/packs/adversaries/adversary_Rotted_Zombie_gP3fWTLzSFnpA8EJ.json b/src/packs/adversaries/adversary_Rotted_Zombie_gP3fWTLzSFnpA8EJ.json
index 6755d27f..f1c7f470 100644
--- a/src/packs/adversaries/adversary_Rotted_Zombie_gP3fWTLzSFnpA8EJ.json
+++ b/src/packs/adversaries/adversary_Rotted_Zombie_gP3fWTLzSFnpA8EJ.json
@@ -125,12 +125,9 @@
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
"anchorX": 0.5,
"anchorY": 0.5,
- "offsetX": 0,
- "offsetY": 0,
"fit": "contain",
"scaleX": 1,
"scaleY": 1,
- "rotation": 0,
"tint": "#ffffff",
"alphaThreshold": 0.75
},
@@ -181,7 +178,7 @@
"saturation": 0,
"contrast": 0
},
- "detectionModes": [],
+ "detectionModes": {},
"occludable": {
"radius": 0
},
@@ -207,7 +204,8 @@
"flags": {},
"randomImg": false,
"appendNumber": false,
- "prependAdjective": false
+ "prependAdjective": false,
+ "depth": 1
},
"items": [
{
@@ -245,33 +243,95 @@
"description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
",
"resource": null,
"actions": {
- "DJBNtd3hWjwsjPwq": {
- "type": "effect",
- "_id": "DJBNtd3hWjwsjPwq",
+ "8wRrAWHU0xHW4zuE": {
+ "type": "attack",
+ "_id": "8wRrAWHU0xHW4zuE",
"systemPath": "actions",
+ "baseAction": false,
"description": "",
"chatDisplay": true,
+ "originItem": {
+ "type": "itemCollection"
+ },
"actionType": "action",
+ "triggers": [],
"cost": [
{
"scalable": false,
"key": "fear",
"value": 1,
- "step": null
+ "itemId": null,
+ "step": null,
+ "consumeOnSuccess": false
}
],
"uses": {
"value": null,
"max": "",
- "recovery": null
+ "recovery": null,
+ "consumeOnSuccess": false
+ },
+ "damage": {
+ "parts": {
+ "hitPoints": {
+ "applyTo": "hitPoints",
+ "resultBased": false,
+ "value": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": true,
+ "formula": "2"
+ }
+ },
+ "valueAlt": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": false,
+ "formula": ""
+ }
+ },
+ "base": false,
+ "type": [
+ "physical"
+ ]
+ }
+ },
+ "includeBase": false,
+ "direct": false,
+ "groupAttack": "close"
},
- "effects": [],
"target": {
- "type": "self",
+ "type": "any",
"amount": null
},
+ "effects": [],
+ "roll": {
+ "type": "attack",
+ "trait": null,
+ "difficulty": null,
+ "bonus": null,
+ "advState": "neutral",
+ "diceRolling": {
+ "multiplier": "prof",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "compare": null,
+ "treshold": null
+ },
+ "useDefault": false
+ },
+ "save": {
+ "trait": null,
+ "difficulty": null,
+ "damageMod": "none"
+ },
"name": "Spend Fear",
- "img": "icons/creatures/abilities/tail-strike-bone-orange.webp",
"range": ""
}
},
diff --git a/src/packs/adversaries/adversary_Sellsword_bgreCaQ6ap2DVpCr.json b/src/packs/adversaries/adversary_Sellsword_bgreCaQ6ap2DVpCr.json
index ed6d7775..46bed122 100644
--- a/src/packs/adversaries/adversary_Sellsword_bgreCaQ6ap2DVpCr.json
+++ b/src/packs/adversaries/adversary_Sellsword_bgreCaQ6ap2DVpCr.json
@@ -125,12 +125,9 @@
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
"anchorX": 0.5,
"anchorY": 0.5,
- "offsetX": 0,
- "offsetY": 0,
"fit": "contain",
"scaleX": 1,
"scaleY": 1,
- "rotation": 0,
"tint": "#ffffff",
"alphaThreshold": 0.75
},
@@ -181,7 +178,7 @@
"saturation": 0,
"contrast": 0
},
- "detectionModes": [],
+ "detectionModes": {},
"occludable": {
"radius": 0
},
@@ -207,7 +204,8 @@
"flags": {},
"randomImg": false,
"appendNumber": false,
- "prependAdjective": false
+ "prependAdjective": false,
+ "depth": 1
},
"items": [
{
@@ -245,33 +243,95 @@
"description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
",
"resource": null,
"actions": {
- "ghgFZskDiizJDjcn": {
- "type": "effect",
- "_id": "ghgFZskDiizJDjcn",
+ "K3pF2DBnR9zJ90W8": {
+ "type": "attack",
+ "_id": "K3pF2DBnR9zJ90W8",
"systemPath": "actions",
+ "baseAction": false,
"description": "",
"chatDisplay": true,
+ "originItem": {
+ "type": "itemCollection"
+ },
"actionType": "action",
+ "triggers": [],
"cost": [
{
+ "consumeOnSuccess": false,
"scalable": false,
"key": "fear",
"value": 1,
+ "itemId": null,
"step": null
}
],
"uses": {
"value": null,
"max": "",
- "recovery": null
+ "recovery": null,
+ "consumeOnSuccess": false
+ },
+ "damage": {
+ "parts": {
+ "hitPoints": {
+ "applyTo": "hitPoints",
+ "resultBased": false,
+ "value": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": true,
+ "formula": "3"
+ }
+ },
+ "valueAlt": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": false,
+ "formula": ""
+ }
+ },
+ "base": false,
+ "type": [
+ "physical"
+ ]
+ }
+ },
+ "includeBase": false,
+ "direct": false,
+ "groupAttack": "close"
},
- "effects": [],
"target": {
- "type": "self",
+ "type": "any",
"amount": null
},
+ "effects": [],
+ "roll": {
+ "type": "attack",
+ "trait": null,
+ "difficulty": null,
+ "bonus": null,
+ "advState": "neutral",
+ "diceRolling": {
+ "multiplier": "prof",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "compare": null,
+ "treshold": null
+ },
+ "useDefault": false
+ },
+ "save": {
+ "trait": null,
+ "difficulty": null,
+ "damageMod": "none"
+ },
"name": "Spend Fear",
- "img": "icons/creatures/abilities/tail-strike-bone-orange.webp",
"range": ""
}
},
diff --git a/src/packs/adversaries/adversary_Skeleton_Dredge_6l1a3Fazq8BoKIcc.json b/src/packs/adversaries/adversary_Skeleton_Dredge_6l1a3Fazq8BoKIcc.json
index e4cbab5e..1a82abb8 100644
--- a/src/packs/adversaries/adversary_Skeleton_Dredge_6l1a3Fazq8BoKIcc.json
+++ b/src/packs/adversaries/adversary_Skeleton_Dredge_6l1a3Fazq8BoKIcc.json
@@ -125,12 +125,9 @@
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
"anchorX": 0.5,
"anchorY": 0.5,
- "offsetX": 0,
- "offsetY": 0,
"fit": "contain",
"scaleX": 1,
"scaleY": 1,
- "rotation": 0,
"tint": "#ffffff",
"alphaThreshold": 0.75
},
@@ -181,7 +178,7 @@
"saturation": 0,
"contrast": 0
},
- "detectionModes": [],
+ "detectionModes": {},
"occludable": {
"radius": 0
},
@@ -207,7 +204,8 @@
"flags": {},
"randomImg": false,
"appendNumber": false,
- "prependAdjective": false
+ "prependAdjective": false,
+ "depth": 1
},
"items": [
{
@@ -245,33 +243,95 @@
"description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
",
"resource": null,
"actions": {
- "Sz55uB8xkoNytLwJ": {
- "type": "effect",
- "_id": "Sz55uB8xkoNytLwJ",
+ "6rdwJKwsSCO4R0Ty": {
+ "type": "attack",
+ "_id": "6rdwJKwsSCO4R0Ty",
"systemPath": "actions",
+ "baseAction": false,
"description": "",
"chatDisplay": true,
+ "originItem": {
+ "type": "itemCollection"
+ },
"actionType": "action",
+ "triggers": [],
"cost": [
{
"scalable": false,
"key": "fear",
"value": 1,
- "step": null
+ "itemId": null,
+ "step": null,
+ "consumeOnSuccess": false
}
],
"uses": {
"value": null,
"max": "",
- "recovery": null
+ "recovery": null,
+ "consumeOnSuccess": false
+ },
+ "damage": {
+ "parts": {
+ "hitPoints": {
+ "applyTo": "hitPoints",
+ "resultBased": false,
+ "value": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": true,
+ "formula": "1"
+ }
+ },
+ "valueAlt": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": false,
+ "formula": ""
+ }
+ },
+ "base": false,
+ "type": [
+ "physical"
+ ]
+ }
+ },
+ "includeBase": false,
+ "direct": false,
+ "groupAttack": "close"
},
- "effects": [],
"target": {
- "type": "self",
+ "type": "any",
"amount": null
},
+ "effects": [],
+ "roll": {
+ "type": "attack",
+ "trait": null,
+ "difficulty": null,
+ "bonus": null,
+ "advState": "neutral",
+ "diceRolling": {
+ "multiplier": "prof",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "compare": null,
+ "treshold": null
+ },
+ "useDefault": false
+ },
+ "save": {
+ "trait": null,
+ "difficulty": null,
+ "damageMod": "none"
+ },
"name": "Spend Fear",
- "img": "icons/creatures/abilities/tail-strike-bone-orange.webp",
"range": ""
}
},
diff --git a/src/packs/adversaries/adversary_Tangle_Bramble_XcAGOSmtCFLT1unN.json b/src/packs/adversaries/adversary_Tangle_Bramble_XcAGOSmtCFLT1unN.json
index c36502de..d635b2ca 100644
--- a/src/packs/adversaries/adversary_Tangle_Bramble_XcAGOSmtCFLT1unN.json
+++ b/src/packs/adversaries/adversary_Tangle_Bramble_XcAGOSmtCFLT1unN.json
@@ -164,12 +164,9 @@
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
"anchorX": 0.5,
"anchorY": 0.5,
- "offsetX": 0,
- "offsetY": 0,
"fit": "contain",
"scaleX": 1,
"scaleY": 1,
- "rotation": 0,
"tint": "#ffffff",
"alphaThreshold": 0.75
},
@@ -220,7 +217,7 @@
"saturation": 0,
"contrast": 0
},
- "detectionModes": [],
+ "detectionModes": {},
"occludable": {
"radius": 0
},
@@ -246,7 +243,8 @@
"flags": {},
"randomImg": false,
"appendNumber": false,
- "prependAdjective": false
+ "prependAdjective": false,
+ "depth": 1
},
"items": [
{
@@ -284,33 +282,95 @@
"description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
",
"resource": null,
"actions": {
- "ZC5pKIb9N82vgMWu": {
- "type": "effect",
- "_id": "ZC5pKIb9N82vgMWu",
+ "V58Ry90tvIjvfDTZ": {
+ "type": "attack",
+ "_id": "V58Ry90tvIjvfDTZ",
"systemPath": "actions",
+ "baseAction": false,
"description": "",
"chatDisplay": true,
+ "originItem": {
+ "type": "itemCollection"
+ },
"actionType": "action",
+ "triggers": [],
"cost": [
{
+ "consumeOnSuccess": false,
"scalable": false,
"key": "fear",
"value": 1,
+ "itemId": null,
"step": null
}
],
"uses": {
"value": null,
"max": "",
- "recovery": null
+ "recovery": null,
+ "consumeOnSuccess": false
+ },
+ "damage": {
+ "parts": {
+ "hitPoints": {
+ "applyTo": "hitPoints",
+ "resultBased": false,
+ "value": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": true,
+ "formula": "2"
+ }
+ },
+ "valueAlt": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": false,
+ "formula": ""
+ }
+ },
+ "base": false,
+ "type": [
+ "physical"
+ ]
+ }
+ },
+ "includeBase": false,
+ "direct": false,
+ "groupAttack": "close"
},
- "effects": [],
"target": {
- "type": "self",
+ "type": "any",
"amount": null
},
+ "effects": [],
+ "roll": {
+ "type": "attack",
+ "trait": null,
+ "difficulty": null,
+ "bonus": null,
+ "advState": "neutral",
+ "diceRolling": {
+ "multiplier": "prof",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "compare": null,
+ "treshold": null
+ },
+ "useDefault": false
+ },
+ "save": {
+ "trait": null,
+ "difficulty": null,
+ "damageMod": "none"
+ },
"name": "Spend Fear",
- "img": "icons/creatures/abilities/tail-strike-bone-orange.webp",
"range": ""
}
},
diff --git a/src/packs/adversaries/adversary_Treant_Sapling_o63nS0k3wHu6EgKP.json b/src/packs/adversaries/adversary_Treant_Sapling_o63nS0k3wHu6EgKP.json
index c6c11d36..c03a1b52 100644
--- a/src/packs/adversaries/adversary_Treant_Sapling_o63nS0k3wHu6EgKP.json
+++ b/src/packs/adversaries/adversary_Treant_Sapling_o63nS0k3wHu6EgKP.json
@@ -125,12 +125,9 @@
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
"anchorX": 0.5,
"anchorY": 0.5,
- "offsetX": 0,
- "offsetY": 0,
"fit": "contain",
"scaleX": 1,
"scaleY": 1,
- "rotation": 0,
"tint": "#ffffff",
"alphaThreshold": 0.75
},
@@ -181,7 +178,7 @@
"saturation": 0,
"contrast": 0
},
- "detectionModes": [],
+ "detectionModes": {},
"occludable": {
"radius": 0
},
@@ -207,7 +204,8 @@
"flags": {},
"randomImg": false,
"appendNumber": false,
- "prependAdjective": false
+ "prependAdjective": false,
+ "depth": 1
},
"items": [
{
@@ -242,33 +240,95 @@
"description": "Spend a Fear to choose a target and spotlight all @Lookup[@name]s within Close range of them. Those Minions move into Melee range of the target and make one shared attack roll. On a success, they deal @Lookup[@system.attack.damageFormula] physical damage each. Combine this damage.
",
"resource": null,
"actions": {
- "euP8VA4wvfsCpwN1": {
- "type": "effect",
- "_id": "euP8VA4wvfsCpwN1",
+ "Itubbr63irPJcbXG": {
+ "type": "attack",
+ "_id": "Itubbr63irPJcbXG",
"systemPath": "actions",
+ "baseAction": false,
"description": "",
"chatDisplay": true,
+ "originItem": {
+ "type": "itemCollection"
+ },
"actionType": "action",
+ "triggers": [],
"cost": [
{
"scalable": false,
"key": "fear",
"value": 1,
- "step": null
+ "itemId": null,
+ "step": null,
+ "consumeOnSuccess": false
}
],
"uses": {
"value": null,
"max": "",
- "recovery": null
+ "recovery": null,
+ "consumeOnSuccess": false
+ },
+ "damage": {
+ "parts": {
+ "hitPoints": {
+ "applyTo": "hitPoints",
+ "resultBased": false,
+ "value": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": true,
+ "formula": "8"
+ }
+ },
+ "valueAlt": {
+ "multiplier": "flat",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "bonus": null,
+ "custom": {
+ "enabled": false,
+ "formula": ""
+ }
+ },
+ "base": false,
+ "type": [
+ "physical"
+ ]
+ }
+ },
+ "includeBase": false,
+ "direct": false,
+ "groupAttack": "close"
},
- "effects": [],
"target": {
- "type": "self",
+ "type": "any",
"amount": null
},
+ "effects": [],
+ "roll": {
+ "type": "attack",
+ "trait": null,
+ "difficulty": null,
+ "bonus": null,
+ "advState": "neutral",
+ "diceRolling": {
+ "multiplier": "prof",
+ "flatMultiplier": 1,
+ "dice": "d6",
+ "compare": null,
+ "treshold": null
+ },
+ "useDefault": false
+ },
+ "save": {
+ "trait": null,
+ "difficulty": null,
+ "damageMod": "none"
+ },
"name": "Spend Fear",
- "img": "icons/magic/unholy/orb-hands-pink.webp",
"range": ""
}
},
diff --git a/src/packs/domains/domainCard_Bare_Bones_l5D9kq901JDESaXw.json b/src/packs/domains/domainCard_Bare_Bones_l5D9kq901JDESaXw.json
index 098f5f4c..7956d6eb 100644
--- a/src/packs/domains/domainCard_Bare_Bones_l5D9kq901JDESaXw.json
+++ b/src/packs/domains/domainCard_Bare_Bones_l5D9kq901JDESaXw.json
@@ -4,7 +4,7 @@
"type": "domainCard",
"folder": "QpOL7jPbMBzH96qR",
"system": {
- "description": "When you choose not to equip armor, you have a base Armor Score of 3 + your Strength and use the following as your base damage thresholds:
Tier 1: 9/19Tier 2: 11/24Tier 3: 13/31Tier 4: 15/38Equip the below armor to use Bare Bones.
@UUID[Compendium.daggerheart.armors.Item.ITAjcigTcUw5pMCN]{Bare Bones}
",
+ "description": "When you choose not to equip armor, you have a base Armor Score of 3 + your Strength and use the following as your base damage thresholds:
Tier 1: 9/19
Tier 2: 11/24
Tier 3: 13/31
Tier 4: 15/38
",
"domain": "valor",
"recallCost": 0,
"level": 1,
@@ -28,22 +28,33 @@
{
"type": "armor",
"phase": "initial",
- "priority": 20,
"value": {
+ "current": 0,
"max": "3 + @system.traits.strength.value",
"interaction": "inactive",
"damageThresholds": {
- "major": "9 + (@tier - 1) * 5 + max(0, (@tier -2) * 2 )",
+ "major": "9 + (@tier - 1) * 2",
"severe": "19 + (@tier - 1) * 5 + max(0, (@tier -2) * 2 )"
}
- }
+ },
+ "priority": 20
}
- ]
+ ],
+ "duration": {
+ "type": ""
+ }
},
"_id": "FCsgz7Tdsw6QUzBs",
"img": "icons/magic/control/buff-strength-muscle-damage-orange.webp",
"disabled": false,
- "start": null,
+ "start": {
+ "time": 0,
+ "combat": null,
+ "combatant": null,
+ "initiative": null,
+ "round": null,
+ "turn": null
+ },
"duration": {
"value": null,
"units": "seconds",
diff --git a/styles/less/dialog/damage-selection/sheet.less b/styles/less/dialog/damage-selection/sheet.less
index 0f765748..9f8cfc8a 100644
--- a/styles/less/dialog/damage-selection/sheet.less
+++ b/styles/less/dialog/damage-selection/sheet.less
@@ -21,7 +21,7 @@
gap: 4px;
.critical-chip {
flex: 0;
-
+
display: flex;
align-items: center;
border-radius: 5px;
@@ -41,6 +41,26 @@
}
}
+ .group-attack-container {
+ margin: 0;
+
+ .group-attack-inner-container {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+
+ > * {
+ flex: 1;
+ }
+
+ .group-attack-tools {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ }
+ }
+ }
+
.damage-section-controls {
display: flex;
align-items: center;
diff --git a/styles/less/dialog/group-roll-dialog/initialization.less b/styles/less/dialog/group-roll-dialog/initialization.less
new file mode 100644
index 00000000..96990339
--- /dev/null
+++ b/styles/less/dialog/group-roll-dialog/initialization.less
@@ -0,0 +1,78 @@
+.theme-light .daggerheart.dialog.dh-style.views.group-roll-dialog {
+ .initialization-container .members-container .member-container {
+ .member-name {
+ background-image: url('../assets/parchments/dh-parchment-light.png');
+ }
+ }
+}
+
+.daggerheart.dialog.dh-style.views.group-roll-dialog {
+ .initialization-container {
+ h2 {
+ text-align: center;
+ }
+
+ .members-container {
+ display: grid;
+ grid-template-columns: 1fr 1fr 1fr 1fr;
+ gap: 8px;
+
+ .member-container {
+ position: relative;
+ display: flex;
+ justify-content: center;
+
+ &.inactive {
+ opacity: 0.4;
+ }
+
+ .member-name {
+ position: absolute;
+ padding: 0 2px;
+ border: 1px solid;
+ border-radius: 6px;
+ margin-top: 4px;
+ color: light-dark(@dark, @beige);
+ background-image: url('../assets/parchments/dh-parchment-dark.png');
+ }
+
+ img {
+ border-radius: 6px;
+ border: 1px solid light-dark(@dark-blue, @golden);
+ }
+ }
+ }
+
+ .main-roll {
+ margin-top: 8px;
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 8px;
+
+ &.inactive {
+ opacity: 0.4;
+ }
+ }
+
+ footer {
+ margin-top: 8px;
+ display: flex;
+ gap: 8px;
+
+ button {
+ flex: 1;
+ }
+
+ .finish-tools {
+ flex: none;
+ display: flex;
+ align-items: center;
+ gap: 4px;
+
+ &.inactive {
+ opacity: 0.4;
+ }
+ }
+ }
+ }
+}
diff --git a/styles/less/dialog/group-roll-dialog/leader.less b/styles/less/dialog/group-roll-dialog/leader.less
new file mode 100644
index 00000000..b3fa3a3b
--- /dev/null
+++ b/styles/less/dialog/group-roll-dialog/leader.less
@@ -0,0 +1,35 @@
+.daggerheart.dialog.dh-style.views.group-roll-dialog {
+ .main-character-outer-container {
+ &.inactive {
+ opacity: 0.3;
+ pointer-events: none;
+ }
+
+ .main-character-container {
+ .character-info {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+ height: 64px;
+
+ img {
+ height: 64px;
+ border-radius: 6px;
+ border: 1px solid light-dark(@dark-blue, @golden);
+ }
+
+ .character-data {
+ padding-left: 0.75rem;
+ flex: 1;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ text-align: left;
+ font-size: var(--font-size-18);
+ }
+ }
+ }
+ }
+}
diff --git a/styles/less/dialog/group-roll-dialog/sheet.less b/styles/less/dialog/group-roll-dialog/sheet.less
new file mode 100644
index 00000000..823f6cbf
--- /dev/null
+++ b/styles/less/dialog/group-roll-dialog/sheet.less
@@ -0,0 +1,266 @@
+.daggerheart.dialog.dh-style.views.group-roll-dialog {
+ .team-container {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 16px;
+ margin-bottom: 16px;
+
+ .team-member-container {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ gap: 8px;
+ flex: 1;
+
+ &.inactive {
+ opacity: 0.3;
+ pointer-events: none;
+ }
+
+ .data-container {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ width: 100%;
+ }
+
+ .member-info {
+ display: flex;
+ align-items: center;
+ width: 100%;
+ height: 64px;
+
+ img {
+ height: 64px;
+ border-radius: 6px;
+ border: 1px solid light-dark(@dark-blue, @golden);
+ }
+
+ .member-data {
+ padding-left: 0.75rem;
+ flex: 1;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ text-align: left;
+ font-size: var(--font-size-18);
+ }
+ }
+ }
+ }
+
+ .roll-container {
+ display: flex;
+ flex-direction: column;
+ }
+
+ .roll-title {
+ font-size: var(--font-size-20);
+ font-weight: bold;
+ color: light-dark(@dark-blue, @golden);
+ text-align: center;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+
+ &.hope,
+ &.fear,
+ &.critical {
+ color: var(--text-color);
+ }
+
+ &.hope {
+ --text-color: @golden;
+ }
+
+ &.fear {
+ --text-color: @chat-blue;
+ }
+
+ &.critical {
+ --text-color: @chat-purple;
+ }
+
+ &::before,
+ &::after {
+ color: var(--text-color);
+ content: '';
+ flex: 1;
+ height: 2px;
+ }
+
+ &::before {
+ background: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, light-dark(@dark-blue, @golden) 100%);
+ }
+
+ &::after {
+ background: linear-gradient(90deg, light-dark(@dark-blue, @golden) 0%, rgba(0, 0, 0, 0) 100%);
+ }
+ }
+
+ .roll-tools {
+ display: flex;
+ gap: 4px;
+ align-items: center;
+
+ img {
+ height: 16px;
+ }
+
+ a {
+ display: flex;
+ font-size: 16px;
+
+ &:hover {
+ text-shadow: none;
+ filter: drop-shadow(0 0 8px var(--golden));
+ }
+ }
+ }
+
+ .roll-data {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 4px;
+
+ &.hope {
+ --text-color: @golden;
+ --bg-color: @golden-40;
+ }
+
+ &.fear {
+ --text-color: @chat-blue;
+ --bg-color: @chat-blue-40;
+ }
+
+ &.critical {
+ --text-color: @chat-purple;
+ --bg-color: @chat-purple-40;
+ }
+
+ .duality-label {
+ color: var(--text-color);
+ font-size: var(--font-size-20);
+ font-weight: bold;
+ text-align: center;
+
+ .unused-damage {
+ text-decoration: line-through;
+ }
+ }
+
+ .roll-dice-container {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-wrap: wrap;
+ gap: 8px;
+
+ .roll-dice {
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ .dice-label {
+ position: absolute;
+ color: white;
+ font-size: 1rem;
+ paint-order: stroke fill;
+ -webkit-text-stroke: 2px black;
+ }
+
+ img {
+ height: 32px;
+ }
+ }
+
+ .roll-operator {
+ font-size: var(--font-size-24);
+ }
+
+ .roll-value {
+ font-size: 18px;
+ }
+ }
+
+ .roll-total {
+ background: var(--bg-color);
+ color: var(--text-color);
+ border-radius: 4px;
+ padding: 3px;
+ }
+ }
+
+ .roll-success-container {
+ display: flex;
+ align-items: center;
+ justify-content: space-around;
+
+ .roll-success-tools {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ color: light-dark(@dark-blue, @golden);
+
+ i {
+ font-size: 24px;
+ }
+ }
+
+ .roll-success-modifier {
+ display: flex;
+ align-items: center;
+ justify-content: right;
+ gap: 2px;
+ font-size: var(--font-size-20);
+ padding: 0px 4px;
+
+ &.success {
+ background: @green-10;
+ color: @green;
+ }
+
+ &.failure {
+ background: @red-10;
+ color: @red;
+ }
+ }
+ }
+
+ .section-title {
+ font-size: var(--font-size-18);
+ font-weight: bold;
+ }
+
+ .group-roll-results {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 4px;
+ font-size: var(--font-size-20);
+
+ .group-roll-container {
+ display: flex;
+ align-items: center;
+ gap: 2px;
+ }
+ }
+
+ .finish-container {
+ margin-top: 16px;
+ gap: 16px;
+ display: grid;
+ grid-template-columns: 1fr 1fr 1fr;
+
+ .finish-button {
+ grid-column: span 2;
+ }
+ }
+
+ .hint {
+ text-align: center;
+ }
+}
diff --git a/styles/less/dialog/index.less b/styles/less/dialog/index.less
index 73738eaa..947142ff 100644
--- a/styles/less/dialog/index.less
+++ b/styles/less/dialog/index.less
@@ -36,6 +36,10 @@
@import './tag-team-dialog/initialization.less';
@import './tag-team-dialog/sheet.less';
+@import './group-roll-dialog/initialization.less';
+@import './group-roll-dialog/leader.less';
+@import './group-roll-dialog/sheet.less';
+
@import './image-select/sheet.less';
@import './item-transfer/sheet.less';
diff --git a/styles/less/dialog/tag-team-dialog/initialization.less b/styles/less/dialog/tag-team-dialog/initialization.less
index 30676f82..0d16aa3b 100644
--- a/styles/less/dialog/tag-team-dialog/initialization.less
+++ b/styles/less/dialog/tag-team-dialog/initialization.less
@@ -20,6 +20,17 @@
.member-name {
position: absolute;
+ padding: 0 2px;
+ border: 1px solid;
+ border-radius: 6px;
+ margin-top: 4px;
+ color: light-dark(@dark, @beige);
+ background-image: url('../assets/parchments/dh-parchment-dark.png');
+ }
+
+ img {
+ border-radius: 6px;
+ border: 1px solid light-dark(@dark-blue, @golden);
}
}
}
diff --git a/styles/less/global/elements.less b/styles/less/global/elements.less
index 793c8164..c5bca1da 100755
--- a/styles/less/global/elements.less
+++ b/styles/less/global/elements.less
@@ -419,11 +419,19 @@
width: fit-content;
display: flex;
align-items: center;
+
.form-fields {
height: 32px;
align-content: center;
}
}
+
+ &.select {
+ width: fit-content;
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ }
}
.scalable-input {
diff --git a/styles/less/sheets/actors/party/party-members.less b/styles/less/sheets/actors/party/party-members.less
index a433ae34..155fcc36 100644
--- a/styles/less/sheets/actors/party/party-members.less
+++ b/styles/less/sheets/actors/party/party-members.less
@@ -1,28 +1,293 @@
@import '../../../utils/colors.less';
@import '../../../utils/fonts.less';
+@import '../../../utils/mixin.less';
-.application.sheet.daggerheart.actor.dh-style.party {
- .tab.partyMembers {
- max-height: 400px;
- overflow: auto;
+body.game:is(.performance-low, .noblur) {
+ .application.sheet.daggerheart.actor.dh-style.party .tab.resources .actors-list .actor-resources {
+ background: light-dark(@dark-blue, @dark-golden);
- .actors-list {
- display: flex;
- flex-direction: column;
- gap: 10px;
- align-items: center;
- width: 100%;
- }
- .actors-dragger {
- display: flex;
- align-items: center;
- justify-content: center;
- box-sizing: border-box;
- width: 100%;
- height: 40px;
- border: 1px dashed light-dark(@dark-blue-50, @beige-50);
- border-radius: 3px;
- color: light-dark(@dark-blue-50, @beige-50);
+ .actor-name {
+ background: light-dark(@dark-blue, @dark-golden);
}
}
}
+
+.application.sheet.daggerheart.actor.dh-style.party .tab.partyMembers {
+ overflow: auto;
+
+ .actors-list {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ align-items: stretch;
+ width: 100%;
+
+ .actor-resources {
+ display: grid;
+ grid-template:
+ "img header" min-content
+ "img body" 1fr
+ / 7.5rem 1fr;
+ gap: 6px;
+ column-gap: 12px;
+ padding: 6px;
+ background-color: light-dark(@dark-blue-10, @golden-10);
+
+ .actor-img-frame {
+ grid-area: img;
+ width: 7.5rem;
+ height: 7.5rem;
+ position: relative;
+
+ .actor-img {
+ object-fit: cover;
+ object-position: top center;
+ border-radius: 6px;
+ width: 100%;
+ height: 100%;
+ }
+
+ .equipped-weapons {
+ position: absolute;
+ top: -2px;
+ left: -3px;
+ display: flex;
+ flex-direction: column;
+ gap: 1px;
+ img {
+ border-radius: 50%;
+ width: 24px;
+ height: 24px;
+ border: 1px solid light-dark(@dark-blue, @golden);
+ object-fit: cover;
+ }
+ }
+
+ .evasion {
+ position: absolute;
+ top: 1px;
+ right: 1px;
+ width: 1.75rem;
+ height: 1.75rem;
+ background: url('../assets/svg/trait-shield.svg') no-repeat;
+ background-size: 100%;
+ font-size: var(--font-size-14);
+ font-weight: 700;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .threshold-section {
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: -2px;
+ margin: auto;
+
+ display: flex;
+ gap: 4px;
+ background-color: light-dark(transparent, @dark-blue);
+ color: light-dark(@dark-blue, @golden);
+ padding: 4px 6px;
+ border: 1px solid light-dark(@dark-blue, @golden);
+ border-radius: 3px;
+ align-items: baseline;
+ width: fit-content;
+
+ h4 {
+ font-weight: bold;
+ text-transform: uppercase;
+ white-space: nowrap;
+
+ &.threshold-label {
+ font-size: var(--font-size-10);
+ color: light-dark(@dark-blue, @golden);
+ }
+
+ &.threshold-value {
+ font-size: var(--font-size-11);
+ color: light-dark(@dark, @beige);
+ }
+ }
+ }
+ }
+
+ header {
+ grid-area: header;
+ display: grid;
+ grid-template:
+ "name hope" min-content
+ "subtitle subtitle" min-content
+ / 1fr min-content;
+
+ .actor-name {
+ width: 100%;
+ z-index: 1;
+ font-size: var(--font-size-20);
+ color: light-dark(@beige, @golden);
+ font-weight: bold;
+ }
+
+ .delete-icon {
+ font-size: 0.75em;
+ }
+
+ .subtitle {
+ grid-area: subtitle;
+ font-size: var(--font-size-14);
+ }
+
+ .hope-section {
+ display: flex;
+ background-color: light-dark(transparent, @dark-blue);
+ color: light-dark(@dark-blue, @golden);
+ padding: 3px 6px;
+ border: 1px solid light-dark(@dark-blue, @golden);
+ border-radius: 3px;
+ align-items: center;
+ width: fit-content;
+ margin-left: auto;
+
+ h4 {
+ font-size: var(--font-size-12);
+ font-weight: bold;
+ text-transform: uppercase;
+ color: light-dark(@dark-blue, @golden);
+ margin-right: 3px;
+ }
+
+ .hope-value {
+ display: flex;
+ cursor: pointer;
+ font-size: var(--font-size-12);
+ margin-left: 1px;
+ }
+ }
+ }
+
+ .body {
+ grid-area: body;
+ display: flex;
+ align-items: start;
+ justify-content: space-between;
+ }
+
+ .resources {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+
+ .slot-section {
+ display: flex;
+ flex-direction: row;
+ align-items: stretch;
+
+ .slot-label {
+ display: flex;
+ align-items: center;
+ color: light-dark(@beige, @dark-blue);
+ background: light-dark(@dark-blue, @golden);
+ padding: 0 4px;
+ width: fit-content;
+ font-weight: bold;
+ border-radius: 6px 0px 0px 6px;
+ font-size: var(--font-size-12);
+ white-space: nowrap;
+
+ .label {
+ padding-right: 2px;
+ }
+
+ .value {
+ font-variant-numeric: tabular-nums;
+ .current {
+ display: inline-block;
+ text-align: end;
+ width: 2ch;
+ }
+ .max {
+ display: inline-block;
+ text-align: start;
+ width: 2ch;
+ }
+ }
+ }
+
+ .slot-bar {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 4px;
+
+ background-color: light-dark(@dark-blue-10, @dark-blue);
+ color: light-dark(@dark-blue, @golden);
+ padding: 2px 5px;
+ border: 1px solid light-dark(@dark-blue, @golden);
+ border-radius: 0 6px 6px 0;
+ width: fit-content;
+ min-height: 22px;
+
+ .armor-slot {
+ cursor: pointer;
+ transition: all 0.3s ease;
+ font-size: var(--font-size-12);
+
+ .fa-shield-halved {
+ color: light-dark(@dark-blue-40, @golden-40);
+ }
+ }
+
+ .slot {
+ width: 16px;
+ height: 10px;
+ border: 1px solid light-dark(@dark-blue, @golden);
+ background: light-dark(@dark-blue-10, @golden-10);
+ border-radius: 3px;
+ transition: all 0.3s ease;
+ cursor: pointer;
+
+ &.filled {
+ background: light-dark(@dark-blue, @golden);
+ }
+ }
+ }
+ }
+ }
+
+ .traits {
+ background-color: light-dark(@dark-blue-10, @dark-blue);
+ border: 1px solid light-dark(@dark-blue, @golden);
+ border-radius: 6px;
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ font-size: var(--font-size-12);
+ padding: 3px 4px;
+ gap: 3px 7px;
+ .trait {
+ display: flex;
+ justify-content: space-between;
+ gap: 3px;
+ .label {
+ color: light-dark(@dark-blue, @golden);
+ }
+ .value {
+ font-weight: 600;
+ }
+ }
+ }
+ }
+ }
+
+ .actors-dragger {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ box-sizing: border-box;
+ width: 100%;
+ height: 40px;
+ border: 1px dashed light-dark(@dark-blue-50, @beige-50);
+ border-radius: 3px;
+ color: light-dark(@dark-blue-50, @beige-50);
+ }
+}
diff --git a/styles/less/sheets/actors/party/resources.less b/styles/less/sheets/actors/party/resources.less
deleted file mode 100644
index 68628295..00000000
--- a/styles/less/sheets/actors/party/resources.less
+++ /dev/null
@@ -1,216 +0,0 @@
-@import '../../../utils/colors.less';
-@import '../../../utils/fonts.less';
-@import '../../../utils/mixin.less';
-
-body.game:is(.performance-low, .noblur) {
- .application.sheet.daggerheart.actor.dh-style.party .tab.resources .actors-list .actor-resources {
- background: light-dark(@dark-blue, @dark-golden);
-
- .actor-name {
- background: light-dark(@dark-blue, @dark-golden);
- }
- }
-}
-
-.application.sheet.daggerheart.actor.dh-style.party {
- .tab.resources {
- overflow: auto;
-
- .actors-list {
- display: flex;
- flex-direction: row;
- flex-wrap: wrap;
- gap: 10px;
- align-items: center;
- width: 100%;
- justify-content: center;
-
- .actor-resources {
- display: flex;
- flex-direction: column;
- position: relative;
- background: light-dark(@dark-blue-40, @dark-golden-40);
- border-radius: 6px;
- border: 1px solid light-dark(@dark-blue, @golden);
- width: 230px;
- height: -webkit-fill-available;
-
- .actor-name {
- position: absolute;
- top: 0px;
- background: light-dark(@dark-blue-90, @dark-golden-80);
- backdrop-filter: blur(6.5px);
- border-radius: 6px 6px 0px 0px;
- text-align: center;
- width: 100%;
- z-index: 1;
- font-size: var(--font-size-20);
- color: light-dark(@beige, @golden);
- font-weight: bold;
- padding: 5px 0;
- }
-
- .actor-img {
- height: 150px;
- object-fit: cover;
- object-position: top center;
- border-radius: 6px 6px 0px 0px;
- mask-image: linear-gradient(180deg, black 88%, transparent 100%);
- }
-
- .resources {
- display: flex;
- flex-direction: column;
- gap: 10px;
- align-items: center;
- margin: 10px;
-
- .slot-section {
- display: flex;
- flex-direction: column;
- align-items: center;
-
- .slot-bar {
- display: flex;
- flex-wrap: wrap;
- gap: 5px;
- width: 239px;
-
- background-color: light-dark(@dark-blue-10, @dark-blue);
- color: light-dark(@dark-blue, @golden);
- padding: 5px;
- border: 1px solid light-dark(@dark-blue, @golden);
- border-radius: 6px;
- width: fit-content;
-
- .armor-slot {
- cursor: pointer;
- transition: all 0.3s ease;
- font-size: var(--font-size-12);
-
- .fa-shield-halved {
- color: light-dark(@dark-blue-40, @golden-40);
- }
- }
-
- .slot {
- width: 20px;
- height: 10px;
- border: 1px solid light-dark(@dark-blue, @golden);
- background: light-dark(@dark-blue-10, @golden-10);
- border-radius: 3px;
- transition: all 0.3s ease;
- cursor: pointer;
-
- &.filled {
- background: light-dark(@dark-blue, @golden);
- }
- }
- }
- .slot-label {
- display: flex;
- align-items: center;
- color: light-dark(@beige, @dark-blue);
- background: light-dark(@dark-blue, @golden);
- padding: 0 5px;
- width: fit-content;
- font-weight: bold;
- border-radius: 0px 0px 5px 5px;
- font-size: var(--font-size-12);
-
- .label {
- padding-right: 5px;
- }
-
- .value {
- padding-left: 6px;
- border-left: 1px solid light-dark(@beige, @dark-golden);
- }
- }
- }
-
- .hope-section {
- position: relative;
- display: flex;
- gap: 10px;
- background-color: light-dark(transparent, @dark-blue);
- color: light-dark(@dark-blue, @golden);
- padding: 5px 10px;
- border: 1px solid light-dark(@dark-blue, @golden);
- border-radius: 3px;
- align-items: center;
- width: fit-content;
-
- h4 {
- font-size: var(--font-size-12);
- font-weight: bold;
- text-transform: uppercase;
- color: light-dark(@dark-blue, @golden);
- }
-
- .hope-value {
- display: flex;
- cursor: pointer;
- font-size: var(--font-size-12);
- }
- }
-
- .stat-section {
- position: relative;
- display: flex;
- gap: 10px;
- background-color: light-dark(transparent, @dark-blue);
- color: light-dark(@dark-blue, @golden);
- padding: 5px 10px;
- border: 1px solid light-dark(@dark-blue, @golden);
- border-radius: 3px;
- align-items: center;
- width: fit-content;
-
- h4 {
- font-size: var(--font-size-12);
- font-weight: bold;
- text-transform: uppercase;
- color: light-dark(@dark-blue, @golden);
- }
- }
-
- .threshold-section {
- display: flex;
- align-self: center;
- gap: 10px;
- background-color: light-dark(transparent, @dark-blue);
- color: light-dark(@dark-blue, @golden);
- padding: 5px 10px;
- border: 1px solid light-dark(@dark-blue, @golden);
- border-radius: 3px;
- align-items: center;
- width: fit-content;
-
- h4 {
- font-size: var(--font-size-12);
- font-weight: bold;
- text-transform: uppercase;
- color: light-dark(@dark-blue, @golden);
-
- &.threshold-value {
- color: light-dark(@dark, @beige);
- }
- }
- }
- }
- }
- }
- .actors-dragger {
- display: flex;
- align-items: center;
- justify-content: center;
- box-sizing: border-box;
- width: 100%;
- height: 40px;
- border: 1px dashed light-dark(@dark-blue-50, @beige-50);
- border-radius: 3px;
- color: light-dark(@dark-blue-50, @beige-50);
- }
- }
-}
diff --git a/styles/less/sheets/index.less b/styles/less/sheets/index.less
index e5ffbf3e..7d595614 100644
--- a/styles/less/sheets/index.less
+++ b/styles/less/sheets/index.less
@@ -31,7 +31,6 @@
@import './actors/party/party-members.less';
@import './actors/party/sheet.less';
@import './actors/party/inventory.less';
-@import './actors/party/resources.less';
@import './items/beastform.less';
@import './items/class.less';
diff --git a/styles/less/ui/settings/appearance-settings/diceSoNice.less b/styles/less/ui/settings/appearance-settings/diceSoNice.less
index 079bebc5..a4846596 100644
--- a/styles/less/ui/settings/appearance-settings/diceSoNice.less
+++ b/styles/less/ui/settings/appearance-settings/diceSoNice.less
@@ -68,5 +68,29 @@
text-align: center;
white-space: nowrap;
}
+
+ color-picker {
+ gap: 4px;
+ background: inherit;
+ }
+ }
+
+ .animation-container {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+
+ .animation-inner-container {
+ display: flex;
+ align-items: center;
+ justify-content: right;
+ gap: 8px;
+
+ .animation-control {
+ display: flex;
+ align-items: center;
+ gap: 2px;
+ }
+ }
}
}
diff --git a/system.json b/system.json
index 28d849b3..450c33b2 100644
--- a/system.json
+++ b/system.json
@@ -2,10 +2,10 @@
"id": "daggerheart",
"title": "Daggerheart",
"description": "An unofficial implementation of the Daggerheart system",
- "version": "2.0.3",
+ "version": "2.1.1",
"compatibility": {
"minimum": "14.359",
- "verified": "14.359",
+ "verified": "14.360",
"maximum": "14"
},
"authors": [
@@ -290,7 +290,6 @@
"damageRoll": {},
"abilityUse": {},
"tagTeam": {},
- "groupRoll": {},
"systemMessage": {}
}
},
@@ -299,5 +298,5 @@
"secondaryTokenAttribute": "resources.stress",
"url": "https://github.com/Foundryborne/daggerheart",
"manifest": "https://raw.githubusercontent.com/Foundryborne/daggerheart/main/system.json",
- "download": "https://github.com/Foundryborne/daggerheart/releases/download/2.0.3/system.zip"
+ "download": "https://github.com/Foundryborne/daggerheart/releases/download/2.1.1/system.zip"
}
diff --git a/templates/actionTypes/damage.hbs b/templates/actionTypes/damage.hbs
index 9e7c2884..192c5be5 100644
--- a/templates/actionTypes/damage.hbs
+++ b/templates/actionTypes/damage.hbs
@@ -8,13 +8,16 @@
{{/if}}
{{#unless (eq path 'system.attack.')}} {{/unless}}
-
{{/each}}
+
+ {{#if damageOptions.groupAttack}}
+
+ {{localize "DAGGERHEART.ACTIONS.Settings.groupAttack.label"}}
+
+
+
+
+
+
+ {{selectOptions rangeOptions selected=damageOptions.groupAttack.range localize=true}}
+
+
+
+
+
+ {{/if}}
+
{{#unless (empty @root.modifiers)}}
{{localize "DAGGERHEART.GENERAL.Modifier.plural"}}
diff --git a/templates/dialogs/groupRollDialog/footer.hbs b/templates/dialogs/groupRollDialog/footer.hbs
new file mode 100644
index 00000000..cb041247
--- /dev/null
+++ b/templates/dialogs/groupRollDialog/footer.hbs
@@ -0,0 +1,6 @@
+
+
+ {{localize "DAGGERHEART.APPLICATIONS.GroupRollSelect.cancelGroupRoll"}}
+ {{localize "DAGGERHEART.APPLICATIONS.GroupRollSelect.finishGroupRoll"}}
+
+
\ No newline at end of file
diff --git a/templates/dialogs/groupRollDialog/groupRoll.hbs b/templates/dialogs/groupRollDialog/groupRoll.hbs
new file mode 100644
index 00000000..edf1c980
--- /dev/null
+++ b/templates/dialogs/groupRollDialog/groupRoll.hbs
@@ -0,0 +1,20 @@
+
+
+ {{localize "DAGGERHEART.GENERAL.result.single"}}
+
+
+ {{#if hasRolled}}
{{groupRoll.total}} {{groupRoll.totalLabel}} {{/if}}
+
+ {{#if groupRoll.leaderTotal includeZero=true}}{{groupRoll.leaderTotal}}{{else}}{{localize "DAGGERHEART.APPLICATIONS.GroupRollSelect.leaderRoll"}}{{/if}}
+ {{#each groupRoll.modifiers as |modifier|}}
+ {{#if (gte modifier 0)}}+{{else}}-{{/if}}
+ {{positive modifier}}
+ {{/each}}
+ {{#unless groupRoll.modifiers.length}}
+ +
+ {{localize "DAGGERHEART.GENERAL.Modifier.plural"}}
+ {{/unless}}
+
+
+
+
\ No newline at end of file
diff --git a/templates/dialogs/groupRollDialog/groupRollMember.hbs b/templates/dialogs/groupRollDialog/groupRollMember.hbs
new file mode 100644
index 00000000..acf8e8f1
--- /dev/null
+++ b/templates/dialogs/groupRollDialog/groupRollMember.hbs
@@ -0,0 +1,85 @@
+{{#with (lookup members partId)}}
+
+
+
+
+
+
+ {{#if readyToRoll}}
+
+
+ {{localize "DAGGERHEART.GENERAL.roll"}}
+
+
+
+ {{#if roll}}
+
+
{{roll.total}} {{localize "DAGGERHEART.GENERAL.withThing" thing=roll.totalLabel}}
+
+
+ {{roll.dHope.total}}
+
+
+
+
+
+ {{roll.dFear.total}}
+
+
+ {{#if roll.advantage.type}}
+
{{#if (eq roll.advantage.type 1)}}+{{else}}-{{/if}}
+
+ {{roll.advantage.value}}
+
+
+ {{/if}}
+
{{#if (gte roll.modifierTotal 0)}}+{{else}}-{{/if}}
+
{{positive roll.modifierTotal}}
+
+
+ {{else}}
+
{{localize "DAGGERHEART.APPLICATIONS.TagTeamSelect.makeYourRoll"}}
+ {{/if}}
+
+ {{/if}}
+ {{#if hasRolled}}
+
+ {{#if ../isGM}}
+
+ {{/if}}
+
+ {{localize "DAGGERHEART.GENERAL.Modifier.single"}}{{#if successfull}} + 1{{else if (isNullish successfull)}} + ?{{else}} - 1{{/if}}
+
+
+ {{/if}}
+
+
+{{/with}}
\ No newline at end of file
diff --git a/templates/dialogs/groupRollDialog/initialization.hbs b/templates/dialogs/groupRollDialog/initialization.hbs
new file mode 100644
index 00000000..a520b8bd
--- /dev/null
+++ b/templates/dialogs/groupRollDialog/initialization.hbs
@@ -0,0 +1,32 @@
+
\ No newline at end of file
diff --git a/templates/dialogs/groupRollDialog/leader.hbs b/templates/dialogs/groupRollDialog/leader.hbs
new file mode 100644
index 00000000..3d5db3f7
--- /dev/null
+++ b/templates/dialogs/groupRollDialog/leader.hbs
@@ -0,0 +1,73 @@
+
+ {{#with leader}}
+
+
{{localize "DAGGERHEART.APPLICATIONS.GroupRollSelect.leader"}}
+
+
+
+
+
+
+
+
+ {{#if readyToRoll}}
+
+
+ {{localize "DAGGERHEART.GENERAL.roll"}}
+
+
+
+ {{#if roll}}
+
+
{{roll.total}} {{localize "DAGGERHEART.GENERAL.withThing" thing=roll.totalLabel}}
+
+
+ {{roll.dHope.total}}
+
+
+
+
+
+ {{roll.dFear.total}}
+
+
+ {{#if roll.advantage.type}}
+
{{#if (eq roll.advantage.type 1)}}+{{else}}-{{/if}}
+
+ {{roll.advantage.value}}
+
+
+ {{/if}}
+
{{#if (gte roll.modifierTotal 0)}}+{{else}}-{{/if}}
+
{{positive roll.modifierTotal}}
+
+
+ {{else}}
+
{{localize "DAGGERHEART.APPLICATIONS.TagTeamSelect.makeYourRoll"}}
+ {{/if}}
+
+ {{/if}}
+
+
+ {{/with}}
+
\ No newline at end of file
diff --git a/templates/dialogs/tagTeamDialog/initialization.hbs b/templates/dialogs/tagTeamDialog/initialization.hbs
index d25e8f6c..7ccdf566 100644
--- a/templates/dialogs/tagTeamDialog/initialization.hbs
+++ b/templates/dialogs/tagTeamDialog/initialization.hbs
@@ -1,5 +1,4 @@
- {{partId}}
{{localize "DAGGERHEART.APPLICATIONS.TagTeamSelect.selectParticipants"}}
{{#each memberSelection as |member|}}
diff --git a/templates/settings/appearance-settings/diceSoNice.hbs b/templates/settings/appearance-settings/diceSoNice.hbs
index afe7dd5a..6cd4e52e 100644
--- a/templates/settings/appearance-settings/diceSoNice.hbs
+++ b/templates/settings/appearance-settings/diceSoNice.hbs
@@ -9,9 +9,16 @@
{{/if}}
diff --git a/templates/settings/appearance-settings/diceSoNiceTab.hbs b/templates/settings/appearance-settings/diceSoNiceTab.hbs
index a15b63ec..89b587af 100644
--- a/templates/settings/appearance-settings/diceSoNiceTab.hbs
+++ b/templates/settings/appearance-settings/diceSoNiceTab.hbs
@@ -42,9 +42,15 @@
{{#if animations}}
{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.animations"}}
-
+
{{localize "DAGGERHEART.CONFIG.DaggerheartDiceAnimationEvents.higher.name"}}
- {{formInput fields.sfx.fields.higher.fields.class value=values.sfx.higher.class blank="" localize=true}}
+
+ {{formInput fields.sfx.fields.higher.fields.class value=values.sfx.higher.class blank="" localize=true}}
+
+ {{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.muted"}}
+ {{formInput fields.sfx.fields.higher.fields.options.fields.muteSound value=values.sfx.higher.options.muteSound localize=true}}
+
+
{{/if}}
diff --git a/templates/sheets-settings/action-settings/effect.hbs b/templates/sheets-settings/action-settings/effect.hbs
index 1bdd0304..567cb81c 100644
--- a/templates/sheets-settings/action-settings/effect.hbs
+++ b/templates/sheets-settings/action-settings/effect.hbs
@@ -5,7 +5,7 @@
>
{{#if fields.roll}}{{> 'systems/daggerheart/templates/actionTypes/roll.hbs' fields=fields.roll.fields source=source.roll}}{{/if}}
{{#if fields.save}}{{> 'systems/daggerheart/templates/actionTypes/save.hbs' fields=fields.save.fields source=source.save}}{{/if}}
- {{#if fields.damage}}{{> 'systems/daggerheart/templates/actionTypes/damage.hbs' fields=fields.damage.fields.parts.element.fields source=source.damage directField=fields.damage.fields.direct }}{{/if}}
+ {{#if fields.damage}}{{> 'systems/daggerheart/templates/actionTypes/damage.hbs' fields=fields.damage.fields.parts.element.fields source=source.damage baseFields=fields.damage.fields }}{{/if}}
{{#if fields.macro}}{{> 'systems/daggerheart/templates/actionTypes/macro.hbs' fields=fields.macro source=source.macro}}{{/if}}
{{#if fields.effects}}{{> 'systems/daggerheart/templates/actionTypes/effect.hbs' fields=fields.effects.element.fields source=source.effects}}{{/if}}
{{#if fields.beastform}}{{> 'systems/daggerheart/templates/actionTypes/beastform.hbs' fields=fields.beastform.fields source=source.beastform}}{{/if}}
diff --git a/templates/sheets-settings/adversary-settings/attack.hbs b/templates/sheets-settings/adversary-settings/attack.hbs
index f829338f..41960032 100644
--- a/templates/sheets-settings/adversary-settings/attack.hbs
+++ b/templates/sheets-settings/adversary-settings/attack.hbs
@@ -22,5 +22,5 @@
{{formGroup systemFields.criticalThreshold value=document._source.system.criticalThreshold label="DAGGERHEART.ACTIONS.Settings.criticalThreshold" name="system.criticalThreshold" localize=true}}
- {{> 'systems/daggerheart/templates/actionTypes/damage.hbs' fields=systemFields.attack.fields.damage.fields.parts.element.fields source=document.system.attack.damage path="system.attack." directField=systemFields.attack.fields.damage.fields.direct horde=(eq document._source.system.type 'horde')}}
+ {{> 'systems/daggerheart/templates/actionTypes/damage.hbs' fields=systemFields.attack.fields.damage.fields.parts.element.fields source=document.system.attack.damage path="system.attack." baseFields=systemFields.attack.fields.damage.fields horde=(eq document._source.system.type 'horde')}}
\ No newline at end of file
diff --git a/templates/sheets-settings/companion-settings/attack.hbs b/templates/sheets-settings/companion-settings/attack.hbs
index f99f7d8c..41451ef0 100644
--- a/templates/sheets-settings/companion-settings/attack.hbs
+++ b/templates/sheets-settings/companion-settings/attack.hbs
@@ -18,5 +18,5 @@
{{/if}}
{{/if}}
- {{> 'systems/daggerheart/templates/actionTypes/damage.hbs' fields=systemFields.attack.fields.damage.fields.parts.element.fields source=document.system.attack.damage path="system.attack." directField=systemFields.attack.fields.damage.fields.direct}}
+ {{> 'systems/daggerheart/templates/actionTypes/damage.hbs' fields=systemFields.attack.fields.damage.fields.parts.element.fields source=document.system.attack.damage path="system.attack." baseFields=systemFields.attack.fields.damage.fields}}
\ No newline at end of file
diff --git a/templates/sheets/actors/character/header.hbs b/templates/sheets/actors/character/header.hbs
index 06f464fa..4ceba54d 100644
--- a/templates/sheets/actors/character/header.hbs
+++ b/templates/sheets/actors/character/header.hbs
@@ -4,17 +4,27 @@
- {{#if (or document.system.needsCharacterSetup document.system.levelData.canLevelUp)}}
+ {{#if document.system.needsCharacterSetup}}
+ {{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.buttonTitle"}}
+
+ {{else if document.system.levelData.canLevelUp}}
+
{{/if}}
- {{localize 'DAGGERHEART.GENERAL.level'}}
-
+ {{#unless document.system.needsCharacterSetup}}
+ {{localize 'DAGGERHEART.GENERAL.level'}}
+
+ {{/unless}}
diff --git a/templates/sheets/actors/party/header.hbs b/templates/sheets/actors/party/header.hbs
index f39f683f..3fdb137d 100644
--- a/templates/sheets/actors/party/header.hbs
+++ b/templates/sheets/actors/party/header.hbs
@@ -3,7 +3,6 @@
\ No newline at end of file
diff --git a/templates/sheets/actors/party/party-members.hbs b/templates/sheets/actors/party/party-members.hbs
index b3dd53e6..bc0c6672 100644
--- a/templates/sheets/actors/party/party-members.hbs
+++ b/templates/sheets/actors/party/party-members.hbs
@@ -9,33 +9,160 @@
Tag Team Roll
-
+
Group Roll
- {{!-- NOT YET IMPLEMENTED --}}
- {{!--
-
- Help Action
- --}}
+
+
+ {{localize "DAGGERHEART.APPLICATIONS.Downtime.shortRest.title"}}
+
+
+
+ {{localize "DAGGERHEART.APPLICATIONS.Downtime.longRest.title"}}
+
-
- {{localize tabs.partyMembers.label}}
-
- {{#each document.system.partyMembers as |actor id|}}
- {{> 'daggerheart.inventory-item'
- item=actor
- type='character'
- isActor=true
- hideContextMenu=true
- }}
- {{/each}}
-
- {{#unless document.system.partyMembers.length}}
-
- {{localize "DAGGERHEART.GENERAL.dropActorsHere"}}
-
- {{/unless}}
-
-
\ No newline at end of file
+
+ {{#each partyMembers as |member id|}}
+
+
+
+ {{#if member.weapons}}
+
+ {{#each member.weapons as |weapon|}}
+
+ {{/each}}
+
+ {{/if}}
+ {{#if member.evasion includeZero=true}}
+
{{member.evasion}}
+ {{/if}}
+ {{#if member.difficulty includeZero=true}}
+
{{member.difficulty}}
+ {{/if}}
+ {{#unless (eq member.type 'companion')}}
+
+
{{localize "DAGGERHEART.ACTORS.Party.Thresholds.minor"}}
+ {{member.damageThresholds.major}}
+ {{localize "DAGGERHEART.ACTORS.Party.Thresholds.major"}}
+ {{member.damageThresholds.severe}}
+ {{localize "DAGGERHEART.ACTORS.Party.Thresholds.severe"}}
+
+ {{/unless}}
+
+
+
+
+ {{#unless (eq member.type 'companion') }}
+
+
+
+
+
+
+ {{member.resources.hitPoints.value}}
+ /
+ {{member.resources.hitPoints.max}}
+
+
+
+ {{#times member.resources.hitPoints.max}}
+
+
+ {{/times}}
+
+
+ {{/unless}}
+
+
+
+
+
+
+
+ {{member.resources.stress.value}}
+ /
+ {{member.resources.stress.max}}
+
+
+
+ {{#times member.resources.stress.max}}
+
+
+ {{/times}}
+
+
+
+ {{#if member.armorScore.max}}
+
+
+
+
+
+
+ {{member.armorScore.value}}
+ /
+ {{member.armorScore.max}}
+
+
+
+
+ {{/if}}
+
+ {{#if member.traits}}
+
+ {{#each member.traits as |trait|}}
+
+ {{trait.label}}
+ {{trait.value}}
+
+ {{/each}}
+
+ {{/if}}
+
+
+ {{/each}}
+
+ {{#unless document.system.partyMembers.length}}
+
+ {{localize "DAGGERHEART.GENERAL.dropActorsHere"}}
+
+ {{/unless}}
+
diff --git a/templates/sheets/actors/party/resources.hbs b/templates/sheets/actors/party/resources.hbs
deleted file mode 100644
index b53282ca..00000000
--- a/templates/sheets/actors/party/resources.hbs
+++ /dev/null
@@ -1,110 +0,0 @@
-
-
-
-
- {{localize "DAGGERHEART.APPLICATIONS.Downtime.longRest.title"}}
-
-
-
- {{localize "DAGGERHEART.APPLICATIONS.Downtime.shortRest.title"}}
-
-
-
-
- {{localize tabs.resources.label}}
-
- {{#each document.system.partyMembers as |actor id|}}
-
- {{actor.name}}
-
-
- {{#unless (eq actor.type 'companion') }}
-
-
- {{#times actor.system.resources.hitPoints.max}}
-
-
- {{/times}}
-
-
- {{localize "DAGGERHEART.GENERAL.HitPoints.short"}}
- {{actor.system.resources.hitPoints.value}} / {{actor.system.resources.hitPoints.max}}
-
-
- {{/unless}}
-
-
-
- {{#times actor.system.resources.stress.max}}
-
-
- {{/times}}
-
-
- {{localize "DAGGERHEART.GENERAL.stress"}}
- {{actor.system.resources.stress.value}} / {{actor.system.resources.stress.max}}
-
-
-
- {{#if actor.system.armorScore.max}}
-
-
-
- {{localize "DAGGERHEART.GENERAL.armorSlots"}}
- {{actor.system.armorScore.value}} / {{actor.system.armorScore.max}}
-
-
- {{/if}}
-
- {{#unless (or (eq actor.type 'companion') (eq actor.type 'adversary')) }}
-
-
{{localize "DAGGERHEART.GENERAL.hope"}}
- {{#times actor.system.resources.hope.max}}
-
- {{#if (gte actor.system.resources.hope.value (add this 1))}}
-
- {{else}}
-
- {{/if}}
-
- {{/times}}
-
- {{/unless}}
-
- {{#if (eq actor.type 'character')}}
-
-
{{localize "DAGGERHEART.GENERAL.evasion"}}: {{actor.system.evasion}}
-
- {{/if}}
-
- {{#unless (eq actor.type 'companion')}}
-
-
{{localize "DAGGERHEART.GENERAL.DamageThresholds.minor"}}
- {{actor.system.damageThresholds.major}}
- {{localize "DAGGERHEART.GENERAL.DamageThresholds.major"}}
- {{actor.system.damageThresholds.severe}}
- {{localize "DAGGERHEART.GENERAL.DamageThresholds.severe"}}
-
- {{/unless}}
-
-
- {{/each}}
-
-
-
\ No newline at end of file
diff --git a/templates/ui/chat/parts/damage-part.hbs b/templates/ui/chat/parts/damage-part.hbs
index 02519a86..45b09b72 100644
--- a/templates/ui/chat/parts/damage-part.hbs
+++ b/templates/ui/chat/parts/damage-part.hbs
@@ -33,31 +33,32 @@
{{total}}
{{/if}}
- {{#each dice}}
- {{#each results}}
- {{#unless discarded}}
-
-
- {{/unless}}
+ {{/unless}}
+ {{/each}}
{{/each}}
- {{/each}}
- {{#if modifierTotal}}
-
- {{/if}}
- {{#unless dice.length}}
+ {{#if modifierTotal}}
+
+ {{/if}}
+ {{else}}
- {{/unless}}
+ {{/if}}
{{/each}}