mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-18 16:09:03 +01:00
commit
9d0cfdd927
130 changed files with 2460 additions and 1451 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -2,5 +2,6 @@
|
|||
node_modules
|
||||
/packs
|
||||
Build
|
||||
build
|
||||
foundry
|
||||
styles/daggerheart.css
|
||||
styles/daggerheart.css
|
||||
|
|
|
|||
15
README.md
15
README.md
|
|
@ -1,21 +1,26 @@
|
|||
# Daggerheart
|
||||
# Foundryborne Daggerheart
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Overview](#overview)
|
||||
- [User Install Guide](#user-install)
|
||||
- [Documentation](#documentation)
|
||||
- [Developer Setup](#development-setup)
|
||||
- [Contribution Info](#contributing)
|
||||
|
||||
## Overview
|
||||
|
||||
This is a community repo for a Foundry VTT implementation of Daggerheart. It is not associated with Critical Role or Darrington Press.
|
||||
This is the community repo for the Foundry VTT system _Foundryborne_ Daggerheart. It is not associated with Critical Role or Darrington Press.
|
||||
|
||||
## User Install
|
||||
|
||||
1. **(Not Yet Supported - No Releases Yet)** Pasting `https://raw.githubusercontent.com/Foundryborne/daggerheart/refs/heads/main/system.json` into the Install System dialog on the Setup menu of the application.
|
||||
2. **(Not Yet Supported - No Releases Yet)** Browsing the repository's Releases page, where you can copy any system.json link for use in the Install System dialog.
|
||||
3. **(Not Yet Supported - No Releases Yet)** Downloading one of the .zip archives from the Releases page and extracting it into your foundry Data folder, under Data/systems/daggerheart.
|
||||
1. **Recommended** Searching for _Daggerheart_ or _Foundryborne_ in the System Installation dialogue of the FoundryVTT admin settings.
|
||||
2. Pasting `https://raw.githubusercontent.com/Foundryborne/daggerheart/refs/heads/main/system.json` into the Install System dialog on the Setup menu of the application.
|
||||
3. Downloading one of the .zip archives from the Releases page and extracting it into your foundry Data folder, under Data/systems/daggerheart.
|
||||
|
||||
## Documentation
|
||||
|
||||
You can find the documentation here: https://github.com/Foundryborne/daggerheart/wiki
|
||||
|
||||
## Development Setup
|
||||
|
||||
|
|
|
|||
BIN
assets/icons/documents/items/ArcaneWheelchair.webp
Normal file
BIN
assets/icons/documents/items/ArcaneWheelchair.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
BIN
assets/icons/documents/items/HeavyWheelchair.webp
Normal file
BIN
assets/icons/documents/items/HeavyWheelchair.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
assets/icons/documents/items/LightWheelchair.webp
Normal file
BIN
assets/icons/documents/items/LightWheelchair.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
assets/logos/FoundrybornBackgroundLogo.png
Normal file
BIN
assets/logos/FoundrybornBackgroundLogo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 54 KiB |
22
lang/en.json
22
lang/en.json
|
|
@ -272,7 +272,8 @@
|
|||
"combatStarted": "Active",
|
||||
"giveSpotlight": "Give The Spotlight",
|
||||
"requestingSpotlight": "Requesting The Spotlight",
|
||||
"requestSpotlight": "Request The Spotlight"
|
||||
"requestSpotlight": "Request The Spotlight",
|
||||
"openCountdowns": "Countdowns"
|
||||
},
|
||||
"ContextMenu": {
|
||||
"disableEffect": "Disable Effect",
|
||||
|
|
@ -498,6 +499,11 @@
|
|||
"ReactionRoll": {
|
||||
"title": "Reaction Roll: {trait}"
|
||||
},
|
||||
"RerollDialog": {
|
||||
"title": "Reroll",
|
||||
"deselectDiceNotification": "Deselect one of the selected dice first",
|
||||
"acceptCurrentRolls": "Accept Current Rolls"
|
||||
},
|
||||
"ResourceDice": {
|
||||
"title": "{name} Resource",
|
||||
"rerollDice": "Reroll Dice"
|
||||
|
|
@ -505,7 +511,8 @@
|
|||
},
|
||||
"CLASS": {
|
||||
"Feature": {
|
||||
"rallyDice": "Bardic Rally Dice"
|
||||
"rallyDice": "Bardic Rally Dice",
|
||||
"short": "Rally"
|
||||
}
|
||||
},
|
||||
"CONFIG": {
|
||||
|
|
@ -817,8 +824,8 @@
|
|||
"name": "Restrained",
|
||||
"description": "When an effect makes a creature Restrained, it means they cannot move until this condition is cleared.\nThey can still take actions from their current position."
|
||||
},
|
||||
"unconcious": {
|
||||
"name": "Unconcious",
|
||||
"unconscious": {
|
||||
"name": "Unconscious",
|
||||
"description": "Your character can’t move or act while unconscious, they can’t be targeted by an attack."
|
||||
},
|
||||
"vulnerable": {
|
||||
|
|
@ -1757,6 +1764,7 @@
|
|||
"weapon": "Range Increase: Weapon"
|
||||
},
|
||||
"RefreshType": {
|
||||
"scene": "Scene",
|
||||
"session": "Session",
|
||||
"shortrest": "Short Rest",
|
||||
"longrest": "Long Rest"
|
||||
|
|
@ -1892,6 +1900,7 @@
|
|||
"difficulty": "Difficulty",
|
||||
"downtime": "Downtime",
|
||||
"dropActorsHere": "Drop Actors here",
|
||||
"dropFeaturesHere": "Drop Features here",
|
||||
"duality": "Duality",
|
||||
"dualityRoll": "Duality Roll",
|
||||
"enabled": "Enabled",
|
||||
|
|
@ -2259,11 +2268,6 @@
|
|||
"abilityCheckTitle": "{ability} Check"
|
||||
},
|
||||
"featureTitle": "Class Feature",
|
||||
"foundationCard": {
|
||||
"ancestryTitle": "Ancestry Card",
|
||||
"communityTitle": "Community Card",
|
||||
"subclassFeatureTitle": "Subclass Feature"
|
||||
},
|
||||
"healingRoll": {
|
||||
"title": "Heal - {damage}",
|
||||
"heal": "Heal",
|
||||
|
|
|
|||
|
|
@ -359,6 +359,11 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
|
|||
context.community = { ...this.setup.community, compendium: 'communities' };
|
||||
context.class = { ...this.setup.class, compendium: 'classes' };
|
||||
context.subclass = { ...this.setup.subclass, compendium: 'subclasses' };
|
||||
|
||||
const allDomainData = CONFIG.DH.DOMAIN.allDomains();
|
||||
context.classDomains = context.class.uuid
|
||||
? context.class.system.domains.map(key => game.i18n.localize(allDomainData[key].label))
|
||||
: [];
|
||||
context.domainCards = Object.keys(this.setup.domainCards).reduce((acc, x) => {
|
||||
acc[x] = { ...this.setup.domainCards[x], compendium: 'domains' };
|
||||
return acc;
|
||||
|
|
|
|||
|
|
@ -6,5 +6,6 @@ export { default as DeathMove } from './deathMove.mjs';
|
|||
export { default as Downtime } from './downtime.mjs';
|
||||
export { default as MulticlassChoiceDialog } from './multiclassChoiceDialog.mjs';
|
||||
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';
|
||||
|
|
|
|||
|
|
@ -45,6 +45,10 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
return this.config.title;
|
||||
}
|
||||
|
||||
get actor() {
|
||||
return this.config?.data?.parent;
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
header: {
|
||||
|
|
@ -69,9 +73,10 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
icon
|
||||
}));
|
||||
|
||||
this.config.costs ??= [];
|
||||
if (this.config.costs?.length) {
|
||||
const updatedCosts = game.system.api.fields.ActionFields.CostField.calcCosts.call(
|
||||
this.action,
|
||||
this.action ?? { actor: this.actor },
|
||||
this.config.costs
|
||||
);
|
||||
context.costs = updatedCosts.map(x => ({
|
||||
|
|
@ -80,7 +85,10 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
? this.action.parent.parent.name
|
||||
: game.i18n.localize(CONFIG.DH.GENERAL.abilityCosts[x.key].label)
|
||||
}));
|
||||
context.canRoll = game.system.api.fields.ActionFields.CostField.hasCost.call(this.action, updatedCosts);
|
||||
context.canRoll = game.system.api.fields.ActionFields.CostField.hasCost.call(
|
||||
this.action ?? { actor: this.actor },
|
||||
updatedCosts
|
||||
);
|
||||
this.config.data.scale = this.config.costs[0].total;
|
||||
}
|
||||
if (this.config.uses?.max) {
|
||||
|
|
@ -143,6 +151,20 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
this.config.experiences.indexOf(button.dataset.key) > -1
|
||||
? this.config.experiences.filter(x => x !== button.dataset.key)
|
||||
: [...this.config.experiences, button.dataset.key];
|
||||
if (this.config?.data?.parent?.type === 'character' || this.config?.data?.parent?.type === 'companion') {
|
||||
this.config.costs =
|
||||
this.config.costs.indexOf(this.config.costs.find(c => c.extKey === button.dataset.key)) > -1
|
||||
? this.config.costs.filter(x => x.extKey !== button.dataset.key)
|
||||
: [
|
||||
...this.config.costs,
|
||||
{
|
||||
extKey: button.dataset.key,
|
||||
key: 'hope',
|
||||
value: 1,
|
||||
name: this.config.data?.experiences?.[button.dataset.key]?.name
|
||||
}
|
||||
];
|
||||
}
|
||||
this.render();
|
||||
}
|
||||
|
||||
|
|
@ -152,8 +174,8 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
this.config.roll.type = this.reactionOverride
|
||||
? CONFIG.DH.ITEM.actionTypes.reaction.id
|
||||
: this.config.roll.type === CONFIG.DH.ITEM.actionTypes.reaction.id
|
||||
? null
|
||||
: this.config.roll.type;
|
||||
? null
|
||||
: this.config.roll.type;
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,12 +56,14 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
|
|||
label,
|
||||
icon
|
||||
}));
|
||||
context.modifiers = this.config.modifiers;
|
||||
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.selectedRollMode = rest.selectedRollMode;
|
||||
|
||||
this.render();
|
||||
|
|
|
|||
279
module/applications/dialogs/rerollDamageDialog.mjs
Normal file
279
module/applications/dialogs/rerollDamageDialog.mjs
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
|
||||
export default class RerollDamageDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor(message, options = {}) {
|
||||
super(options);
|
||||
|
||||
this.message = message;
|
||||
this.damage = Object.keys(message.system.damage).reduce((acc, typeKey) => {
|
||||
const type = message.system.damage[typeKey];
|
||||
acc[typeKey] = Object.keys(type.parts).reduce((acc, partKey) => {
|
||||
const part = type.parts[partKey];
|
||||
acc[partKey] = Object.keys(part.dice).reduce((acc, diceKey) => {
|
||||
const dice = part.dice[diceKey];
|
||||
const activeResults = dice.results.filter(x => x.active);
|
||||
acc[diceKey] = {
|
||||
dice: dice.dice,
|
||||
selectedResults: activeResults.length,
|
||||
maxSelected: activeResults.length,
|
||||
results: activeResults.map(x => ({ ...x, selected: true }))
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
id: 'reroll-dialog',
|
||||
classes: ['daggerheart', 'dialog', 'dh-style', 'views', 'reroll-dialog'],
|
||||
window: {
|
||||
icon: 'fa-solid fa-dice'
|
||||
},
|
||||
actions: {
|
||||
toggleResult: RerollDamageDialog.#toggleResult,
|
||||
selectRoll: RerollDamageDialog.#selectRoll,
|
||||
doReroll: RerollDamageDialog.#doReroll,
|
||||
save: RerollDamageDialog.#save
|
||||
}
|
||||
};
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: {
|
||||
id: 'main',
|
||||
template: 'systems/daggerheart/templates/dialogs/rerollDialog/damage/main.hbs'
|
||||
},
|
||||
footer: {
|
||||
id: 'footer',
|
||||
template: 'systems/daggerheart/templates/dialogs/rerollDialog/footer.hbs'
|
||||
}
|
||||
};
|
||||
|
||||
get title() {
|
||||
return game.i18n.localize('DAGGERHEART.APPLICATIONS.RerollDialog.damageTitle');
|
||||
}
|
||||
|
||||
_attachPartListeners(partId, htmlElement, options) {
|
||||
super._attachPartListeners(partId, htmlElement, options);
|
||||
|
||||
htmlElement.querySelectorAll('.to-reroll-input').forEach(element => {
|
||||
element.addEventListener('change', this.toggleDice.bind(this));
|
||||
});
|
||||
}
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.damage = this.damage;
|
||||
context.disabledReroll = !this.getRerollDice().length;
|
||||
context.saveDisabled = !this.isSelectionDone();
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static async #save() {
|
||||
const update = {
|
||||
'system.damage': Object.keys(this.damage).reduce((acc, typeKey) => {
|
||||
const type = this.damage[typeKey];
|
||||
let typeTotal = 0;
|
||||
const messageType = this.message.system.damage[typeKey];
|
||||
const parts = Object.keys(type).map(partKey => {
|
||||
const part = type[partKey];
|
||||
const messagePart = messageType.parts[partKey];
|
||||
let partTotal = messagePart.modifierTotal;
|
||||
const dice = Object.keys(part).map(diceKey => {
|
||||
const dice = part[diceKey];
|
||||
const total = dice.results.reduce((acc, result) => {
|
||||
if (result.active) acc += result.result;
|
||||
return acc;
|
||||
}, 0);
|
||||
partTotal += total;
|
||||
const messageDice = messagePart.dice[diceKey];
|
||||
return {
|
||||
...messageDice,
|
||||
total: total,
|
||||
results: dice.results.map(x => ({
|
||||
...x,
|
||||
hasRerolls: dice.results.length > 1
|
||||
}))
|
||||
};
|
||||
});
|
||||
|
||||
typeTotal += partTotal;
|
||||
return {
|
||||
...messagePart,
|
||||
total: partTotal,
|
||||
dice: dice
|
||||
};
|
||||
});
|
||||
|
||||
acc[typeKey] = {
|
||||
...messageType,
|
||||
total: typeTotal,
|
||||
parts: parts
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, {})
|
||||
};
|
||||
await this.message.update(update);
|
||||
await this.close();
|
||||
}
|
||||
|
||||
getRerollDice() {
|
||||
const rerollDice = [];
|
||||
Object.keys(this.damage).forEach(typeKey => {
|
||||
const type = this.damage[typeKey];
|
||||
Object.keys(type).forEach(partKey => {
|
||||
const part = type[partKey];
|
||||
Object.keys(part).forEach(diceKey => {
|
||||
const dice = part[diceKey];
|
||||
Object.keys(dice.results).forEach(resultKey => {
|
||||
const result = dice.results[resultKey];
|
||||
if (result.toReroll) {
|
||||
rerollDice.push({
|
||||
...result,
|
||||
dice: dice.dice,
|
||||
type: typeKey,
|
||||
part: partKey,
|
||||
dice: diceKey,
|
||||
result: resultKey
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return rerollDice;
|
||||
}
|
||||
|
||||
isSelectionDone() {
|
||||
const diceFinishedData = [];
|
||||
Object.keys(this.damage).forEach(typeKey => {
|
||||
const type = this.damage[typeKey];
|
||||
Object.keys(type).forEach(partKey => {
|
||||
const part = type[partKey];
|
||||
Object.keys(part).forEach(diceKey => {
|
||||
const dice = part[diceKey];
|
||||
const selected = dice.results.reduce((acc, result) => acc + (result.active ? 1 : 0), 0);
|
||||
diceFinishedData.push(selected === dice.maxSelected);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return diceFinishedData.every(x => x);
|
||||
}
|
||||
|
||||
toggleDice(event) {
|
||||
const target = event.target;
|
||||
const { type, part, dice } = target.dataset;
|
||||
const toggleDice = this.damage[type][part][dice];
|
||||
|
||||
const existingDiceRerolls = this.getRerollDice().filter(
|
||||
x => x.type === type && x.part === part && x.dice === dice
|
||||
);
|
||||
|
||||
const allRerolled = existingDiceRerolls.length === toggleDice.results.filter(x => x.active).length;
|
||||
|
||||
toggleDice.toReroll = !allRerolled;
|
||||
toggleDice.results.forEach(result => {
|
||||
if (result.active) {
|
||||
result.toReroll = !allRerolled;
|
||||
}
|
||||
});
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static #toggleResult(event) {
|
||||
event.stopPropagation();
|
||||
|
||||
const target = event.target.closest('.to-reroll-result');
|
||||
const { type, part, dice, result } = target.dataset;
|
||||
const toggleDice = this.damage[type][part][dice];
|
||||
const toggleResult = toggleDice.results[result];
|
||||
toggleResult.toReroll = !toggleResult.toReroll;
|
||||
|
||||
const existingDiceRerolls = this.getRerollDice().filter(
|
||||
x => x.type === type && x.part === part && x.dice === dice
|
||||
);
|
||||
|
||||
const allToReroll = existingDiceRerolls.length === toggleDice.results.filter(x => x.active).length;
|
||||
toggleDice.toReroll = allToReroll;
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async #selectRoll(_, button) {
|
||||
const { type, part, dice, result } = button.dataset;
|
||||
|
||||
const diceVal = this.damage[type][part][dice];
|
||||
const diceResult = diceVal.results[result];
|
||||
if (!diceResult.active && diceVal.results.filter(x => x.active).length === diceVal.maxSelected) {
|
||||
return ui.notifications.warn(
|
||||
game.i18n.localize('DAGGERHEART.APPLICATIONS.RerollDialog.deselectDiceNotification')
|
||||
);
|
||||
}
|
||||
|
||||
if (diceResult.active) {
|
||||
diceVal.toReroll = false;
|
||||
diceResult.toReroll = false;
|
||||
}
|
||||
|
||||
diceVal.selectedResults += diceResult.active ? -1 : 1;
|
||||
diceResult.active = !diceResult.active;
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async #doReroll() {
|
||||
const toReroll = this.getRerollDice().map(x => {
|
||||
const { type, part, dice, result } = x;
|
||||
const diceData = this.damage[type][part][dice].results[result];
|
||||
return {
|
||||
...diceData,
|
||||
dice: this.damage[type][part][dice].dice,
|
||||
typeKey: type,
|
||||
partKey: part,
|
||||
diceKey: dice,
|
||||
resultsIndex: result
|
||||
};
|
||||
});
|
||||
|
||||
const roll = await new Roll(toReroll.map(x => `1${x.dice}`).join(' + ')).evaluate();
|
||||
|
||||
if (game.modules.get('dice-so-nice')?.active) {
|
||||
const diceSoNiceRoll = {
|
||||
_evaluated: true,
|
||||
dice: roll.dice,
|
||||
options: { appearance: {} }
|
||||
};
|
||||
|
||||
await game.dice3d.showForRoll(diceSoNiceRoll, game.user, true);
|
||||
}
|
||||
|
||||
toReroll.forEach((data, index) => {
|
||||
const { typeKey, partKey, diceKey, resultsIndex } = data;
|
||||
const rerolledDice = roll.dice[index];
|
||||
|
||||
const dice = this.damage[typeKey][partKey][diceKey];
|
||||
dice.toReroll = false;
|
||||
dice.results[resultsIndex].active = false;
|
||||
dice.results[resultsIndex].discarded = true;
|
||||
dice.results[resultsIndex].toReroll = false;
|
||||
dice.results.splice(dice.results.length, 0, {
|
||||
...rerolledDice.results[0],
|
||||
toReroll: false,
|
||||
selected: true
|
||||
});
|
||||
});
|
||||
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
279
module/applications/dialogs/rerollDialog.mjs
Normal file
279
module/applications/dialogs/rerollDialog.mjs
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
|
||||
export default class RerollDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor(message, options = {}) {
|
||||
super(options);
|
||||
|
||||
this.message = message;
|
||||
this.damage = Object.keys(message.system.damage).reduce((acc, typeKey) => {
|
||||
const type = message.system.damage[typeKey];
|
||||
acc[typeKey] = Object.keys(type.parts).reduce((acc, partKey) => {
|
||||
const part = type.parts[partKey];
|
||||
acc[partKey] = Object.keys(part.dice).reduce((acc, diceKey) => {
|
||||
const dice = part.dice[diceKey];
|
||||
const activeResults = dice.results.filter(x => x.active);
|
||||
acc[diceKey] = {
|
||||
dice: dice.dice,
|
||||
selectedResults: activeResults.length,
|
||||
maxSelected: activeResults.length,
|
||||
results: activeResults.map(x => ({ ...x, selected: true }))
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
id: 'reroll-dialog',
|
||||
classes: ['daggerheart', 'dialog', 'dh-style', 'views', 'reroll-dialog'],
|
||||
window: {
|
||||
icon: 'fa-solid fa-dice'
|
||||
},
|
||||
actions: {
|
||||
toggleResult: RerollDialog.#toggleResult,
|
||||
selectRoll: RerollDialog.#selectRoll,
|
||||
doReroll: RerollDialog.#doReroll,
|
||||
save: RerollDialog.#save
|
||||
}
|
||||
};
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: {
|
||||
id: 'main',
|
||||
template: 'systems/daggerheart/templates/dialogs/rerollDialog/main.hbs'
|
||||
},
|
||||
footer: {
|
||||
id: 'footer',
|
||||
template: 'systems/daggerheart/templates/dialogs/rerollDialog/footer.hbs'
|
||||
}
|
||||
};
|
||||
|
||||
get title() {
|
||||
return game.i18n.localize('DAGGERHEART.APPLICATIONS.RerollDialog.title');
|
||||
}
|
||||
|
||||
_attachPartListeners(partId, htmlElement, options) {
|
||||
super._attachPartListeners(partId, htmlElement, options);
|
||||
|
||||
htmlElement.querySelectorAll('.to-reroll-input').forEach(element => {
|
||||
element.addEventListener('change', this.toggleDice.bind(this));
|
||||
});
|
||||
}
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.damage = this.damage;
|
||||
context.disabledReroll = !this.getRerollDice().length;
|
||||
context.saveDisabled = !this.isSelectionDone();
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static async #save() {
|
||||
const update = {
|
||||
'system.damage': Object.keys(this.damage).reduce((acc, typeKey) => {
|
||||
const type = this.damage[typeKey];
|
||||
let typeTotal = 0;
|
||||
const messageType = this.message.system.damage[typeKey];
|
||||
const parts = Object.keys(type).map(partKey => {
|
||||
const part = type[partKey];
|
||||
const messagePart = messageType.parts[partKey];
|
||||
let partTotal = messagePart.modifierTotal;
|
||||
const dice = Object.keys(part).map(diceKey => {
|
||||
const dice = part[diceKey];
|
||||
const total = dice.results.reduce((acc, result) => {
|
||||
if (result.active) acc += result.result;
|
||||
return acc;
|
||||
}, 0);
|
||||
partTotal += total;
|
||||
const messageDice = messagePart.dice[diceKey];
|
||||
return {
|
||||
...messageDice,
|
||||
total: total,
|
||||
results: dice.results.map(x => ({
|
||||
...x,
|
||||
hasRerolls: dice.results.length > 1
|
||||
}))
|
||||
};
|
||||
});
|
||||
|
||||
typeTotal += partTotal;
|
||||
return {
|
||||
...messagePart,
|
||||
total: partTotal,
|
||||
dice: dice
|
||||
};
|
||||
});
|
||||
|
||||
acc[typeKey] = {
|
||||
...messageType,
|
||||
total: typeTotal,
|
||||
parts: parts
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, {})
|
||||
};
|
||||
await this.message.update(update);
|
||||
await this.close();
|
||||
}
|
||||
|
||||
getRerollDice() {
|
||||
const rerollDice = [];
|
||||
Object.keys(this.damage).forEach(typeKey => {
|
||||
const type = this.damage[typeKey];
|
||||
Object.keys(type).forEach(partKey => {
|
||||
const part = type[partKey];
|
||||
Object.keys(part).forEach(diceKey => {
|
||||
const dice = part[diceKey];
|
||||
Object.keys(dice.results).forEach(resultKey => {
|
||||
const result = dice.results[resultKey];
|
||||
if (result.toReroll) {
|
||||
rerollDice.push({
|
||||
...result,
|
||||
dice: dice.dice,
|
||||
type: typeKey,
|
||||
part: partKey,
|
||||
dice: diceKey,
|
||||
result: resultKey
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return rerollDice;
|
||||
}
|
||||
|
||||
isSelectionDone() {
|
||||
const diceFinishedData = [];
|
||||
Object.keys(this.damage).forEach(typeKey => {
|
||||
const type = this.damage[typeKey];
|
||||
Object.keys(type).forEach(partKey => {
|
||||
const part = type[partKey];
|
||||
Object.keys(part).forEach(diceKey => {
|
||||
const dice = part[diceKey];
|
||||
const selected = dice.results.reduce((acc, result) => acc + (result.active ? 1 : 0), 0);
|
||||
diceFinishedData.push(selected === dice.maxSelected);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return diceFinishedData.every(x => x);
|
||||
}
|
||||
|
||||
toggleDice(event) {
|
||||
const target = event.target;
|
||||
const { type, part, dice } = target.dataset;
|
||||
const toggleDice = this.damage[type][part][dice];
|
||||
|
||||
const existingDiceRerolls = this.getRerollDice().filter(
|
||||
x => x.type === type && x.part === part && x.dice === dice
|
||||
);
|
||||
|
||||
const allRerolled = existingDiceRerolls.length === toggleDice.results.filter(x => x.active).length;
|
||||
|
||||
toggleDice.toReroll = !allRerolled;
|
||||
toggleDice.results.forEach(result => {
|
||||
if (result.active) {
|
||||
result.toReroll = !allRerolled;
|
||||
}
|
||||
});
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static #toggleResult(event) {
|
||||
event.stopPropagation();
|
||||
|
||||
const target = event.target.closest('.to-reroll-result');
|
||||
const { type, part, dice, result } = target.dataset;
|
||||
const toggleDice = this.damage[type][part][dice];
|
||||
const toggleResult = toggleDice.results[result];
|
||||
toggleResult.toReroll = !toggleResult.toReroll;
|
||||
|
||||
const existingDiceRerolls = this.getRerollDice().filter(
|
||||
x => x.type === type && x.part === part && x.dice === dice
|
||||
);
|
||||
|
||||
const allToReroll = existingDiceRerolls.length === toggleDice.results.length;
|
||||
toggleDice.toReroll = allToReroll;
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async #selectRoll(_, button) {
|
||||
const { type, part, dice, result } = button.dataset;
|
||||
|
||||
const diceVal = this.damage[type][part][dice];
|
||||
const diceResult = diceVal.results[result];
|
||||
if (!diceResult.active && diceVal.results.filter(x => x.active).length === diceVal.maxSelected) {
|
||||
return ui.notifications.warn(
|
||||
game.i18n.localize('DAGGERHEART.APPLICATIONS.RerollDialog.deselectDiceNotification')
|
||||
);
|
||||
}
|
||||
|
||||
if (diceResult.active) {
|
||||
diceVal.toReroll = false;
|
||||
diceResult.toReroll = false;
|
||||
}
|
||||
|
||||
diceVal.selectedResults += diceResult.active ? -1 : 1;
|
||||
diceResult.active = !diceResult.active;
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async #doReroll() {
|
||||
const toReroll = this.getRerollDice().map(x => {
|
||||
const { type, part, dice, result } = x;
|
||||
const diceData = this.damage[type][part][dice].results[result];
|
||||
return {
|
||||
...diceData,
|
||||
dice: this.damage[type][part][dice].dice,
|
||||
typeKey: type,
|
||||
partKey: part,
|
||||
diceKey: dice,
|
||||
resultsIndex: result
|
||||
};
|
||||
});
|
||||
|
||||
const roll = await new Roll(toReroll.map(x => `1${x.dice}`).join(' + ')).evaluate();
|
||||
|
||||
if (game.modules.get('dice-so-nice')?.active) {
|
||||
const diceSoNiceRoll = {
|
||||
_evaluated: true,
|
||||
dice: roll.dice,
|
||||
options: { appearance: {} }
|
||||
};
|
||||
|
||||
await game.dice3d.showForRoll(diceSoNiceRoll, game.user, true);
|
||||
}
|
||||
|
||||
toReroll.forEach((data, index) => {
|
||||
const { typeKey, partKey, diceKey, resultsIndex } = data;
|
||||
const rerolledDice = roll.dice[index];
|
||||
|
||||
const dice = this.damage[typeKey][partKey][diceKey];
|
||||
dice.toReroll = false;
|
||||
dice.results[resultsIndex].active = false;
|
||||
dice.results[resultsIndex].discarded = true;
|
||||
dice.results[resultsIndex].toReroll = false;
|
||||
dice.results.splice(dice.results.length, 0, {
|
||||
...rerolledDice.results[0],
|
||||
toReroll: false,
|
||||
selected: true
|
||||
});
|
||||
});
|
||||
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
|
@ -51,7 +51,7 @@ export default class DhCharacterLevelUp extends LevelUpBase {
|
|||
.filter(exp => exp.data.length > 0)
|
||||
.flatMap(exp =>
|
||||
exp.data.map(data => {
|
||||
const experience = Object.keys(this.actor.system.experiences).find(x => x === data);
|
||||
const experience = Object.keys(this.actor.system.experiences)[data];
|
||||
return this.actor.system.experiences[experience].name;
|
||||
})
|
||||
);
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ export default class DhCompanionLevelUp extends BaseLevelUp {
|
|||
.filter(exp => exp.data.length > 0)
|
||||
.flatMap(exp =>
|
||||
exp.data.map(data => {
|
||||
const experience = Object.keys(this.actor.system.experiences).find(x => x === data);
|
||||
const experience = Object.keys(this.actor.system.experiences)[data];
|
||||
return this.actor.system.experiences[experience].name;
|
||||
})
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import DhAppearance from '../../data/settings/Appearance.mjs';
|
||||
import { getDiceSoNicePreset } from '../../config/generalConfig.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
|
|
@ -25,7 +26,8 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
|
|||
},
|
||||
actions: {
|
||||
reset: this.reset,
|
||||
save: this.save
|
||||
save: this.save,
|
||||
preview: this.preview
|
||||
},
|
||||
form: { handler: this.updateData, submitOnChange: true }
|
||||
};
|
||||
|
|
@ -89,6 +91,22 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
|
|||
this.render();
|
||||
}
|
||||
|
||||
static async preview() {
|
||||
const source = this.settings._source.diceSoNice[this.tabGroups.diceSoNice];
|
||||
let faces = 'd12';
|
||||
switch (this.tabGroups.diceSoNice) {
|
||||
case 'advantage':
|
||||
case 'disadvantage':
|
||||
faces = 'd6';
|
||||
}
|
||||
const preset = await getDiceSoNicePreset(source, faces);
|
||||
const diceSoNiceRoll = await new Roll(`1${faces}`).evaluate();
|
||||
diceSoNiceRoll.dice[0].options.appearance = preset.appearance;
|
||||
diceSoNiceRoll.dice[0].options.modelFile = preset.modelFile;
|
||||
|
||||
await game.dice3d.showForRoll(diceSoNiceRoll, game.user, false);
|
||||
}
|
||||
|
||||
static async reset() {
|
||||
this.settings = new DhAppearance();
|
||||
this.render();
|
||||
|
|
|
|||
|
|
@ -98,11 +98,17 @@ export default class DHAdversarySettings extends DHBaseActorSettings {
|
|||
|
||||
async _onDrop(event) {
|
||||
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
|
||||
if (data.fromInternal) return;
|
||||
|
||||
|
||||
const item = await fromUuid(data.uuid);
|
||||
if (item.type === 'feature') {
|
||||
await this.actor.createEmbeddedDocuments('Item', [item]);
|
||||
if (item?.type === 'feature') {
|
||||
if (data.fromInternal && item.parent?.uuid === this.actor.uuid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const itemData = item.toObject();
|
||||
delete itemData._id;
|
||||
|
||||
await this.actor.createEmbeddedDocuments('Item', [itemData]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -631,13 +631,33 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
},
|
||||
hasRoll: true
|
||||
};
|
||||
this.document.diceRoll({
|
||||
const result = await this.document.diceRoll({
|
||||
...config,
|
||||
headerTitle: `${game.i18n.localize('DAGGERHEART.GENERAL.dualityRoll')}: ${this.actor.name}`,
|
||||
title: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
|
||||
ability: abilityLabel
|
||||
})
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
this.consumeResource(result?.costs);
|
||||
}, 50);
|
||||
}
|
||||
|
||||
async consumeResource(costs) {
|
||||
if (!costs?.length) return;
|
||||
const usefulResources = foundry.utils.deepClone(this.actor.system.resources);
|
||||
const resources = game.system.api.fields.ActionFields.CostField.getRealCosts(costs).map(c => {
|
||||
const resource = usefulResources[c.key];
|
||||
return {
|
||||
key: c.key,
|
||||
value: (c.total ?? c.value) * (resource.isReversed ? 1 : -1),
|
||||
target: resource.target,
|
||||
keyIsID: resource.keyIsID
|
||||
};
|
||||
});
|
||||
|
||||
await this.actor.modifyResource(resources);
|
||||
}
|
||||
|
||||
//TODO: redo toggleEquipItem method
|
||||
|
|
|
|||
|
|
@ -51,9 +51,8 @@ import { ItemBrowser } from '../../ui/itemBrowser.mjs';
|
|||
*/
|
||||
|
||||
/**
|
||||
* @template {Constructor<foundry.applications.api.DocumentSheet>} BaseDocumentSheet
|
||||
* @param {BaseDocumentSheet} Base - The base class to extend.
|
||||
* @returns {BaseDocumentSheet}
|
||||
* @template {new (...args: any[]) => {}} T
|
||||
* @arg Base {T}
|
||||
*/
|
||||
export default function DHApplicationMixin(Base) {
|
||||
class DHSheetV2 extends HandlebarsApplicationMixin(Base) {
|
||||
|
|
@ -123,12 +122,14 @@ export default function DHApplicationMixin(Base) {
|
|||
super._attachPartListeners(partId, htmlElement, options);
|
||||
this._dragDrop.forEach(d => d.bind(htmlElement));
|
||||
}
|
||||
|
||||
/**@inheritdoc */
|
||||
async _onFirstRender(context, options) {
|
||||
await super._onFirstRender(context, options);
|
||||
|
||||
const docs = [];
|
||||
for (var docData of this.relatedDocs) {
|
||||
for (const docData of this.relatedDocs) {
|
||||
if (!docData) continue;
|
||||
const doc = await foundry.utils.fromUuid(docData.uuid);
|
||||
docs.push(doc);
|
||||
}
|
||||
|
|
@ -247,6 +248,9 @@ export default function DHApplicationMixin(Base) {
|
|||
/* Context Menu */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Create all configured context menus for this application ins tance.
|
||||
*/
|
||||
_createContextMenus() {
|
||||
for (const config of this.options.contextMenus) {
|
||||
const { handler, selector, options } = config;
|
||||
|
|
@ -257,9 +261,9 @@ export default function DHApplicationMixin(Base) {
|
|||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the set of ContextMenu options for DomainCards.
|
||||
* Get the set of ContextMenu options for ActiveEffects.
|
||||
* @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance
|
||||
* @this {CharacterSheet}
|
||||
* @this {DHSheetV2}
|
||||
* @protected
|
||||
*/
|
||||
static #getEffectContextOptions() {
|
||||
|
|
@ -305,8 +309,13 @@ export default function DHApplicationMixin(Base) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the set of ContextMenu options.
|
||||
* @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance
|
||||
* Get the common ContextMenu options for an element.
|
||||
* @param {Object} options
|
||||
* @param {boolean} [options.usable=false] - Whether to include an option to use the item or apply damage.
|
||||
* @param {boolean} [options.toChat=false] - Whether to include an option to send the item to chat.
|
||||
* @param {boolean} [options.deletable=true] - Whether to include an option to delete the item.
|
||||
*
|
||||
* @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]}
|
||||
*/
|
||||
_getContextMenuCommonOptions({ usable = false, toChat = false, deletable = true }) {
|
||||
const options = [
|
||||
|
|
@ -325,7 +334,7 @@ export default function DHApplicationMixin(Base) {
|
|||
}
|
||||
];
|
||||
|
||||
if (usable)
|
||||
if (usable) {
|
||||
options.unshift({
|
||||
name: 'DAGGERHEART.GENERAL.damage',
|
||||
icon: 'fa-solid fa-explosion',
|
||||
|
|
@ -340,15 +349,16 @@ export default function DHApplicationMixin(Base) {
|
|||
}
|
||||
});
|
||||
|
||||
options.unshift({
|
||||
name: 'DAGGERHEART.APPLICATIONS.ContextMenu.useItem',
|
||||
icon: 'fa-solid fa-burst',
|
||||
condition: target => {
|
||||
const doc = getDocFromElementSync(target);
|
||||
return doc && !(doc.type === 'domainCard' && doc.system.inVault);
|
||||
},
|
||||
callback: async (target, event) => (await getDocFromElement(target)).use(event)
|
||||
});
|
||||
options.unshift({
|
||||
name: 'DAGGERHEART.APPLICATIONS.ContextMenu.useItem',
|
||||
icon: 'fa-solid fa-burst',
|
||||
condition: target => {
|
||||
const doc = getDocFromElementSync(target);
|
||||
return doc && !(doc.type === 'domainCard' && doc.system.inVault);
|
||||
},
|
||||
callback: async (target, event) => (await getDocFromElement(target)).use(event)
|
||||
});
|
||||
}
|
||||
|
||||
if (toChat)
|
||||
options.push({
|
||||
|
|
@ -410,7 +420,7 @@ export default function DHApplicationMixin(Base) {
|
|||
: this.document.system.actions?.get(actionId);
|
||||
if (!doc) return;
|
||||
|
||||
const description = doc.system?.description ?? doc.description;
|
||||
const description = game.i18n.localize(doc.system?.description ?? doc.description);
|
||||
const isAction = !!actionId;
|
||||
descriptionElement.innerHTML = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||
description,
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@ const { ActorSheetV2 } = foundry.applications.sheets;
|
|||
|
||||
/**
|
||||
* A base actor sheet extending {@link ActorSheetV2} via {@link DHApplicationMixin}
|
||||
* @extends ActorSheetV2
|
||||
* @mixes DHSheetV2
|
||||
*/
|
||||
export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
||||
/** @inheritDoc */
|
||||
|
|
@ -106,7 +104,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
|||
/**
|
||||
* Get the set of ContextMenu options for Features.
|
||||
* @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance
|
||||
* @this {DHSheetV2}
|
||||
* @this {DHBaseActorSheet}
|
||||
* @protected
|
||||
*/
|
||||
static #getFeatureContextOptions() {
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@ const { ItemSheetV2 } = foundry.applications.sheets;
|
|||
|
||||
/**
|
||||
* A base item sheet extending {@link ItemSheetV2} via {@link DHApplicationMixin}
|
||||
* @extends ItemSheetV2
|
||||
* @mixes DHSheetV2
|
||||
*/
|
||||
export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
||||
/** @inheritDoc */
|
||||
|
|
@ -108,7 +106,7 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
|||
/**
|
||||
* Get the set of ContextMenu options for Features.
|
||||
* @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance
|
||||
* @this {DHSheetV2}
|
||||
* @this {DHBaseItemSheet}
|
||||
* @protected
|
||||
*/
|
||||
static #getFeatureContextOptions() {
|
||||
|
|
@ -183,12 +181,18 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
|||
static async #deleteFeature(_, element) {
|
||||
const target = element.closest('[data-item-uuid]');
|
||||
const feature = await getDocFromElement(target);
|
||||
if (!feature) return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureIsMissing'));
|
||||
await this.document.update({
|
||||
'system.features': this.document.system.features
|
||||
.filter(x => target.dataset.type !== x.type || x.item.uuid !== feature.uuid)
|
||||
.map(x => ({ ...x, item: x.item.uuid }))
|
||||
});
|
||||
if (!feature) {
|
||||
await this.document.update({
|
||||
'system.features': this.document.system.features
|
||||
.filter(x => x.item)
|
||||
.map(x => ({ ...x, item: x.item.uuid }))
|
||||
});
|
||||
} else
|
||||
await this.document.update({
|
||||
'system.features': this.document.system.features
|
||||
.filter(x => target.dataset.type !== x.type || x.item.uuid !== feature.uuid)
|
||||
.map(x => ({ ...x, item: x.item.uuid }))
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -261,21 +265,45 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
|||
if (data.fromInternal) return;
|
||||
|
||||
const target = event.target.closest('fieldset.drop-section');
|
||||
const item = await fromUuid(data.uuid);
|
||||
let item = await fromUuid(data.uuid);
|
||||
if (item?.type === 'feature') {
|
||||
const cls = foundry.documents.Item.implementation;
|
||||
|
||||
if (this.document.parent?.type === 'character') {
|
||||
const itemData = item.toObject();
|
||||
item = await cls.create(
|
||||
{
|
||||
...itemData,
|
||||
system: {
|
||||
...itemData.system,
|
||||
originItemType: this.document.type,
|
||||
originId: this.document.id,
|
||||
identifier: this.document.system.isMulticlass ? 'multiclass' : null
|
||||
}
|
||||
},
|
||||
{ parent: this.document.parent }
|
||||
);
|
||||
}
|
||||
|
||||
if (target.dataset.type) {
|
||||
await this.document.update({
|
||||
'system.features': [...this.document.system.features, { type: target.dataset.type, item }].map(
|
||||
x => ({
|
||||
...x,
|
||||
item: x.item?.uuid
|
||||
})
|
||||
)
|
||||
});
|
||||
await this.document.update(
|
||||
{
|
||||
'system.features': [...this.document.system.features, { type: target.dataset.type, item }].map(
|
||||
x => ({
|
||||
...x,
|
||||
item: x.item?.uuid
|
||||
})
|
||||
)
|
||||
},
|
||||
{ parent: this.document.parent?.type === 'character' ? this.document.parent : undefined }
|
||||
);
|
||||
} else {
|
||||
await this.document.update({
|
||||
'system.features': [...this.document.system.features, item].map(x => x.uuid)
|
||||
});
|
||||
await this.document.update(
|
||||
{
|
||||
'system.features': [...this.document.system.features, item].map(x => x.uuid)
|
||||
},
|
||||
{ parent: this.document.parent?.type === 'character' ? this.document.parent : undefined }
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,40 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
classes: ['daggerheart']
|
||||
};
|
||||
|
||||
_getEntryContextOptions() {
|
||||
return [
|
||||
...super._getEntryContextOptions(),
|
||||
// {
|
||||
// name: 'Reroll',
|
||||
// icon: '<i class="fa-solid fa-dice"></i>',
|
||||
// condition: li => {
|
||||
// const message = game.messages.get(li.dataset.messageId);
|
||||
|
||||
// return (game.user.isGM || message.isAuthor) && message.rolls.length > 0;
|
||||
// },
|
||||
// callback: li => {
|
||||
// const message = game.messages.get(li.dataset.messageId);
|
||||
// new game.system.api.applications.dialogs.RerollDialog(message).render({ force: true });
|
||||
// }
|
||||
// },
|
||||
{
|
||||
name: 'Reroll Damage',
|
||||
icon: '<i class="fa-solid fa-dice"></i>',
|
||||
condition: li => {
|
||||
const message = game.messages.get(li.dataset.messageId);
|
||||
const hasRolledDamage = message.system.hasDamage
|
||||
? Object.keys(message.system.damage).length > 0
|
||||
: false;
|
||||
return (game.user.isGM || message.isAuthor) && hasRolledDamage;
|
||||
},
|
||||
callback: li => {
|
||||
const message = game.messages.get(li.dataset.messageId);
|
||||
new game.system.api.applications.dialogs.RerollDamageDialog(message).render({ force: true });
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
addChatListeners = async (app, html, data) => {
|
||||
html.querySelectorAll('.duality-action-damage').forEach(element =>
|
||||
element.addEventListener('click', event => this.onRollDamage(event, data.message))
|
||||
|
|
@ -33,14 +67,6 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
html.querySelectorAll('.simple-roll-button').forEach(element =>
|
||||
element.addEventListener('click', event => this.onRollSimple(event, data.message))
|
||||
);
|
||||
html.querySelectorAll('.target-container').forEach(element => {
|
||||
element.addEventListener('mouseenter', this.hoverTarget);
|
||||
element.addEventListener('mouseleave', this.unhoverTarget);
|
||||
element.addEventListener('click', this.clickTarget);
|
||||
});
|
||||
html.querySelectorAll('.button-target-selection').forEach(element => {
|
||||
element.addEventListener('click', event => this.onTargetSelection(event, data.message));
|
||||
});
|
||||
html.querySelectorAll('.healing-button').forEach(element =>
|
||||
element.addEventListener('click', event => this.onHealing(event, data.message))
|
||||
);
|
||||
|
|
@ -138,33 +164,6 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
});
|
||||
}
|
||||
|
||||
onTargetSelection(event, message) {
|
||||
event.stopPropagation();
|
||||
const msg = ui.chat.collection.get(message._id);
|
||||
msg.system.targetMode = Boolean(event.target.dataset.targetHit);
|
||||
}
|
||||
|
||||
hoverTarget(event) {
|
||||
event.stopPropagation();
|
||||
const token = canvas.tokens.get(event.currentTarget.dataset.token);
|
||||
if (!token?.controlled) token._onHoverIn(event, { hoverOutOthers: true });
|
||||
}
|
||||
|
||||
unhoverTarget(event) {
|
||||
const token = canvas.tokens.get(event.currentTarget.dataset.token);
|
||||
if (!token?.controlled) token._onHoverOut(event);
|
||||
}
|
||||
|
||||
clickTarget(event) {
|
||||
event.stopPropagation();
|
||||
const token = canvas.tokens.get(event.currentTarget.dataset.token);
|
||||
if (!token) {
|
||||
ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.attackTargetDoesNotExist'));
|
||||
return;
|
||||
}
|
||||
game.canvas.pan(token);
|
||||
}
|
||||
|
||||
async onRollSimple(event, message) {
|
||||
const buttonType = event.target.dataset.type ?? 'damage',
|
||||
total = message.rolls.reduce((a, c) => a + Roll.fromJSON(c).total, 0),
|
||||
|
|
@ -228,19 +227,28 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
}
|
||||
|
||||
const target = event.target.closest('[data-die-index]');
|
||||
let originalRoll_parsed = message.rolls.map(roll => JSON.parse(roll))[0];
|
||||
const rollClass =
|
||||
game.system.api.dice[
|
||||
message.type === 'dualityRoll' ? 'DualityRoll' : target.dataset.type === 'damage' ? 'DHRoll' : 'D20Roll'
|
||||
];
|
||||
|
||||
if (!game.modules.get('dice-so-nice')?.active) foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice });
|
||||
if (target.dataset.type === 'damage') {
|
||||
game.system.api.dice.DamageRoll.reroll(target, message);
|
||||
} else {
|
||||
let originalRoll_parsed = message.rolls.map(roll => JSON.parse(roll))[0];
|
||||
const rollClass =
|
||||
game.system.api.dice[
|
||||
message.type === 'dualityRoll'
|
||||
? 'DualityRoll'
|
||||
: target.dataset.type === 'damage'
|
||||
? 'DHRoll'
|
||||
: 'D20Roll'
|
||||
];
|
||||
|
||||
const { newRoll, parsedRoll } = await rollClass.reroll(originalRoll_parsed, target, message);
|
||||
if (!game.modules.get('dice-so-nice')?.active) foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice });
|
||||
|
||||
await game.messages.get(message._id).update({
|
||||
'system.roll': newRoll,
|
||||
'rolls': [parsedRoll]
|
||||
});
|
||||
const { newRoll, parsedRoll } = await rollClass.reroll(originalRoll_parsed, target, message);
|
||||
|
||||
await game.messages.get(message._id).update({
|
||||
'system.roll': newRoll,
|
||||
'rolls': [parsedRoll]
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
this.config = CONFIG.DH.ITEMBROWSER.compendiumConfig;
|
||||
this.presets = options.presets;
|
||||
|
||||
if(this.presets?.compendium && this.presets?.folder)
|
||||
if (this.presets?.compendium && this.presets?.folder)
|
||||
ItemBrowser.selectFolder.call(this, null, null, this.presets.compendium, this.presets.folder);
|
||||
}
|
||||
|
||||
|
|
@ -26,7 +26,6 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
id: 'itemBrowser',
|
||||
classes: ['daggerheart', 'dh-style', 'dialog', 'compendium-browser'],
|
||||
tag: 'div',
|
||||
// title: 'Item Browser',
|
||||
window: {
|
||||
frame: true,
|
||||
title: 'Compendium Browser',
|
||||
|
|
@ -41,9 +40,8 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
sortList: this.sortList
|
||||
},
|
||||
position: {
|
||||
top: 330,
|
||||
left: 120,
|
||||
width: 800,
|
||||
left: 100,
|
||||
width: 850,
|
||||
height: 600
|
||||
}
|
||||
};
|
||||
|
|
@ -88,16 +86,14 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
|
||||
/** @inheritDoc */
|
||||
async _preFirstRender(context, options) {
|
||||
if(context.presets?.render?.noFolder || context.presets?.render?.lite)
|
||||
options.position.width = 600;
|
||||
|
||||
if (context.presets?.render?.noFolder || context.presets?.render?.lite) options.position.width = 600;
|
||||
|
||||
await super._preFirstRender(context, options);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
async _preRender(context, options) {
|
||||
|
||||
if(context.presets?.render?.noFolder || context.presets?.render?.lite)
|
||||
if (context.presets?.render?.noFolder || context.presets?.render?.lite)
|
||||
options.parts.splice(options.parts.indexOf('sidebar'), 1);
|
||||
|
||||
await super._preRender(context, options);
|
||||
|
|
@ -110,18 +106,17 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
this._createSearchFilter();
|
||||
this._createFilterInputs();
|
||||
this._createDragProcess();
|
||||
|
||||
if(context.presets?.render?.lite)
|
||||
this.element.classList.add('lite');
|
||||
|
||||
if(context.presets?.render?.noFolder)
|
||||
this.element.classList.add('no-folder');
|
||||
|
||||
if(context.presets?.render?.noFilter)
|
||||
this.element.classList.add('no-filter');
|
||||
|
||||
if(this.presets?.filter) {
|
||||
Object.entries(this.presets.filter).forEach(([k,v]) => this.fieldFilter.find(c => c.name === k).value = v.value);
|
||||
if (context.presets?.render?.lite) this.element.classList.add('lite');
|
||||
|
||||
if (context.presets?.render?.noFolder) this.element.classList.add('no-folder');
|
||||
|
||||
if (context.presets?.render?.noFilter) this.element.classList.add('no-filter');
|
||||
|
||||
if (this.presets?.filter) {
|
||||
Object.entries(this.presets.filter).forEach(
|
||||
([k, v]) => (this.fieldFilter.find(c => c.name === k).value = v.value)
|
||||
);
|
||||
await this._onInputFilterBrowser();
|
||||
}
|
||||
}
|
||||
|
|
@ -198,6 +193,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
|
||||
formatLabel(item, field) {
|
||||
const property = foundry.utils.getProperty(item, field.key);
|
||||
if (Array.isArray(property)) property.join(', ');
|
||||
if (typeof field.format !== 'function') return property ?? '-';
|
||||
return field.format(property);
|
||||
}
|
||||
|
|
@ -315,19 +311,18 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
async _onInputFilterBrowser(event) {
|
||||
this.#filteredItems.browser.input.clear();
|
||||
|
||||
if(event) this.fieldFilter.find(f => f.name === event.target.name).value = event.target.value;
|
||||
if (event) this.fieldFilter.find(f => f.name === event.target.name).value = event.target.value;
|
||||
|
||||
for (const li of this.element.querySelectorAll('.item-container')) {
|
||||
const itemUUID = li.dataset.itemUuid,
|
||||
item = this.items.find(i => i.uuid === itemUUID);
|
||||
|
||||
if(!item) continue;
|
||||
|
||||
if (!item) continue;
|
||||
|
||||
const matchesMenu =
|
||||
this.fieldFilter.length === 0 ||
|
||||
this.fieldFilter.every(f => (
|
||||
!f.value && f.value !== false) ||
|
||||
ItemBrowser.evaluateFilter(item, this.createFilterData(f))
|
||||
this.fieldFilter.every(
|
||||
f => (!f.value && f.value !== false) || ItemBrowser.evaluateFilter(item, this.createFilterData(f))
|
||||
);
|
||||
if (matchesMenu) this.#filteredItems.browser.input.add(item.id);
|
||||
|
||||
|
|
@ -335,21 +330,21 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
li.hidden = !(search.has(item.id) && matchesMenu);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Foundry evaluateFilter doesn't allow you to match if filter values are included into item data
|
||||
* @param {*} obj
|
||||
* @param {*} filter
|
||||
* @param {*} obj
|
||||
* @param {*} filter
|
||||
*/
|
||||
static evaluateFilter(obj, filter) {
|
||||
let docValue = foundry.utils.getProperty(obj, filter.field);
|
||||
let filterValue = filter.value;
|
||||
switch (filter.operator) {
|
||||
case "contains2":
|
||||
case 'contains2':
|
||||
filterValue = Array.isArray(filterValue) ? filterValue : [filterValue];
|
||||
docValue = Array.isArray(docValue) ? docValue : [docValue];
|
||||
return docValue.some(dv => filterValue.includes(dv));
|
||||
case "contains3":
|
||||
case 'contains3':
|
||||
return docValue.some(f => f.value === filterValue);
|
||||
default:
|
||||
return foundry.applications.ux.SearchFilter.evaluateFilter(obj, filter);
|
||||
|
|
@ -373,30 +368,33 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
this.render({ force: true });
|
||||
}
|
||||
|
||||
static getFolderConfig(folder, property = "columns") {
|
||||
if(!folder) return [];
|
||||
static getFolderConfig(folder, property = 'columns') {
|
||||
if (!folder) return [];
|
||||
return folder[property] ?? CONFIG.DH.ITEMBROWSER.typeConfig[folder.listType]?.[property] ?? [];
|
||||
}
|
||||
|
||||
static sortList(_, target) {
|
||||
const key = target.dataset.sortKey,
|
||||
type = !target.dataset.sortType || target.dataset.sortType === "DESC" ? "ASC" : "DESC",
|
||||
itemListContainer = target.closest(".compendium-results").querySelector(".item-list"),
|
||||
itemList = itemListContainer.querySelectorAll(".item-container");
|
||||
type = !target.dataset.sortType || target.dataset.sortType === 'DESC' ? 'ASC' : 'DESC',
|
||||
itemListContainer = target.closest('.compendium-results').querySelector('.item-list'),
|
||||
itemList = itemListContainer.querySelectorAll('.item-container');
|
||||
|
||||
target.closest(".item-list-header").querySelectorAll('[data-sort-key]').forEach(b => b.dataset.sortType = "");
|
||||
target
|
||||
.closest('.item-list-header')
|
||||
.querySelectorAll('[data-sort-key]')
|
||||
.forEach(b => (b.dataset.sortType = ''));
|
||||
target.dataset.sortType = type;
|
||||
|
||||
|
||||
const newOrder = [...itemList].reverse().sort((a, b) => {
|
||||
const aProp = a.querySelector(`[data-item-key="${key}"]`),
|
||||
bProp = b.querySelector(`[data-item-key="${key}"]`)
|
||||
if(type === "DESC") {
|
||||
bProp = b.querySelector(`[data-item-key="${key}"]`);
|
||||
if (type === 'DESC') {
|
||||
return aProp.innerText < bProp.innerText ? 1 : -1;
|
||||
} else {
|
||||
return aProp.innerText > bProp.innerText ? 1 : -1;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
itemListContainer.replaceChildren(...newOrder);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -183,11 +183,11 @@ export const conditions = {
|
|||
icon: 'icons/magic/control/debuff-chains-shackle-movement-red.webp',
|
||||
description: 'DAGGERHEART.CONFIG.Condition.restrained.description'
|
||||
},
|
||||
unconcious: {
|
||||
id: 'unconcious',
|
||||
name: 'DAGGERHEART.CONFIG.Condition.unconcious.name',
|
||||
unconscious: {
|
||||
id: 'unconscious',
|
||||
name: 'DAGGERHEART.CONFIG.Condition.unconscious.name',
|
||||
icon: 'icons/magic/control/sleep-bubble-purple.webp',
|
||||
description: 'DAGGERHEART.CONFIG.Condition.unconcious.description'
|
||||
description: 'DAGGERHEART.CONFIG.Condition.unconscious.description'
|
||||
},
|
||||
dead: {
|
||||
id: 'dead',
|
||||
|
|
@ -494,44 +494,49 @@ export const diceSetNumbers = {
|
|||
flat: 'Flat'
|
||||
};
|
||||
|
||||
export const getDiceSoNicePresets = async (hopeFaces, fearFaces, advantageFaces = 'd6', disadvantageFaces = 'd6') => {
|
||||
const { diceSoNice } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance);
|
||||
const getPreset = async (type, faces) => {
|
||||
const system = game.dice3d.DiceFactory.systems.get(type.system).dice.get(faces);
|
||||
if (!system) {
|
||||
ui.notifications.error(
|
||||
game.i18n.format('DAGGERHEART.UI.Notifications.noDiceSystem', {
|
||||
system: game.dice3d.DiceFactory.systems.get(type.system).name,
|
||||
faces: faces
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
export const getDiceSoNicePreset = async (type, faces) => {
|
||||
const system = game.dice3d.DiceFactory.systems.get(type.system).dice.get(faces);
|
||||
if (!system) {
|
||||
ui.notifications.error(
|
||||
game.i18n.format('DAGGERHEART.UI.Notifications.noDiceSystem', {
|
||||
system: game.dice3d.DiceFactory.systems.get(type.system).name,
|
||||
faces: faces
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (system.modelFile && !system.modelLoaded) {
|
||||
await system.loadModel(game.dice3d.DiceFactory.loaderGLTF);
|
||||
} else {
|
||||
await system.loadTextures();
|
||||
}
|
||||
|
||||
return {
|
||||
modelFile: system.modelFile,
|
||||
appearance: {
|
||||
...system.appearance,
|
||||
...type
|
||||
}
|
||||
};
|
||||
};
|
||||
if (system.modelFile && !system.modelLoaded) {
|
||||
await system.loadModel(game.dice3d.DiceFactory.loaderGLTF);
|
||||
} else {
|
||||
await system.loadTextures();
|
||||
}
|
||||
|
||||
return {
|
||||
hope: await getPreset(diceSoNice.hope, hopeFaces),
|
||||
fear: await getPreset(diceSoNice.fear, fearFaces),
|
||||
advantage: await getPreset(diceSoNice.advantage, advantageFaces),
|
||||
disadvantage: await getPreset(diceSoNice.disadvantage, disadvantageFaces)
|
||||
modelFile: system.modelFile,
|
||||
appearance: {
|
||||
...system.appearance,
|
||||
...type
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const getDiceSoNicePresets = async (hopeFaces, fearFaces, advantageFaces = 'd6', disadvantageFaces = 'd6') => {
|
||||
const { diceSoNice } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance);
|
||||
|
||||
return {
|
||||
hope: await getDiceSoNicePreset(diceSoNice.hope, hopeFaces),
|
||||
fear: await getDiceSoNicePreset(diceSoNice.fear, fearFaces),
|
||||
advantage: await getDiceSoNicePreset(diceSoNice.advantage, advantageFaces),
|
||||
disadvantage: await getDiceSoNicePreset(diceSoNice.disadvantage, disadvantageFaces)
|
||||
};
|
||||
};
|
||||
|
||||
export const refreshTypes = {
|
||||
scene: {
|
||||
id: 'session',
|
||||
label: 'DAGGERHEART.GENERAL.RefreshType.scene'
|
||||
},
|
||||
session: {
|
||||
id: 'session',
|
||||
label: 'DAGGERHEART.GENERAL.RefreshType.session'
|
||||
|
|
|
|||
|
|
@ -780,7 +780,15 @@ export const weaponFeatures = {
|
|||
mode: 2,
|
||||
value: '1'
|
||||
}
|
||||
]
|
||||
],
|
||||
system: {
|
||||
rangeDependence: {
|
||||
enabled: true,
|
||||
range: 'melee',
|
||||
target: 'hostile',
|
||||
type: 'withinRange'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -1040,16 +1048,6 @@ export const weaponFeatures = {
|
|||
key: 'system.evasion',
|
||||
mode: 2,
|
||||
value: '-1'
|
||||
},
|
||||
{
|
||||
key: 'system.bonuses.damage.primaryWeapon.extraDice',
|
||||
mode: 2,
|
||||
value: '1'
|
||||
},
|
||||
{
|
||||
key: 'system.rules.weapon.dropLowestDamageDice',
|
||||
mode: 5,
|
||||
value: '1'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1089,7 +1087,15 @@ export const weaponFeatures = {
|
|||
mode: 2,
|
||||
value: 'ITEM.@system.tier + 1'
|
||||
}
|
||||
]
|
||||
],
|
||||
system: {
|
||||
rangeDependence: {
|
||||
enabled: true,
|
||||
range: 'melee',
|
||||
target: 'hostile',
|
||||
type: 'withinRange'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -1166,18 +1172,7 @@ export const weaponFeatures = {
|
|||
name: 'DAGGERHEART.CONFIG.WeaponFeature.powerful.effects.powerful.name',
|
||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.powerful.effects.powerful.description',
|
||||
img: 'icons/magic/control/buff-flight-wings-runes-red-yellow.webp',
|
||||
changes: [
|
||||
{
|
||||
key: 'system.bonuses.damage.primaryWeapon.extraDice',
|
||||
mode: 2,
|
||||
value: '1'
|
||||
},
|
||||
{
|
||||
key: 'system.rules.weapon.dropLowestDamageDice',
|
||||
mode: 5,
|
||||
value: '1'
|
||||
}
|
||||
]
|
||||
changes: []
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -1229,7 +1224,7 @@ export const weaponFeatures = {
|
|||
img: 'icons/skills/melee/strike-sword-slashing-red.webp',
|
||||
changes: [
|
||||
{
|
||||
key: 'system.bonuses.roll.primaryWeapon.attack',
|
||||
key: 'system.bonuses.roll.primaryWeapon.bonus',
|
||||
mode: 2,
|
||||
value: 1
|
||||
}
|
||||
|
|
@ -1301,13 +1296,7 @@ export const weaponFeatures = {
|
|||
name: 'DAGGERHEART.CONFIG.WeaponFeature.selfCorrecting.effects.selfCorrecting.name',
|
||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.selfCorrecting.effects.selfCorrecting.description',
|
||||
img: 'icons/weapons/ammunition/arrow-broadhead-glowing-orange.webp',
|
||||
changes: [
|
||||
{
|
||||
key: 'system.rules.damage.flipMinDiceValue',
|
||||
mode: 5,
|
||||
value: 1
|
||||
}
|
||||
]
|
||||
changes: []
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -1319,13 +1308,7 @@ export const weaponFeatures = {
|
|||
name: 'DAGGERHEART.CONFIG.WeaponFeature.serrated.effects.serrated.name',
|
||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.serrated.effects.serrated.description',
|
||||
img: 'icons/weapons/ammunition/arrow-broadhead-glowing-orange.webp',
|
||||
changes: [
|
||||
{
|
||||
key: 'system.rules.damage.flipMinDiceValue',
|
||||
mode: 5,
|
||||
value: 1
|
||||
}
|
||||
]
|
||||
changes: []
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -115,7 +115,6 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
if (!this.actor) throw new Error("An Action can't be used outside of an Actor context.");
|
||||
|
||||
if (this.chatDisplay) await this.toChat();
|
||||
|
||||
let { byPassRoll } = options,
|
||||
config = this.prepareConfig(event, byPassRoll);
|
||||
for (let i = 0; i < this.constructor.extraSchemas.length; i++) {
|
||||
|
|
@ -145,9 +144,8 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
if (this.rollDamage && this.damage.parts.length) await this.rollDamage(event, config);
|
||||
else if (this.trigger) await this.trigger(event, config);
|
||||
else if (this.hasSave || this.hasEffect) {
|
||||
const roll = new Roll('');
|
||||
const roll = new CONFIG.Dice.daggerheart.DHRoll('');
|
||||
roll._evaluated = true;
|
||||
if (this.hasTarget) config.targetSelection = config.targets.length > 0;
|
||||
await CONFIG.Dice.daggerheart.DHRoll.toMessage(roll, config);
|
||||
}
|
||||
}
|
||||
|
|
@ -180,7 +178,6 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
hasHealing: this.damage?.parts?.length && this.type === 'healing',
|
||||
hasEffect: !!this.effects?.length,
|
||||
hasSave: this.hasSave,
|
||||
hasTarget: true,
|
||||
selectedRollMode: game.settings.get('core', 'rollMode'),
|
||||
isFastForward: event.shiftKey,
|
||||
data: this.getRollData(),
|
||||
|
|
@ -211,7 +208,14 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
}
|
||||
|
||||
async consume(config, successCost = false) {
|
||||
const usefulResources = foundry.utils.deepClone(this.actor.system.resources);
|
||||
const usefulResources = {
|
||||
...foundry.utils.deepClone(this.actor.system.resources),
|
||||
fear: {
|
||||
value: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear),
|
||||
max: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxFear,
|
||||
reversed: false
|
||||
}
|
||||
};
|
||||
|
||||
for (var cost of config.costs) {
|
||||
if (cost.keyIsID) {
|
||||
|
|
@ -223,24 +227,26 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
}
|
||||
}
|
||||
|
||||
const resources = config.costs
|
||||
const resources = game.system.api.fields.ActionFields.CostField.getRealCosts(config.costs)
|
||||
.filter(
|
||||
c =>
|
||||
c.enabled !== false &&
|
||||
((!successCost && (!c.consumeOnSuccess || config.roll?.success)) ||
|
||||
(successCost && c.consumeOnSuccess))
|
||||
(!successCost && (!c.consumeOnSuccess || config.roll?.success)) ||
|
||||
(successCost && c.consumeOnSuccess)
|
||||
)
|
||||
.map(c => {
|
||||
.reduce((a, c) => {
|
||||
const resource = usefulResources[c.key];
|
||||
return {
|
||||
key: c.key,
|
||||
value: (c.total ?? c.value) * (resource.isReversed ? 1 : -1),
|
||||
target: resource.target,
|
||||
keyIsID: resource.keyIsID
|
||||
};
|
||||
});
|
||||
if (resource) {
|
||||
a.push({
|
||||
key: c.key,
|
||||
value: (c.total ?? c.value) * (resource.isReversed ? 1 : -1),
|
||||
target: resource.target,
|
||||
keyIsID: resource.keyIsID
|
||||
});
|
||||
return a;
|
||||
}
|
||||
}, []);
|
||||
|
||||
await this.actor.modifyResource(resources);
|
||||
await (this.actor.system.partner ?? this.actor).modifyResource(resources);
|
||||
if (
|
||||
config.uses?.enabled &&
|
||||
((!successCost && (!config.uses?.consumeOnSuccess || config.roll?.success)) ||
|
||||
|
|
@ -248,8 +254,11 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
)
|
||||
this.update({ 'uses.value': this.uses.value + 1 });
|
||||
|
||||
if (config.roll?.success || successCost)
|
||||
(config.message ?? config.parent).update({ 'system.successConsumed': true });
|
||||
if (config.roll?.success || successCost) {
|
||||
setTimeout(() => {
|
||||
(config.message ?? config.parent).update({ 'system.successConsumed': true });
|
||||
}, 50);
|
||||
}
|
||||
}
|
||||
/* */
|
||||
|
||||
|
|
@ -368,15 +377,15 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
|
||||
async updateChatMessage(message, targetId, changes, chain = true) {
|
||||
setTimeout(async () => {
|
||||
const chatMessage = ui.chat.collection.get(message._id),
|
||||
msgTarget =
|
||||
chatMessage.system.targets.find(mt => mt.id === targetId) ??
|
||||
chatMessage.system.oldTargets.find(mt => mt.id === targetId);
|
||||
msgTarget.saved = changes;
|
||||
const chatMessage = ui.chat.collection.get(message._id);
|
||||
|
||||
await chatMessage.update({
|
||||
system: {
|
||||
targets: chatMessage.system.targets,
|
||||
oldTargets: chatMessage.system.oldTargets
|
||||
flags: {
|
||||
[game.system.id]: {
|
||||
reactionRolls: {
|
||||
[targetId]: changes
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
|
|
|
|||
|
|
@ -49,8 +49,7 @@ export default class DHDamageAction extends DHBaseAction {
|
|||
...systemData,
|
||||
roll: formulas,
|
||||
dialog: {},
|
||||
data: this.getRollData(),
|
||||
targetSelection: systemData.targets.length > 0
|
||||
data: this.getRollData()
|
||||
};
|
||||
if (this.hasSave) config.onSave = this.save.damageMod;
|
||||
if (data.system) {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ export default class BeastformEffect extends BaseEffect {
|
|||
|
||||
if (this.parent.parent?.type === 'character') {
|
||||
this.parent.parent.system.primaryWeapon?.update?.({ 'system.equipped': false });
|
||||
this.parent.parent.system.secondayWeapon?.update?.({ 'system.equipped': false });
|
||||
this.parent.parent.system.secondaryWeapon?.update?.({ 'system.equipped': false });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -287,18 +287,6 @@ export default class DhCharacter extends BaseDataActor {
|
|||
})
|
||||
})
|
||||
}),
|
||||
weapon: new fields.SchemaField({
|
||||
/* Unimplemented
|
||||
-> Should remove the lowest damage dice from weapon damage
|
||||
-> Reflect this in the chat message somehow so players get feedback that their choice is helping them.
|
||||
*/
|
||||
dropLowestDamageDice: new fields.BooleanField({ initial: false }),
|
||||
/* Unimplemented
|
||||
-> Should flip any lowest possible dice rolls for weapon damage to highest
|
||||
-> Reflect this in the chat message somehow so players get feedback that their choice is helping them.
|
||||
*/
|
||||
flipMinDiceValue: new fields.BooleanField({ intial: false })
|
||||
}),
|
||||
runeWard: new fields.BooleanField({ initial: false }),
|
||||
burden: new fields.SchemaField({
|
||||
ignore: new fields.BooleanField()
|
||||
|
|
@ -363,6 +351,17 @@ export default class DhCharacter extends BaseDataActor {
|
|||
return [...classDomains, ...multiclassDomains];
|
||||
}
|
||||
|
||||
get domainData() {
|
||||
const allDomainData = CONFIG.DH.DOMAIN.allDomains();
|
||||
return this.domains.map(key => {
|
||||
const domain = allDomainData[key];
|
||||
return {
|
||||
...domain,
|
||||
label: game.i18n.localize(domain.label)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
get domainCards() {
|
||||
const domainCards = this.parent.items.filter(x => x.type === 'domainCard');
|
||||
const loadout = domainCards.filter(x => !x.system.inVault);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ export default class DHAbilityUse extends foundry.abstract.TypeDataModel {
|
|||
const fields = foundry.data.fields;
|
||||
|
||||
return {
|
||||
title: new fields.StringField({}),
|
||||
origin: new fields.StringField({}),
|
||||
img: new fields.StringField({}),
|
||||
name: new fields.StringField({}),
|
||||
|
|
|
|||
|
|
@ -18,15 +18,11 @@ const targetsField = () =>
|
|||
);
|
||||
|
||||
export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
||||
targetHook = null;
|
||||
|
||||
static defineSchema() {
|
||||
return {
|
||||
title: new fields.StringField(),
|
||||
roll: new fields.ObjectField(),
|
||||
targets: targetsField(),
|
||||
oldTargets: targetsField(),
|
||||
targetSelection: new fields.BooleanField({ initial: false }),
|
||||
hasRoll: new fields.BooleanField({ initial: false }),
|
||||
hasDamage: new fields.BooleanField({ initial: false }),
|
||||
hasHealing: new fields.BooleanField({ initial: false }),
|
||||
|
|
@ -63,66 +59,59 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
|||
return actionItem.system.actionsList?.find(a => a.id === this.source.action);
|
||||
}
|
||||
|
||||
get messageTemplate() {
|
||||
return 'systems/daggerheart/templates/ui/chat/roll.hbs';
|
||||
}
|
||||
|
||||
get targetMode() {
|
||||
return this.targetSelection;
|
||||
return this.parent.targetSelection;
|
||||
}
|
||||
|
||||
set targetMode(mode) {
|
||||
this.targetSelection = mode;
|
||||
this.updateTargets();
|
||||
if (!this.parent.isAuthor) return;
|
||||
this.parent.targetSelection = mode;
|
||||
this.registerTargetHook();
|
||||
this.parent.update({
|
||||
system: {
|
||||
targetSelection: this.targetSelection,
|
||||
oldTargets: this.oldTargets
|
||||
this.updateTargets();
|
||||
}
|
||||
|
||||
get hitTargets() {
|
||||
return this.currentTargets.filter(t => t.hit || !this.hasRoll || !this.targetMode);
|
||||
}
|
||||
|
||||
async updateTargets() {
|
||||
if (!ui.chat.collection.get(this.parent.id)) return;
|
||||
let targets;
|
||||
if (this.targetMode) targets = this.targets;
|
||||
else
|
||||
targets = Array.from(game.user.targets).map(t =>
|
||||
game.system.api.fields.ActionFields.TargetField.formatTarget(t)
|
||||
);
|
||||
|
||||
await this.parent.update({
|
||||
flags: {
|
||||
[game.system.id]: {
|
||||
targets: targets,
|
||||
targetMode: this.targetMode
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
get hitTargets() {
|
||||
return this.currentTargets.filter(t => t.hit || !this.hasRoll || !this.targetSelection);
|
||||
}
|
||||
|
||||
async updateTargets() {
|
||||
this.currentTargets = this.getTargetList();
|
||||
if (!this.targetSelection) {
|
||||
this.currentTargets.forEach(ct => {
|
||||
if (this.targets.find(t => t.actorId === ct.actorId)) return;
|
||||
const indexTarget = this.oldTargets.findIndex(ot => ot.actorId === ct.actorId);
|
||||
if (indexTarget === -1) this.oldTargets.push(ct);
|
||||
});
|
||||
if (this.hasSave) this.setPendingSaves();
|
||||
if (this.currentTargets.length) {
|
||||
if (!this.parent._id) return;
|
||||
const updates = await this.parent.update({
|
||||
system: {
|
||||
oldTargets: this.oldTargets
|
||||
}
|
||||
});
|
||||
if (!updates && ui.chat.collection.get(this.parent.id)) ui.chat.updateMessage(this.parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
registerTargetHook() {
|
||||
if (this.targetSelection && this.targetHook !== null) {
|
||||
Hooks.off('targetToken', this.targetHook);
|
||||
this.targetHook = null;
|
||||
} else if (!this.targetSelection && this.targetHook === null) {
|
||||
this.targetHook = Hooks.on('targetToken', foundry.utils.debounce(this.updateTargets.bind(this), 50));
|
||||
if (!this.parent.isAuthor) return;
|
||||
if (this.targetMode && this.parent.targetHook !== null) {
|
||||
Hooks.off('targetToken', this.parent.targetHook);
|
||||
return (this.parent.targetHook = null);
|
||||
} else if (!this.targetMode && this.parent.targetHook === null) {
|
||||
return (this.parent.targetHook = Hooks.on(
|
||||
'targetToken',
|
||||
foundry.utils.debounce(this.updateTargets.bind(this), 50)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
prepareDerivedData() {
|
||||
if (this.hasTarget) {
|
||||
this.hasHitTarget = this.targets.filter(t => t.hit === true).length > 0;
|
||||
this.updateTargets();
|
||||
this.registerTargetHook();
|
||||
if (this.targetSelection === true) {
|
||||
this.currentTargets = this.getTargetList();
|
||||
|
||||
if (this.targetMode === true && this.hasRoll) {
|
||||
this.targetShort = this.targets.reduce(
|
||||
(a, c) => {
|
||||
if (c.hit) a.hit += 1;
|
||||
|
|
@ -136,23 +125,28 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
|||
}
|
||||
|
||||
this.canViewSecret = this.parent.speakerActor?.testUserPermission(game.user, 'OBSERVER');
|
||||
this.canButtonApply = game.user.isGM;
|
||||
}
|
||||
|
||||
getTargetList() {
|
||||
return this.targetSelection !== true
|
||||
? Array.from(game.user.targets).map(t => {
|
||||
const target = game.system.api.fields.ActionFields.TargetField.formatTarget(t),
|
||||
oldTarget =
|
||||
this.targets.find(ot => ot.actorId === target.actorId) ??
|
||||
this.oldTargets.find(ot => ot.actorId === target.actorId);
|
||||
if (oldTarget) return oldTarget;
|
||||
return target;
|
||||
})
|
||||
: this.targets;
|
||||
const targets =
|
||||
this.targetMode && this.parent.isAuthor
|
||||
? this.targets
|
||||
: (this.parent.getFlag(game.system.id, 'targets') ?? this.targets),
|
||||
reactionRolls = this.parent.getFlag(game.system.id, 'reactionRolls');
|
||||
|
||||
if (reactionRolls) {
|
||||
Object.entries(reactionRolls).forEach(([k, r]) => {
|
||||
const target = targets.find(t => t.id === k);
|
||||
if (target) target.saved = r;
|
||||
});
|
||||
}
|
||||
|
||||
return targets;
|
||||
}
|
||||
|
||||
setPendingSaves() {
|
||||
this.pendingSaves = this.targetSelection
|
||||
this.pendingSaves = this.targetMode
|
||||
? this.targets.filter(target => target.hit && target.saved.success === null).length > 0
|
||||
: this.currentTargets.filter(target => target.saved.success === null).length > 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,10 @@ export default class CostField extends fields.ArrayField {
|
|||
value: new fields.NumberField({ nullable: true, initial: 1, min: 0 }),
|
||||
scalable: new fields.BooleanField({ initial: false }),
|
||||
step: new fields.NumberField({ nullable: true, initial: null }),
|
||||
consumeOnSuccess: new fields.BooleanField({ initial: false, label: "DAGGERHEART.ACTIONS.Settings.consumeOnSuccess.label" })
|
||||
consumeOnSuccess: new fields.BooleanField({
|
||||
initial: false,
|
||||
label: 'DAGGERHEART.ACTIONS.Settings.consumeOnSuccess.label'
|
||||
})
|
||||
});
|
||||
super(element, options, context);
|
||||
}
|
||||
|
|
@ -47,6 +50,7 @@ export default class CostField extends fields.ArrayField {
|
|||
static hasCost(costs) {
|
||||
const realCosts = CostField.getRealCosts.call(this, costs),
|
||||
hasFearCost = realCosts.findIndex(c => c.key === 'fear');
|
||||
|
||||
if (hasFearCost > -1) {
|
||||
const fearCost = realCosts.splice(hasFearCost, 1)[0];
|
||||
if (
|
||||
|
|
@ -70,7 +74,9 @@ export default class CostField extends fields.ArrayField {
|
|||
}
|
||||
|
||||
static getResources(costs) {
|
||||
const actorResources = this.actor.system.resources;
|
||||
const actorResources = foundry.utils.deepClone(this.actor.system.resources);
|
||||
if (this.actor.system.partner)
|
||||
actorResources.hope = foundry.utils.deepClone(this.actor.system.partner.system.resources.hope);
|
||||
const itemResources = {};
|
||||
for (let itemResource of costs) {
|
||||
if (itemResource.keyIsID) {
|
||||
|
|
@ -89,7 +95,13 @@ export default class CostField extends fields.ArrayField {
|
|||
|
||||
static getRealCosts(costs) {
|
||||
const realCosts = costs?.length ? costs.filter(c => c.enabled) : [];
|
||||
return realCosts;
|
||||
let mergedCosts = [];
|
||||
realCosts.forEach(c => {
|
||||
const getCost = Object.values(mergedCosts).find(gc => gc.key === c.key);
|
||||
if (getCost) getCost.total += c.total;
|
||||
else mergedCosts.push(c);
|
||||
});
|
||||
return mergedCosts;
|
||||
}
|
||||
|
||||
static formatMax(max) {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ export default class TargetField extends fields.SchemaField {
|
|||
|
||||
static prepareConfig(config) {
|
||||
if (!this.target?.type) return [];
|
||||
config.hasTarget = true;
|
||||
let targets;
|
||||
if (this.target?.type === CONFIG.DH.GENERAL.targetTypes.self.id)
|
||||
targets = [this.actor.token ?? this.actor.prototypeToken];
|
||||
|
|
|
|||
|
|
@ -229,7 +229,7 @@ export function ActionMixin(Base) {
|
|||
}
|
||||
|
||||
return this.inCollection
|
||||
? foundry.utils.getProperty(result, basePath).get(this.id)
|
||||
? foundry.utils.getProperty(result, basePath)?.get(this.id)
|
||||
: foundry.utils.getProperty(result, basePath);
|
||||
}
|
||||
|
||||
|
|
@ -285,6 +285,7 @@ export function ActionMixin(Base) {
|
|||
}
|
||||
};
|
||||
|
||||
ChatMessage.applyRollMode(msg, game.settings.get('core', 'rollMode'));
|
||||
cls.create(msg);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ export default class DHArmor extends AttachableItem {
|
|||
const allowed = await super._preUpdate(changes, options, user);
|
||||
if (allowed === false) return false;
|
||||
|
||||
if (changes.system.armorFeatures) {
|
||||
if (changes.system?.armorFeatures) {
|
||||
const removed = this.armorFeatures.filter(x => !changes.system.armorFeatures.includes(x));
|
||||
const added = changes.system.armorFeatures.filter(x => !this.armorFeatures.includes(x));
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,11 @@ export default class DHDomainCard extends BaseDataItem {
|
|||
};
|
||||
}
|
||||
|
||||
get domainLabel() {
|
||||
const allDomainData = CONFIG.DH.DOMAIN.allDomains();
|
||||
return game.i18n.localize(allDomainData[this.domain].label);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**@override */
|
||||
|
|
@ -71,7 +76,7 @@ export default class DHDomainCard extends BaseDataItem {
|
|||
_getTags() {
|
||||
const tags = [
|
||||
game.i18n.localize(`DAGGERHEART.CONFIG.DomainCardTypes.${this.type}`),
|
||||
game.i18n.localize(`DAGGERHEART.GENERAL.Domain.${this.domain}.label`),
|
||||
this.domainLabel,
|
||||
`${game.i18n.localize('DAGGERHEART.ITEMS.DomainCard.recallCost')}: ${this.recallCost}`
|
||||
];
|
||||
|
||||
|
|
@ -85,7 +90,7 @@ export default class DHDomainCard extends BaseDataItem {
|
|||
_getLabels() {
|
||||
const labels = [
|
||||
game.i18n.localize(`DAGGERHEART.CONFIG.DomainCardTypes.${this.type}`),
|
||||
game.i18n.localize(`DAGGERHEART.GENERAL.Domain.${this.domain}.label`),
|
||||
this.domainLabel,
|
||||
{
|
||||
value: `${this.recallCost}`, //converts the number to a string
|
||||
icons: ['fa-bolt']
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ export default class DHSubclass extends BaseDataItem {
|
|||
integer: false,
|
||||
nullable: true,
|
||||
initial: null,
|
||||
label: "DAGGERHEART.ITEMS.Subclass.spellcastingTrait"
|
||||
label: 'DAGGERHEART.ITEMS.Subclass.spellcastingTrait'
|
||||
}),
|
||||
features: new ItemLinkFields(),
|
||||
featureState: new fields.NumberField({ required: true, initial: 1, min: 1 }),
|
||||
|
|
@ -50,7 +50,8 @@ export default class DHSubclass extends BaseDataItem {
|
|||
|
||||
async _preCreate(data, options, user) {
|
||||
if (this.actor?.type === 'character') {
|
||||
const dataUuid = data.uuid ?? data._stats?.compendiumSource ?? `Item.${data._id}`;
|
||||
const dataUuid =
|
||||
(data.uuid ?? data.folder) ? `Compendium.daggerheart.subclasses.Item.${data._id}` : `Item.${data._id}`;
|
||||
if (this.actor.system.class.subclass) {
|
||||
if (this.actor.system.multiclass.subclass) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.subclassesAlreadyPresent'));
|
||||
|
|
|
|||
|
|
@ -139,17 +139,15 @@ export default class D20Roll extends DHRoll {
|
|||
static postEvaluate(roll, config = {}) {
|
||||
const data = super.postEvaluate(roll, config);
|
||||
data.type = config.roll?.type;
|
||||
data.difficulty = config.roll.difficulty;
|
||||
if (config.targets?.length) {
|
||||
config.targetSelection = true;
|
||||
config.targets.forEach(target => {
|
||||
const difficulty = config.roll.difficulty ?? target.difficulty ?? target.evasion;
|
||||
target.hit = roll.isCritical || roll.total >= difficulty;
|
||||
});
|
||||
data.success = config.targets.some(target => target.hit);
|
||||
} else if (config.roll.difficulty) {
|
||||
data.difficulty = config.roll.difficulty;
|
||||
data.success = roll.isCritical || roll.total >= config.roll.difficulty;
|
||||
}
|
||||
} else if (config.roll.difficulty) data.success = roll.isCritical || roll.total >= config.roll.difficulty;
|
||||
|
||||
data.advantage = {
|
||||
type: config.roll.advantage,
|
||||
dice: roll.dAdvantage?.denomination,
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ export default class DamageRoll extends DHRoll {
|
|||
const parts = config.roll.map(r => this.postEvaluate(r));
|
||||
|
||||
config.damage = this.unifyDamageRoll(parts);
|
||||
// config.targetSelection = config.targets?.length
|
||||
}
|
||||
|
||||
static postEvaluate(roll, config = {}) {
|
||||
|
|
@ -30,16 +29,18 @@ export default class DamageRoll extends DHRoll {
|
|||
}
|
||||
|
||||
static async buildPost(roll, config, message) {
|
||||
const chatMessage = config.source?.message
|
||||
? ui.chat.collection.get(config.source.message)
|
||||
: getDocumentClass('ChatMessage').applyRollMode({}, config.rollMode);
|
||||
if (game.modules.get('dice-so-nice')?.active) {
|
||||
const pool = foundry.dice.terms.PoolTerm.fromRolls(
|
||||
Object.values(config.damage).flatMap(r => r.parts.map(p => p.roll))
|
||||
),
|
||||
diceRoll = Roll.fromTerms([pool]);
|
||||
await game.dice3d.showForRoll(diceRoll, game.user, true);
|
||||
await game.dice3d.showForRoll(diceRoll, game.user, true, chatMessage.whisper, chatMessage.blind);
|
||||
}
|
||||
await super.buildPost(roll, config, message);
|
||||
if (config.source?.message) {
|
||||
const chatMessage = ui.chat.collection.get(config.source.message);
|
||||
chatMessage.update({ 'system.damage': config.damage });
|
||||
}
|
||||
}
|
||||
|
|
@ -102,14 +103,14 @@ export default class DamageRoll extends DHRoll {
|
|||
}
|
||||
|
||||
constructFormula(config) {
|
||||
this.options.roll.forEach(part => {
|
||||
this.options.roll.forEach((part, index) => {
|
||||
part.roll = new Roll(Roll.replaceFormulaData(part.formula, config.data));
|
||||
this.constructFormulaPart(config, part);
|
||||
this.constructFormulaPart(config, part, index);
|
||||
});
|
||||
return this.options.roll;
|
||||
}
|
||||
|
||||
constructFormulaPart(config, part) {
|
||||
constructFormulaPart(config, part, index) {
|
||||
part.roll.terms = Roll.parse(part.roll.formula, config.data);
|
||||
|
||||
if (part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) {
|
||||
|
|
@ -120,6 +121,14 @@ export default class DamageRoll extends DHRoll {
|
|||
});
|
||||
}
|
||||
|
||||
/* To Remove When Reaction System */
|
||||
if (index === 0 && part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) {
|
||||
for (const mod in config.modifiers) {
|
||||
const modifier = config.modifiers[mod];
|
||||
if (modifier.beforeCrit === true && (modifier.enabled || modifier.value)) modifier.callback(part);
|
||||
}
|
||||
}
|
||||
|
||||
if (part.extraFormula) {
|
||||
part.roll.terms.push(
|
||||
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
|
||||
|
|
@ -132,6 +141,192 @@ export default class DamageRoll extends DHRoll {
|
|||
criticalBonus = tmpRoll.total - this.constructor.calculateTotalModifiers(tmpRoll);
|
||||
part.roll.terms.push(...this.formatModifier(criticalBonus));
|
||||
}
|
||||
|
||||
/* To Remove When Reaction System */
|
||||
if (index === 0 && part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) {
|
||||
for (const mod in config.modifiers) {
|
||||
const modifier = config.modifiers[mod];
|
||||
if (!modifier.beforeCrit && (modifier.enabled || modifier.value)) modifier.callback(part);
|
||||
}
|
||||
}
|
||||
|
||||
return (part.roll._formula = this.constructor.getFormula(part.roll.terms));
|
||||
}
|
||||
|
||||
/* To Remove When Reaction System */
|
||||
static temporaryModifierBuilder(config) {
|
||||
const mods = {};
|
||||
if (config.data?.parent) {
|
||||
if (config.data.parent.appliedEffects) {
|
||||
// Bardic Rally
|
||||
const rallyChoices = config.data?.parent?.appliedEffects.reduce((a, c) => {
|
||||
const change = c.changes.find(ch => ch.key === 'system.bonuses.rally');
|
||||
if (change) a.push({ value: c.id, label: change.value });
|
||||
return a;
|
||||
}, [])
|
||||
if(rallyChoices.length) {
|
||||
mods.rally = {
|
||||
label: 'DAGGERHEART.CLASS.Feature.rallyDice',
|
||||
values: rallyChoices,
|
||||
value: null,
|
||||
beforeCrit: true,
|
||||
callback: part => {
|
||||
const rallyFaces = config.modifiers.rally.values.find(
|
||||
r => r.value === config.modifiers.rally.value
|
||||
)?.label;
|
||||
part.roll.terms.push(
|
||||
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
|
||||
...this.parse(`1${rallyFaces}`)
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const item = config.data.parent.items?.get(config.source.item);
|
||||
if (item) {
|
||||
// Massive (Weapon Feature)
|
||||
if (item.system.itemFeatures.find(f => f.value === 'massive'))
|
||||
mods.massive = {
|
||||
label: CONFIG.DH.ITEM.weaponFeatures.massive.label,
|
||||
enabled: true,
|
||||
callback: part => {
|
||||
part.roll.terms[0].modifiers.push(`kh${part.roll.terms[0].number}`);
|
||||
part.roll.terms[0].number += 1;
|
||||
}
|
||||
};
|
||||
|
||||
// Powerful (Weapon Feature)
|
||||
if (item.system.itemFeatures.find(f => f.value === 'powerful'))
|
||||
mods.powerful = {
|
||||
label: CONFIG.DH.ITEM.weaponFeatures.powerful.label,
|
||||
enabled: true,
|
||||
callback: part => {
|
||||
part.roll.terms[0].modifiers.push(`kh${part.roll.terms[0].number}`);
|
||||
part.roll.terms[0].number += 1;
|
||||
}
|
||||
};
|
||||
|
||||
// Brutal (Weapon Feature)
|
||||
if (item.system.itemFeatures.find(f => f.value === 'brutal'))
|
||||
mods.brutal = {
|
||||
label: CONFIG.DH.ITEM.weaponFeatures.brutal.label,
|
||||
enabled: true,
|
||||
beforeCrit: true,
|
||||
callback: part => {
|
||||
part.roll.terms[0].modifiers.push(`x${part.roll.terms[0].faces}`);
|
||||
}
|
||||
};
|
||||
|
||||
// Serrated (Weapon Feature)
|
||||
if (item.system.itemFeatures.find(f => f.value === 'serrated'))
|
||||
mods.serrated = {
|
||||
label: CONFIG.DH.ITEM.weaponFeatures.serrated.label,
|
||||
enabled: true,
|
||||
callback: part => {
|
||||
part.roll.terms[0].modifiers.push(`sc8`);
|
||||
}
|
||||
};
|
||||
|
||||
// Self-Correcting (Weapon Feature)
|
||||
if (item.system.itemFeatures.find(f => f.value === 'selfCorrecting'))
|
||||
mods.selfCorrecting = {
|
||||
label: CONFIG.DH.ITEM.weaponFeatures.selfCorrecting.label,
|
||||
enabled: true,
|
||||
callback: part => {
|
||||
part.roll.terms[0].modifiers.push(`sc6`);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
config.modifiers = mods;
|
||||
return mods;
|
||||
}
|
||||
|
||||
static async reroll(target, message) {
|
||||
const { damageType, part, dice, result } = target.dataset;
|
||||
const rollPart = message.system.damage[damageType].parts[part];
|
||||
|
||||
let diceIndex = 0;
|
||||
let parsedRoll = game.system.api.dice.DamageRoll.fromData({
|
||||
...rollPart.roll,
|
||||
terms: rollPart.roll.terms.map(term => {
|
||||
const isDie = term.class === 'Die';
|
||||
const fixedTerm = {
|
||||
...term,
|
||||
...(isDie ? { results: rollPart.dice[diceIndex].results } : {})
|
||||
};
|
||||
|
||||
if (isDie) diceIndex++;
|
||||
return fixedTerm;
|
||||
}),
|
||||
class: 'DamageRoll',
|
||||
evaluated: false
|
||||
});
|
||||
|
||||
const parsedDiceTerms = Object.keys(parsedRoll.terms).reduce((acc, key) => {
|
||||
const term = parsedRoll.terms[key];
|
||||
if (term instanceof CONFIG.Dice.termTypes.DiceTerm) acc[Object.keys(acc).length] = term;
|
||||
return acc;
|
||||
}, {});
|
||||
const term = parsedDiceTerms[dice];
|
||||
const termResult = parsedDiceTerms[dice].results[result];
|
||||
|
||||
const newIndex = parsedDiceTerms[dice].results.length;
|
||||
await term.reroll(`/r1=${termResult.result}`);
|
||||
|
||||
if (game.modules.get('dice-so-nice')?.active) {
|
||||
const newResult = parsedDiceTerms[dice].results[newIndex];
|
||||
const diceSoNiceRoll = {
|
||||
_evaluated: true,
|
||||
dice: [
|
||||
new foundry.dice.terms.Die({
|
||||
...term,
|
||||
total: newResult.result,
|
||||
faces: term._faces,
|
||||
results: [newResult]
|
||||
})
|
||||
],
|
||||
options: { appearance: {} }
|
||||
};
|
||||
|
||||
await game.dice3d.showForRoll(diceSoNiceRoll, game.user, true);
|
||||
}
|
||||
|
||||
await parsedRoll.evaluate();
|
||||
|
||||
const results = parsedRoll.dice[dice].results.map(result => ({
|
||||
...result,
|
||||
discarded: !result.active
|
||||
}));
|
||||
const newResult = results.splice(results.length - 1, 1);
|
||||
results.splice(Number(result) + 1, 0, newResult[0]);
|
||||
|
||||
const rerolledDice = parsedRoll.dice.map((x, index) => {
|
||||
const isRerollDice = index === Number(dice);
|
||||
if (!isRerollDice) return { ...x, dice: x.denomination };
|
||||
return {
|
||||
dice: parsedRoll.dice[dice].denomination,
|
||||
total: parsedRoll.dice[dice].total,
|
||||
results: results.map(result => ({
|
||||
...result,
|
||||
hasRerolls: result.hasRerolls || isRerollDice
|
||||
}))
|
||||
};
|
||||
});
|
||||
|
||||
const updateMessage = game.messages.get(message._id);
|
||||
await updateMessage.update({
|
||||
[`system.damage.${damageType}`]: {
|
||||
...updateMessage,
|
||||
total: parsedRoll.total,
|
||||
[`parts.${part}`]: {
|
||||
...rollPart,
|
||||
total: parsedRoll.total,
|
||||
dice: rerolledDice
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,19 +2,19 @@ import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs';
|
|||
|
||||
export default class DHRoll extends Roll {
|
||||
baseTerms = [];
|
||||
constructor(formula, data, options) {
|
||||
constructor(formula, data = {}, options = {}) {
|
||||
super(formula, data, options);
|
||||
if (!this.data || !Object.keys(this.data).length) this.data = options.data;
|
||||
}
|
||||
|
||||
get title() {
|
||||
return game.i18n.localize(
|
||||
"DAGGERHEART.GENERAL.Roll.basic"
|
||||
);
|
||||
return game.i18n.localize('DAGGERHEART.GENERAL.Roll.basic');
|
||||
}
|
||||
|
||||
static messageType = 'adversaryRoll';
|
||||
|
||||
static CHAT_TEMPLATE = 'systems/daggerheart/templates/ui/chat/roll.hbs';
|
||||
|
||||
static DefaultDialog = D20RollDialog;
|
||||
|
||||
static async build(config = {}, message = {}) {
|
||||
|
|
@ -34,6 +34,8 @@ export default class DHRoll extends Roll {
|
|||
|
||||
this.applyKeybindings(config);
|
||||
|
||||
this.temporaryModifierBuilder(config);
|
||||
|
||||
let roll = new this(config.roll.formula, config.data, config);
|
||||
if (config.dialog.configure !== false) {
|
||||
// Open Roll Dialog
|
||||
|
|
@ -64,8 +66,7 @@ export default class DHRoll extends Roll {
|
|||
}
|
||||
|
||||
// Create Chat Message
|
||||
if (!config.source?.message)
|
||||
config.message = await this.toMessage(roll, config);
|
||||
if (!config.source?.message) config.message = await this.toMessage(roll, config);
|
||||
}
|
||||
|
||||
static postEvaluate(roll, config = {}) {
|
||||
|
|
@ -92,10 +93,37 @@ export default class DHRoll extends Roll {
|
|||
system: config,
|
||||
rolls: [roll]
|
||||
};
|
||||
if(roll._evaluated) return await cls.create(msg, { rollMode: config.selectedRollMode });
|
||||
config.selectedRollMode ??= game.settings.get('core', 'rollMode');
|
||||
if (roll._evaluated) return await cls.create(msg, { rollMode: config.selectedRollMode });
|
||||
return msg;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
async render({ flavor, template = this.constructor.CHAT_TEMPLATE, isPrivate = false, ...options } = {}) {
|
||||
if (!this._evaluated) return;
|
||||
const chatData = await this._prepareChatRenderContext({ flavor, isPrivate, ...options });
|
||||
return foundry.applications.handlebars.renderTemplate(template, chatData);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
async _prepareChatRenderContext({ flavor, isPrivate = false, ...options } = {}) {
|
||||
if (isPrivate) {
|
||||
return {
|
||||
user: game.user.id,
|
||||
flavor: null,
|
||||
title: '???',
|
||||
roll: {
|
||||
total: '??'
|
||||
},
|
||||
hasRoll: true,
|
||||
isPrivate
|
||||
};
|
||||
} else {
|
||||
options.message.system.user = game.user.id;
|
||||
return options.message.system;
|
||||
}
|
||||
}
|
||||
|
||||
static applyKeybindings(config) {
|
||||
if (config.event)
|
||||
config.dialog.configure ??= !(config.event.shiftKey || config.event.altKey || config.event.ctrlKey);
|
||||
|
|
@ -178,11 +206,15 @@ export default class DHRoll extends Roll {
|
|||
}
|
||||
return modifierTotal;
|
||||
}
|
||||
|
||||
static temporaryModifierBuilder(config) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
export const registerRollDiceHooks = () => {
|
||||
Hooks.on(`${CONFIG.DH.id}.postRollDuality`, async (config, message) => {
|
||||
const hopeFearAutomation = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).hopeFear;
|
||||
const hopeFearAutomation = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).hopeFear;
|
||||
if (
|
||||
!config.source?.actor ||
|
||||
(game.user.isGM ? !hopeFearAutomation.gm : !hopeFearAutomation.players) ||
|
||||
|
|
@ -206,8 +238,10 @@ export const registerRollDiceHooks = () => {
|
|||
|
||||
if (updates.length) {
|
||||
const target = actor.system.partner ?? actor;
|
||||
if (!['dead', 'unconcious'].some(x => actor.statuses.has(x))) {
|
||||
target.modifyResource(updates);
|
||||
if (!['dead', 'unconscious'].some(x => actor.statuses.has(x))) {
|
||||
setTimeout(() => {
|
||||
target.modifyResource(updates);
|
||||
}, 50);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -124,15 +124,20 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
|
|||
return tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ChatMessage to display this document’s data.
|
||||
* @param {String} origin - uuid of a document. TODO: This needs to be reviewed.
|
||||
*/
|
||||
async toChat(origin) {
|
||||
/**@type {foundry.documents.ChatMessage} */
|
||||
const cls = getDocumentClass('ChatMessage');
|
||||
const actor = game.actors.get(cls.getSpeaker().actor);
|
||||
const speaker = cls.getSpeaker();
|
||||
const actor = cls.getSpeakerActor(speaker);
|
||||
const systemData = {
|
||||
action: { img: this.img, name: this.name },
|
||||
actor: { name: actor.name, img: actor.img },
|
||||
author: this.author,
|
||||
speaker: cls.getSpeaker(),
|
||||
origin: origin,
|
||||
actor: { name: actor?.name, img: actor?.img },
|
||||
speaker,
|
||||
origin,
|
||||
description: this.description,
|
||||
actions: []
|
||||
};
|
||||
|
|
|
|||
|
|
@ -84,6 +84,8 @@ export default class DhpActor extends Actor {
|
|||
|
||||
await this.update({ 'system.levelData.level.changed': Math.min(newLevel, maxLevel) });
|
||||
} else {
|
||||
const levelupAuto = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).levelupAuto;
|
||||
|
||||
const usedLevel = Math.max(newLevel, 1);
|
||||
if (newLevel < 1) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.tooLowLevel'));
|
||||
|
|
@ -95,79 +97,90 @@ export default class DhpActor extends Actor {
|
|||
return acc;
|
||||
}, {});
|
||||
|
||||
const features = [];
|
||||
const domainCards = [];
|
||||
const experiences = [];
|
||||
const subclassFeatureState = { class: null, multiclass: null };
|
||||
let multiclass = null;
|
||||
Object.keys(this.system.levelData.levelups)
|
||||
.filter(x => x > usedLevel)
|
||||
.forEach(levelKey => {
|
||||
const level = this.system.levelData.levelups[levelKey];
|
||||
const achievementCards = level.achievements.domainCards.map(x => x.itemUuid);
|
||||
const advancementCards = level.selections.filter(x => x.type === 'domainCard').map(x => x.itemUuid);
|
||||
domainCards.push(...achievementCards, ...advancementCards);
|
||||
experiences.push(...Object.keys(level.achievements.experiences));
|
||||
features.push(...level.selections.flatMap(x => x.features));
|
||||
if (levelupAuto) {
|
||||
const features = [];
|
||||
const domainCards = [];
|
||||
const experiences = [];
|
||||
const subclassFeatureState = { class: null, multiclass: null };
|
||||
let multiclass = null;
|
||||
Object.keys(this.system.levelData.levelups)
|
||||
.filter(x => x > usedLevel)
|
||||
.forEach(levelKey => {
|
||||
const level = this.system.levelData.levelups[levelKey];
|
||||
const achievementCards = level.achievements.domainCards.map(x => x.itemUuid);
|
||||
const advancementCards = level.selections
|
||||
.filter(x => x.type === 'domainCard')
|
||||
.map(x => x.itemUuid);
|
||||
domainCards.push(...achievementCards, ...advancementCards);
|
||||
experiences.push(...Object.keys(level.achievements.experiences));
|
||||
features.push(...level.selections.flatMap(x => x.features));
|
||||
|
||||
const subclass = level.selections.find(x => x.type === 'subclass');
|
||||
if (subclass) {
|
||||
const path = subclass.secondaryData.isMulticlass === 'true' ? 'multiclass' : 'class';
|
||||
const subclassState = Number(subclass.secondaryData.featureState) - 1;
|
||||
subclassFeatureState[path] = subclassFeatureState[path]
|
||||
? Math.min(subclassState, subclassFeatureState[path])
|
||||
: subclassState;
|
||||
}
|
||||
const subclass = level.selections.find(x => x.type === 'subclass');
|
||||
if (subclass) {
|
||||
const path = subclass.secondaryData.isMulticlass === 'true' ? 'multiclass' : 'class';
|
||||
const subclassState = Number(subclass.secondaryData.featureState) - 1;
|
||||
subclassFeatureState[path] = subclassFeatureState[path]
|
||||
? Math.min(subclassState, subclassFeatureState[path])
|
||||
: subclassState;
|
||||
}
|
||||
|
||||
multiclass = level.selections.find(x => x.type === 'multiclass');
|
||||
});
|
||||
multiclass = level.selections.find(x => x.type === 'multiclass');
|
||||
});
|
||||
|
||||
for (let feature of features) {
|
||||
if (feature.onPartner && !this.system.partner) continue;
|
||||
for (let feature of features) {
|
||||
if (feature.onPartner && !this.system.partner) continue;
|
||||
|
||||
const document = feature.onPartner ? this.system.partner : this;
|
||||
document.items.get(feature.id)?.delete();
|
||||
}
|
||||
|
||||
if (experiences.length > 0) {
|
||||
const getUpdate = () => ({
|
||||
'system.experiences': experiences.reduce((acc, key) => {
|
||||
acc[`-=${key}`] = null;
|
||||
return acc;
|
||||
}, {})
|
||||
});
|
||||
this.update(getUpdate());
|
||||
if (this.system.companion) {
|
||||
this.system.companion.update(getUpdate());
|
||||
const document = feature.onPartner ? this.system.partner : this;
|
||||
document.items.get(feature.id)?.delete();
|
||||
}
|
||||
}
|
||||
|
||||
if (subclassFeatureState.class) {
|
||||
this.system.class.subclass.update({ 'system.featureState': subclassFeatureState.class });
|
||||
}
|
||||
|
||||
if (subclassFeatureState.multiclass) {
|
||||
this.system.multiclass.subclass.update({ 'system.featureState': subclassFeatureState.multiclass });
|
||||
}
|
||||
|
||||
if (multiclass) {
|
||||
const multiclassSubclass = this.items.find(x => x.type === 'subclass' && x.system.isMulticlass);
|
||||
const multiclassItem = this.items.find(x => x.uuid === multiclass.itemUuid);
|
||||
|
||||
multiclassSubclass.delete();
|
||||
multiclassItem.delete();
|
||||
|
||||
this.update({
|
||||
'system.multiclass': {
|
||||
value: null,
|
||||
subclass: null
|
||||
if (experiences.length > 0) {
|
||||
const getUpdate = () => ({
|
||||
'system.experiences': experiences.reduce((acc, key) => {
|
||||
acc[`-=${key}`] = null;
|
||||
return acc;
|
||||
}, {})
|
||||
});
|
||||
this.update(getUpdate());
|
||||
if (this.system.companion) {
|
||||
this.system.companion.update(getUpdate());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (let domainCard of domainCards) {
|
||||
const itemCard = this.items.find(x => x.uuid === domainCard);
|
||||
itemCard.delete();
|
||||
if (subclassFeatureState.class) {
|
||||
this.system.class.subclass.update({ 'system.featureState': subclassFeatureState.class });
|
||||
}
|
||||
|
||||
if (subclassFeatureState.multiclass) {
|
||||
this.system.multiclass.subclass.update({ 'system.featureState': subclassFeatureState.multiclass });
|
||||
}
|
||||
|
||||
if (multiclass) {
|
||||
const multiclassItem = this.items.find(x => x.uuid === multiclass.itemUuid);
|
||||
const multiclassFeatures = this.items.filter(
|
||||
x => x.system.originItemType === 'class' && x.system.identifier === 'multiclass'
|
||||
);
|
||||
const subclassFeatures = this.items.filter(
|
||||
x => x.system.originItemType === 'subclass' && x.system.identifier === 'multiclass'
|
||||
);
|
||||
|
||||
this.deleteEmbeddedDocuments(
|
||||
'Item',
|
||||
[multiclassItem, ...multiclassFeatures, ...subclassFeatures].map(x => x.id)
|
||||
);
|
||||
|
||||
this.update({
|
||||
'system.multiclass': {
|
||||
value: null,
|
||||
subclass: null
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (let domainCard of domainCards) {
|
||||
const itemCard = this.items.find(x => x.uuid === domainCard);
|
||||
itemCard.delete();
|
||||
}
|
||||
}
|
||||
|
||||
await this.update({
|
||||
|
|
@ -315,6 +328,7 @@ export default class DhpActor extends Actor {
|
|||
...multiclassData,
|
||||
system: {
|
||||
...multiclassData.system,
|
||||
features: multiclassData.system.features.filter(x => x.type !== 'hope'),
|
||||
domains: [multiclass.secondaryData.domain],
|
||||
isMulticlass: true
|
||||
}
|
||||
|
|
@ -644,16 +658,23 @@ export default class DhpActor extends Actor {
|
|||
);
|
||||
break;
|
||||
case 'armor':
|
||||
updates.armor.resources['system.marks.value'] = Math.max(
|
||||
Math.min(this.system.armor.system.marks.value + r.value, this.system.armorScore),
|
||||
0
|
||||
);
|
||||
if (this.system.armor?.system?.marks) {
|
||||
updates.armor.resources['system.marks.value'] = Math.max(
|
||||
Math.min(this.system.armor.system.marks.value + r.value, this.system.armorScore),
|
||||
0
|
||||
);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
updates.actor.resources[`system.resources.${r.key}.value`] = Math.max(
|
||||
Math.min(this.system.resources[r.key].value + r.value, this.system.resources[r.key].max),
|
||||
0
|
||||
);
|
||||
if (this.system.resources?.[r.key]) {
|
||||
updates.actor.resources[`system.resources.${r.key}.value`] = Math.max(
|
||||
Math.min(
|
||||
this.system.resources[r.key].value + r.value,
|
||||
this.system.resources[r.key].max
|
||||
),
|
||||
0
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,61 @@
|
|||
export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
||||
async renderHTML() {
|
||||
if (this.system.messageTemplate)
|
||||
this.content = await foundry.applications.handlebars.renderTemplate(this.system.messageTemplate, {
|
||||
...this.system,
|
||||
_source: this.system._source
|
||||
});
|
||||
targetHook = null;
|
||||
targetSelection = null;
|
||||
|
||||
async renderHTML() {
|
||||
const actor = game.actors.get(this.speaker.actor);
|
||||
const actorData = actor ?? {
|
||||
img: this.author.avatar ? this.author.avatar : 'icons/svg/mystery-man.svg',
|
||||
name: ''
|
||||
};
|
||||
const actorData =
|
||||
actor && this.isContentVisible
|
||||
? actor
|
||||
: {
|
||||
img: this.author.avatar ? this.author.avatar : 'icons/svg/mystery-man.svg',
|
||||
name: ''
|
||||
};
|
||||
/* We can change to fully implementing the renderHTML function if needed, instead of augmenting it. */
|
||||
const html = await super.renderHTML({ actor: actorData, author: this.author });
|
||||
this.applyPermission(html);
|
||||
|
||||
if (this.type === 'dualityRoll') {
|
||||
this.enrichChatMessage(html);
|
||||
this.addChatListeners(html);
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @inheritDoc */
|
||||
prepareData() {
|
||||
if (this.isAuthor && this.targetSelection === null) this.targetSelection = this.system.targets?.length > 0;
|
||||
super.prepareData();
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @inheritDoc */
|
||||
_onCreate(data, options, userId) {
|
||||
super._onCreate(data, options, userId);
|
||||
if (this.system.registerTargetHook) this.system.registerTargetHook();
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @inheritDoc */
|
||||
async _preDelete(options, user) {
|
||||
if (this.targetHook !== null) Hooks.off('targetToken', this.targetHook);
|
||||
return super._preDelete(options, user);
|
||||
}
|
||||
|
||||
enrichChatMessage(html) {
|
||||
const elements = html.querySelectorAll('[data-perm-id]');
|
||||
elements.forEach(e => {
|
||||
const uuid = e.dataset.permId,
|
||||
document = fromUuidSync(uuid);
|
||||
if (!document) return;
|
||||
|
||||
e.setAttribute('data-view-perm', document.testUserPermission(game.user, 'OBSERVER'));
|
||||
e.setAttribute('data-use-perm', document.testUserPermission(game.user, 'OWNER'));
|
||||
});
|
||||
|
||||
if (this.isContentVisible && this.type === 'dualityRoll') {
|
||||
html.classList.add('duality');
|
||||
switch (this.system.roll?.result?.duality) {
|
||||
case 1:
|
||||
|
|
@ -29,36 +69,14 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.enrichChatMessage(html);
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
applyPermission(html) {
|
||||
const elements = html.querySelectorAll('[data-perm-id]');
|
||||
elements.forEach(e => {
|
||||
const uuid = e.dataset.permId,
|
||||
document = fromUuidSync(uuid);
|
||||
if (!document) return;
|
||||
|
||||
e.setAttribute('data-view-perm', document.testUserPermission(game.user, 'OBSERVER'));
|
||||
e.setAttribute('data-use-perm', document.testUserPermission(game.user, 'OWNER'));
|
||||
});
|
||||
}
|
||||
|
||||
async _preCreate(data, options, user) {
|
||||
options.speaker = ChatMessage.getSpeaker();
|
||||
const rollActorOwner = data.rolls?.[0]?.data?.parent?.owner;
|
||||
if (rollActorOwner) {
|
||||
data.author = rollActorOwner ? rollActorOwner.id : data.author;
|
||||
await this.updateSource({ author: rollActorOwner ?? user });
|
||||
|
||||
if(!game.user.isGM && !this.isAuthor && !this.speakerActor?.isOwner) {
|
||||
const buttons = html.querySelectorAll(".ability-card-footer > .ability-use-button");
|
||||
buttons.forEach(b => b.remove());
|
||||
}
|
||||
|
||||
return super._preCreate(data, options, rollActorOwner ?? user);
|
||||
}
|
||||
|
||||
enrichChatMessage(html) {
|
||||
addChatListeners(html) {
|
||||
html.querySelectorAll('.damage-button').forEach(element =>
|
||||
element.addEventListener('click', this.onDamage.bind(this))
|
||||
);
|
||||
|
|
@ -66,10 +84,20 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
|||
html.querySelectorAll('.duality-action-effect').forEach(element =>
|
||||
element.addEventListener('click', this.onApplyEffect.bind(this))
|
||||
);
|
||||
|
||||
html.querySelectorAll('.roll-target').forEach(element => {
|
||||
element.addEventListener('mouseenter', this.hoverTarget);
|
||||
element.addEventListener('mouseleave', this.unhoverTarget);
|
||||
element.addEventListener('click', this.clickTarget);
|
||||
});
|
||||
|
||||
html.querySelectorAll('.button-target-selection').forEach(element => {
|
||||
element.addEventListener('click', this.onTargetSelection.bind(this));
|
||||
});
|
||||
}
|
||||
|
||||
getTargetList() {
|
||||
const targets = this.system.hitTargets;
|
||||
const targets = this.system.hitTargets ?? [];
|
||||
return targets.map(target => game.canvas.tokens.documentCollection.find(t => t.actor?.uuid === target.actorId));
|
||||
}
|
||||
|
||||
|
|
@ -141,9 +169,36 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
|||
}
|
||||
|
||||
consumeOnSuccess() {
|
||||
if (!this.system.successConsumed && !this.system.targetSelection) {
|
||||
if (!this.system.successConsumed && !this.targetSelection) {
|
||||
const action = this.system.action;
|
||||
if (action) action.consume(this.system, true);
|
||||
}
|
||||
}
|
||||
|
||||
hoverTarget(event) {
|
||||
event.stopPropagation();
|
||||
const token = canvas.tokens.get(event.currentTarget.dataset.token);
|
||||
if (token && !token?.controlled) token._onHoverIn(event, { hoverOutOthers: true });
|
||||
}
|
||||
|
||||
unhoverTarget(event) {
|
||||
const token = canvas.tokens.get(event.currentTarget.dataset.token);
|
||||
if (token && !token?.controlled) token._onHoverOut(event);
|
||||
}
|
||||
|
||||
clickTarget(event) {
|
||||
event.stopPropagation();
|
||||
const token = canvas.tokens.get(event.currentTarget.dataset.token);
|
||||
if (!token) {
|
||||
ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.attackTargetDoesNotExist'));
|
||||
return;
|
||||
}
|
||||
game.canvas.pan(token);
|
||||
}
|
||||
|
||||
onTargetSelection(event) {
|
||||
event.stopPropagation();
|
||||
if (!event.target.classList.contains('target-selected'))
|
||||
this.system.targetMode = Boolean(event.target.dataset.targetHit);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -142,19 +142,16 @@ export default class DHItem extends foundry.documents.Item {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ChatMessage to display this document’s data
|
||||
* @param {String} origin - uuid of a document. TODO: This needs to be reviewed.
|
||||
*/
|
||||
async toChat(origin) {
|
||||
/**@type {foundry.documents.ChatMessage} */
|
||||
const cls = getDocumentClass('ChatMessage');
|
||||
const item = await foundry.utils.fromUuid(origin);
|
||||
|
||||
const systemData = {
|
||||
title:
|
||||
this.type === 'ancestry'
|
||||
? game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.ancestryTitle')
|
||||
: this.type === 'community'
|
||||
? game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.communityTitle')
|
||||
: this.type === 'feature'
|
||||
? game.i18n.localize('TYPES.Item.feature')
|
||||
: game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.subclassFeatureTitle'),
|
||||
origin: origin,
|
||||
img: this.img,
|
||||
item: {
|
||||
|
|
@ -170,7 +167,6 @@ export default class DHItem extends foundry.documents.Item {
|
|||
type: 'abilityUse',
|
||||
user: game.user.id,
|
||||
actor: item.parent,
|
||||
author: this.author,
|
||||
speaker: cls.getSpeaker(),
|
||||
system: systemData,
|
||||
title: game.i18n.localize('DAGGERHEART.ACTIONS.Config.displayInChat'),
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ export default class RegisterHandlebarsHelpers {
|
|||
|
||||
static rollParsed(value, actor, item, numerical) {
|
||||
const isNumerical = typeof numerical === 'boolean' ? numerical : false;
|
||||
const result = itemAbleRollParse(value, actor.getRollData(), item);
|
||||
const result = itemAbleRollParse(value, actor?.getRollData() ?? {}, item);
|
||||
return isNumerical ? (!result ? 0 : Number(result)) : result;
|
||||
}
|
||||
|
||||
|
|
@ -69,7 +69,7 @@ export default class RegisterHandlebarsHelpers {
|
|||
}
|
||||
|
||||
static empty(object) {
|
||||
if(!(typeof object === 'object')) return true;
|
||||
if (!(typeof object === 'object')) return true;
|
||||
return Object.keys(object).length === 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,7 +85,12 @@ export const chunkify = (array, chunkSize, mappingFunc) => {
|
|||
|
||||
export const tagifyElement = (element, baseOptions, onChange, tagifyOptions = {}) => {
|
||||
const { maxTags } = tagifyOptions;
|
||||
const options = typeof baseOptions === 'object' ? Object.values(baseOptions) : baseOptions;
|
||||
const options = Array.isArray(baseOptions)
|
||||
? baseOptions
|
||||
: Object.keys(baseOptions).map(optionKey => ({
|
||||
...baseOptions[optionKey],
|
||||
id: optionKey
|
||||
}));
|
||||
|
||||
const tagifyElement = new Tagify(element, {
|
||||
tagTextProp: 'name',
|
||||
|
|
@ -172,6 +177,26 @@ Roll.replaceFormulaData = function (formula, data = {}, { missing, warn = false
|
|||
return nativeReplaceFormulaData(formula, data, { missing, warn });
|
||||
};
|
||||
|
||||
foundry.dice.terms.Die.MODIFIERS.sc = 'selfCorrecting';
|
||||
|
||||
/**
|
||||
* Return the configured value as result if 1 is rolled
|
||||
* Example: 6d6sc6 Roll 6d6, each result of 1 will be changed into 6
|
||||
* @param {string} modifier The matched modifier query
|
||||
*/
|
||||
foundry.dice.terms.Die.prototype.selfCorrecting = function (modifier) {
|
||||
const rgx = /(?:sc)([0-9]+)/i;
|
||||
const match = modifier.match(rgx);
|
||||
if (!match) return false;
|
||||
let [target] = match.slice(1);
|
||||
target = parseInt(target);
|
||||
for (const r of this.results) {
|
||||
if (r.result === 1) {
|
||||
r.result = target;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const getDamageKey = damage => {
|
||||
return ['none', 'minor', 'major', 'severe'][damage];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
"resource": {
|
||||
"type": "diceValue",
|
||||
"value": 0,
|
||||
"max": "2",
|
||||
"max": "@system.traits.strength.value",
|
||||
"icon": "",
|
||||
"recovery": "session"
|
||||
},
|
||||
|
|
@ -28,12 +28,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1754352649696,
|
||||
"modifiedTime": 1754352712334,
|
||||
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
|
||||
"modifiedTime": 1754845640002,
|
||||
"lastModifiedBy": "H02dtt2xvVJvYESk"
|
||||
},
|
||||
"_key": "!items!Xd7RYhfTxIj9aWI2"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,16 +14,7 @@
|
|||
"description": "",
|
||||
"chatDisplay": false,
|
||||
"actionType": "action",
|
||||
"cost": [
|
||||
{
|
||||
"scalable": false,
|
||||
"key": "hitPoints",
|
||||
"value": 1,
|
||||
"keyIsID": false,
|
||||
"step": null,
|
||||
"consumeOnSuccess": false
|
||||
}
|
||||
],
|
||||
"cost": [],
|
||||
"uses": {
|
||||
"value": null,
|
||||
"max": "1",
|
||||
|
|
@ -67,7 +58,7 @@
|
|||
{
|
||||
"key": "system.bonuses.rally",
|
||||
"mode": 2,
|
||||
"value": "1d6",
|
||||
"value": "d6",
|
||||
"priority": null
|
||||
}
|
||||
],
|
||||
|
|
|
|||
|
|
@ -14,16 +14,7 @@
|
|||
"description": "",
|
||||
"chatDisplay": true,
|
||||
"actionType": "action",
|
||||
"cost": [
|
||||
{
|
||||
"scalable": false,
|
||||
"key": "hitPoints",
|
||||
"value": 1,
|
||||
"keyIsID": false,
|
||||
"step": null,
|
||||
"consumeOnSuccess": false
|
||||
}
|
||||
],
|
||||
"cost": [],
|
||||
"uses": {
|
||||
"value": null,
|
||||
"max": "1",
|
||||
|
|
@ -67,7 +58,7 @@
|
|||
{
|
||||
"key": "system.bonuses.rally",
|
||||
"mode": 2,
|
||||
"value": "1d8",
|
||||
"value": "d8",
|
||||
"priority": null
|
||||
}
|
||||
],
|
||||
|
|
|
|||
|
|
@ -59,13 +59,13 @@
|
|||
{
|
||||
"key": "system.bonuses.damage.physical.dice",
|
||||
"mode": 2,
|
||||
"value": "@system.tierd6",
|
||||
"value": "@tierd6",
|
||||
"priority": null
|
||||
},
|
||||
{
|
||||
"key": "system.bonuses.damage.magical.dice",
|
||||
"mode": 2,
|
||||
"value": "@system.tierd6",
|
||||
"value": "@tierd6",
|
||||
"priority": null
|
||||
}
|
||||
],
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@
|
|||
"effects": [
|
||||
{
|
||||
"name": "Gifted Tracker",
|
||||
"img": "icons/svg/item-bag.svg",
|
||||
"img": "systems/daggerheart/assets/icons/domains/domain-card/sage.png",
|
||||
"origin": "Compendium.daggerheart.domains.Item.VZ2b4zfRzV73XTuT",
|
||||
"transfer": false,
|
||||
"_id": "47Oh2weCdmuvKHM9",
|
||||
|
|
@ -104,12 +104,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1754114056078,
|
||||
"modifiedTime": 1754114073478,
|
||||
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
|
||||
"modifiedTime": 1754670410126,
|
||||
"lastModifiedBy": "49DaecTcBSc5d0DA"
|
||||
},
|
||||
"_key": "!items.effects!VZ2b4zfRzV73XTuT.47Oh2weCdmuvKHM9"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -389,7 +389,7 @@
|
|||
"effects": [
|
||||
{
|
||||
"name": "Healed by Healing Hands",
|
||||
"img": "icons/svg/item-bag.svg",
|
||||
"img": "systems/daggerheart/assets/icons/domains/domain-card/splendor.png",
|
||||
"origin": "Compendium.daggerheart.domains.Item.WTlhnQMajc1r8i50",
|
||||
"transfer": false,
|
||||
"_id": "sd5liP4ZcVeTMAoW",
|
||||
|
|
@ -422,12 +422,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1754263407455,
|
||||
"modifiedTime": 1754263727114,
|
||||
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
|
||||
"modifiedTime": 1754670504951,
|
||||
"lastModifiedBy": "49DaecTcBSc5d0DA"
|
||||
},
|
||||
"_key": "!items.effects!WTlhnQMajc1r8i50.sd5liP4ZcVeTMAoW"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@
|
|||
"effects": [
|
||||
{
|
||||
"name": "Life Ward",
|
||||
"img": "icons/svg/item-bag.svg",
|
||||
"img": "systems/daggerheart/assets/icons/domains/domain-card/splendor.png",
|
||||
"origin": "Compendium.daggerheart.domains.Item.OszbCj0jTqq2ADx9",
|
||||
"transfer": false,
|
||||
"_id": "E7Ou4OMEy3TeK1Gf",
|
||||
|
|
@ -99,12 +99,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1754264687962,
|
||||
"modifiedTime": 1754264717646,
|
||||
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
|
||||
"modifiedTime": 1754670535710,
|
||||
"lastModifiedBy": "49DaecTcBSc5d0DA"
|
||||
},
|
||||
"_key": "!items.effects!OszbCj0jTqq2ADx9.E7Ou4OMEy3TeK1Gf"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
}
|
||||
},
|
||||
"_id": "UJTsJlnhi5Zi0XQ2",
|
||||
"img": "icons/magic/life/heart-cross-blue.webp",
|
||||
"img": "systems/daggerheart/assets/icons/domains/domain-card/bone.png",
|
||||
"changes": [
|
||||
{
|
||||
"key": "system.rules.damageReduction.thresholdImmunities.minor",
|
||||
|
|
@ -67,12 +67,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1754303484332,
|
||||
"modifiedTime": 1754303570504,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
"modifiedTime": 1754670012467,
|
||||
"lastModifiedBy": "49DaecTcBSc5d0DA"
|
||||
},
|
||||
"_key": "!items.effects!zbxPl81kbWEegKQN.UJTsJlnhi5Zi0XQ2"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
{
|
||||
"value": "reliable",
|
||||
"effectIds": [
|
||||
"HrbJ0bI7lMAYUCux"
|
||||
"nRNnU57i7RMJoklC"
|
||||
],
|
||||
"actionIds": []
|
||||
}
|
||||
|
|
@ -109,14 +109,21 @@
|
|||
"img": "icons/skills/melee/strike-sword-slashing-red.webp",
|
||||
"changes": [
|
||||
{
|
||||
"key": "system.bonuses.roll.primaryWeapon.attack",
|
||||
"key": "system.bonuses.roll.primaryWeapon.bonus",
|
||||
"mode": 2,
|
||||
"value": "1"
|
||||
}
|
||||
],
|
||||
"_id": "HrbJ0bI7lMAYUCux",
|
||||
"_id": "nRNnU57i7RMJoklC",
|
||||
"type": "base",
|
||||
"system": {},
|
||||
"system": {
|
||||
"rangeDependence": {
|
||||
"enabled": false,
|
||||
"type": "withinRange",
|
||||
"target": "hostile",
|
||||
"range": "melee"
|
||||
}
|
||||
},
|
||||
"disabled": false,
|
||||
"duration": {
|
||||
"startTime": null,
|
||||
|
|
@ -132,14 +139,14 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1753835307838,
|
||||
"modifiedTime": 1753835307838,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1754815224718,
|
||||
"modifiedTime": 1754815224718,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items.effects!ijodu5yNBoMxpkHV.HrbJ0bI7lMAYUCux"
|
||||
"_key": "!items.effects!ijodu5yNBoMxpkHV.nRNnU57i7RMJoklC"
|
||||
}
|
||||
],
|
||||
"sort": 0,
|
||||
|
|
@ -152,12 +159,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1753835285790,
|
||||
"modifiedTime": 1753835317605,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754815224721,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items!ijodu5yNBoMxpkHV"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"folder": "TyqMEXhSkjOUq5SA",
|
||||
"name": "Advanced Arcane-Frame Wheelchair",
|
||||
"type": "weapon",
|
||||
"img": "icons/svg/item-bag.svg",
|
||||
"img": "systems/daggerheart/assets/icons/documents/items/ArcaneWheelchair.webp",
|
||||
"system": {
|
||||
"description": "",
|
||||
"actions": {},
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
{
|
||||
"value": "reliable",
|
||||
"effectIds": [
|
||||
"G561ymlNGmaFAYFB"
|
||||
"VnV5X9MBMabhz47b"
|
||||
],
|
||||
"actionIds": []
|
||||
}
|
||||
|
|
@ -108,14 +108,21 @@
|
|||
"img": "icons/skills/melee/strike-sword-slashing-red.webp",
|
||||
"changes": [
|
||||
{
|
||||
"key": "system.bonuses.roll.primaryWeapon.attack",
|
||||
"key": "system.bonuses.roll.primaryWeapon.bonus",
|
||||
"mode": 2,
|
||||
"value": "1"
|
||||
}
|
||||
],
|
||||
"_id": "G561ymlNGmaFAYFB",
|
||||
"_id": "VnV5X9MBMabhz47b",
|
||||
"type": "base",
|
||||
"system": {},
|
||||
"system": {
|
||||
"rangeDependence": {
|
||||
"enabled": false,
|
||||
"type": "withinRange",
|
||||
"target": "hostile",
|
||||
"range": "melee"
|
||||
}
|
||||
},
|
||||
"disabled": false,
|
||||
"duration": {
|
||||
"startTime": null,
|
||||
|
|
@ -131,12 +138,14 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"lastModifiedBy": null
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1754815300372,
|
||||
"modifiedTime": 1754815300372,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items.effects!la3sAWgnvadc4NvP.G561ymlNGmaFAYFB"
|
||||
"_key": "!items.effects!la3sAWgnvadc4NvP.VnV5X9MBMabhz47b"
|
||||
}
|
||||
],
|
||||
"ownership": {
|
||||
|
|
@ -148,12 +157,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1753836715885,
|
||||
"modifiedTime": 1753836789197,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754845968271,
|
||||
"lastModifiedBy": "H02dtt2xvVJvYESk"
|
||||
},
|
||||
"_id": "la3sAWgnvadc4NvP",
|
||||
"sort": 0,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
{
|
||||
"value": "reliable",
|
||||
"effectIds": [
|
||||
"fRPKHzbKRz4yTHAF"
|
||||
"wu2AmDvgeWI3hmRQ"
|
||||
],
|
||||
"actionIds": []
|
||||
}
|
||||
|
|
@ -109,14 +109,21 @@
|
|||
"img": "icons/skills/melee/strike-sword-slashing-red.webp",
|
||||
"changes": [
|
||||
{
|
||||
"key": "system.bonuses.roll.primaryWeapon.attack",
|
||||
"key": "system.bonuses.roll.primaryWeapon.bonus",
|
||||
"mode": 2,
|
||||
"value": "1"
|
||||
}
|
||||
],
|
||||
"_id": "fRPKHzbKRz4yTHAF",
|
||||
"_id": "wu2AmDvgeWI3hmRQ",
|
||||
"type": "base",
|
||||
"system": {},
|
||||
"system": {
|
||||
"rangeDependence": {
|
||||
"enabled": false,
|
||||
"type": "withinRange",
|
||||
"target": "hostile",
|
||||
"range": "melee"
|
||||
}
|
||||
},
|
||||
"disabled": false,
|
||||
"duration": {
|
||||
"startTime": null,
|
||||
|
|
@ -132,14 +139,14 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1753831629563,
|
||||
"modifiedTime": 1753831629563,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1754814950116,
|
||||
"modifiedTime": 1754814950116,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items.effects!WtQAGz0TUgz8Xg70.fRPKHzbKRz4yTHAF"
|
||||
"_key": "!items.effects!WtQAGz0TUgz8Xg70.wu2AmDvgeWI3hmRQ"
|
||||
}
|
||||
],
|
||||
"sort": 0,
|
||||
|
|
@ -152,12 +159,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1753831599435,
|
||||
"modifiedTime": 1753831629573,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754814950120,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items!WtQAGz0TUgz8Xg70"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"folder": "TyqMEXhSkjOUq5SA",
|
||||
"name": "Advanced Heavy-Frame Wheelchair",
|
||||
"type": "weapon",
|
||||
"img": "icons/svg/item-bag.svg",
|
||||
"img": "systems/daggerheart/assets/icons/documents/items/HeavyWheelchair.webp",
|
||||
"system": {
|
||||
"description": "",
|
||||
"actions": {},
|
||||
|
|
@ -148,12 +148,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1753836675558,
|
||||
"modifiedTime": 1753836795905,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754845996869,
|
||||
"lastModifiedBy": "H02dtt2xvVJvYESk"
|
||||
},
|
||||
"_id": "eT2Qwb0RdrLX2hH1",
|
||||
"sort": 0,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"folder": "TyqMEXhSkjOUq5SA",
|
||||
"name": "Advanced Light-Frame Wheelchair",
|
||||
"type": "weapon",
|
||||
"img": "icons/svg/item-bag.svg",
|
||||
"img": "systems/daggerheart/assets/icons/documents/items/LightWheelchair.webp",
|
||||
"system": {
|
||||
"description": "",
|
||||
"actions": {
|
||||
|
|
@ -141,12 +141,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1753836614032,
|
||||
"modifiedTime": 1753836802197,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754846020904,
|
||||
"lastModifiedBy": "H02dtt2xvVJvYESk"
|
||||
},
|
||||
"_id": "BuMfupnCzHbziQ8o",
|
||||
"sort": 0,
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@
|
|||
"parts": [
|
||||
{
|
||||
"value": {
|
||||
"dice": "d8",
|
||||
"dice": "d10",
|
||||
"bonus": 9,
|
||||
"multiplier": "prof",
|
||||
"flatMultiplier": 1,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
{
|
||||
"value": "paired",
|
||||
"effectIds": [
|
||||
"gJ7Ey9CfPZqYgxEO"
|
||||
"MYgB3v3oQ5lIr3VE"
|
||||
],
|
||||
"actionIds": []
|
||||
}
|
||||
|
|
@ -114,9 +114,16 @@
|
|||
"value": "ITEM.@system.tier + 1"
|
||||
}
|
||||
],
|
||||
"_id": "gJ7Ey9CfPZqYgxEO",
|
||||
"system": {
|
||||
"rangeDependence": {
|
||||
"enabled": true,
|
||||
"range": "melee",
|
||||
"target": "hostile",
|
||||
"type": "withinRange"
|
||||
}
|
||||
},
|
||||
"_id": "MYgB3v3oQ5lIr3VE",
|
||||
"type": "base",
|
||||
"system": {},
|
||||
"disabled": false,
|
||||
"duration": {
|
||||
"startTime": null,
|
||||
|
|
@ -132,14 +139,14 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1753794991410,
|
||||
"modifiedTime": 1753794991410,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1754814673988,
|
||||
"modifiedTime": 1754814673988,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items.effects!0thN0BpN05KT8Avx.gJ7Ey9CfPZqYgxEO"
|
||||
"_key": "!items.effects!0thN0BpN05KT8Avx.MYgB3v3oQ5lIr3VE"
|
||||
}
|
||||
],
|
||||
"sort": 0,
|
||||
|
|
@ -152,12 +159,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1753794938643,
|
||||
"modifiedTime": 1753794991413,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754814673991,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items!0thN0BpN05KT8Avx"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,15 +12,7 @@
|
|||
"equipped": false,
|
||||
"secondary": false,
|
||||
"burden": "twoHanded",
|
||||
"weaponFeatures": [
|
||||
{
|
||||
"value": "cumbersome",
|
||||
"effectIds": [
|
||||
"hl0S2LrBY5Mg69q6"
|
||||
],
|
||||
"actionIds": []
|
||||
}
|
||||
],
|
||||
"weaponFeatures": [],
|
||||
"attack": {
|
||||
"name": "Attack",
|
||||
"img": "icons/skills/melee/blood-slash-foam-red.webp",
|
||||
|
|
@ -51,8 +43,8 @@
|
|||
"parts": [
|
||||
{
|
||||
"value": {
|
||||
"dice": "d10",
|
||||
"bonus": 8,
|
||||
"dice": "d8",
|
||||
"bonus": 9,
|
||||
"multiplier": "prof",
|
||||
"flatMultiplier": 1,
|
||||
"custom": {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"name": "Arcane-Frame Wheelchair",
|
||||
"type": "weapon",
|
||||
"_id": "XRChepscgr75Uug7",
|
||||
"img": "icons/svg/item-bag.svg",
|
||||
"img": "systems/daggerheart/assets/icons/documents/items/ArcaneWheelchair.webp",
|
||||
"system": {
|
||||
"description": "",
|
||||
"actions": {},
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
{
|
||||
"value": "reliable",
|
||||
"effectIds": [
|
||||
"G561ymlNGmaFAYFB"
|
||||
"dXHsy9qr5FWZqsVq"
|
||||
],
|
||||
"actionIds": []
|
||||
}
|
||||
|
|
@ -109,14 +109,21 @@
|
|||
"img": "icons/skills/melee/strike-sword-slashing-red.webp",
|
||||
"changes": [
|
||||
{
|
||||
"key": "system.bonuses.roll.primaryWeapon.attack",
|
||||
"key": "system.bonuses.roll.primaryWeapon.bonus",
|
||||
"mode": 2,
|
||||
"value": "1"
|
||||
}
|
||||
],
|
||||
"_id": "G561ymlNGmaFAYFB",
|
||||
"_id": "dXHsy9qr5FWZqsVq",
|
||||
"type": "base",
|
||||
"system": {},
|
||||
"system": {
|
||||
"rangeDependence": {
|
||||
"enabled": false,
|
||||
"type": "withinRange",
|
||||
"target": "hostile",
|
||||
"range": "melee"
|
||||
}
|
||||
},
|
||||
"disabled": false,
|
||||
"duration": {
|
||||
"startTime": null,
|
||||
|
|
@ -132,14 +139,14 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1753836707582,
|
||||
"modifiedTime": 1753836707582,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1754815278217,
|
||||
"modifiedTime": 1754815278217,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items.effects!XRChepscgr75Uug7.G561ymlNGmaFAYFB"
|
||||
"_key": "!items.effects!XRChepscgr75Uug7.dXHsy9qr5FWZqsVq"
|
||||
}
|
||||
],
|
||||
"sort": 0,
|
||||
|
|
@ -152,12 +159,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1753836689082,
|
||||
"modifiedTime": 1753836707594,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754845945327,
|
||||
"lastModifiedBy": "H02dtt2xvVJvYESk"
|
||||
},
|
||||
"_key": "!items!XRChepscgr75Uug7"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
{
|
||||
"value": "reliable",
|
||||
"effectIds": [
|
||||
"GNwIa1EAaa0T0RZi"
|
||||
"mqcpj2cFAprf2AmY"
|
||||
],
|
||||
"actionIds": []
|
||||
}
|
||||
|
|
@ -109,14 +109,21 @@
|
|||
"img": "icons/skills/melee/strike-sword-slashing-red.webp",
|
||||
"changes": [
|
||||
{
|
||||
"key": "system.bonuses.roll.primaryWeapon.attack",
|
||||
"key": "system.bonuses.roll.primaryWeapon.bonus",
|
||||
"mode": 2,
|
||||
"value": "1"
|
||||
}
|
||||
],
|
||||
"_id": "GNwIa1EAaa0T0RZi",
|
||||
"_id": "mqcpj2cFAprf2AmY",
|
||||
"type": "base",
|
||||
"system": {},
|
||||
"system": {
|
||||
"rangeDependence": {
|
||||
"enabled": false,
|
||||
"type": "withinRange",
|
||||
"target": "hostile",
|
||||
"range": "melee"
|
||||
}
|
||||
},
|
||||
"disabled": false,
|
||||
"duration": {
|
||||
"startTime": null,
|
||||
|
|
@ -132,14 +139,14 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1753827762112,
|
||||
"modifiedTime": 1753827762112,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1754814769818,
|
||||
"modifiedTime": 1754814769818,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items.effects!1cwWNt4sqlgA8gCT.GNwIa1EAaa0T0RZi"
|
||||
"_key": "!items.effects!1cwWNt4sqlgA8gCT.mqcpj2cFAprf2AmY"
|
||||
}
|
||||
],
|
||||
"sort": 0,
|
||||
|
|
@ -152,12 +159,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1753827734892,
|
||||
"modifiedTime": 1753827762118,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754814769821,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items!1cwWNt4sqlgA8gCT"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"name": "Heavy-Frame Wheelchair",
|
||||
"type": "weapon",
|
||||
"_id": "XjPQjhRCH08VUIbr",
|
||||
"img": "icons/svg/item-bag.svg",
|
||||
"img": "systems/daggerheart/assets/icons/documents/items/HeavyWheelchair.webp",
|
||||
"system": {
|
||||
"description": "",
|
||||
"actions": {},
|
||||
|
|
@ -152,12 +152,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1753836652314,
|
||||
"modifiedTime": 1753836667128,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754845988869,
|
||||
"lastModifiedBy": "H02dtt2xvVJvYESk"
|
||||
},
|
||||
"_key": "!items!XjPQjhRCH08VUIbr"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"folder": "fFuMdvpD1F3UshmM",
|
||||
"name": "Improved Arcane-Frame Wheelchair",
|
||||
"type": "weapon",
|
||||
"img": "icons/svg/item-bag.svg",
|
||||
"img": "systems/daggerheart/assets/icons/documents/items/ArcaneWheelchair.webp",
|
||||
"system": {
|
||||
"description": "",
|
||||
"actions": {},
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
{
|
||||
"value": "reliable",
|
||||
"effectIds": [
|
||||
"G561ymlNGmaFAYFB"
|
||||
"1f6fFhOLwZrmA6e5"
|
||||
],
|
||||
"actionIds": []
|
||||
}
|
||||
|
|
@ -108,14 +108,21 @@
|
|||
"img": "icons/skills/melee/strike-sword-slashing-red.webp",
|
||||
"changes": [
|
||||
{
|
||||
"key": "system.bonuses.roll.primaryWeapon.attack",
|
||||
"key": "system.bonuses.roll.primaryWeapon.bonus",
|
||||
"mode": 2,
|
||||
"value": "1"
|
||||
}
|
||||
],
|
||||
"_id": "G561ymlNGmaFAYFB",
|
||||
"_id": "1f6fFhOLwZrmA6e5",
|
||||
"type": "base",
|
||||
"system": {},
|
||||
"system": {
|
||||
"rangeDependence": {
|
||||
"enabled": false,
|
||||
"type": "withinRange",
|
||||
"target": "hostile",
|
||||
"range": "melee"
|
||||
}
|
||||
},
|
||||
"disabled": false,
|
||||
"duration": {
|
||||
"startTime": null,
|
||||
|
|
@ -131,12 +138,14 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"lastModifiedBy": null
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1754815290648,
|
||||
"modifiedTime": 1754815290648,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items.effects!N9P695V5KKlJbAY5.G561ymlNGmaFAYFB"
|
||||
"_key": "!items.effects!N9P695V5KKlJbAY5.1f6fFhOLwZrmA6e5"
|
||||
}
|
||||
],
|
||||
"ownership": {
|
||||
|
|
@ -148,12 +157,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1753836714712,
|
||||
"modifiedTime": 1753836748404,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754845960700,
|
||||
"lastModifiedBy": "H02dtt2xvVJvYESk"
|
||||
},
|
||||
"_id": "N9P695V5KKlJbAY5",
|
||||
"sort": 0,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
{
|
||||
"value": "reliable",
|
||||
"effectIds": [
|
||||
"xU0DD5ydbwmXMKtF"
|
||||
"228lcQpohdJ3Bbga"
|
||||
],
|
||||
"actionIds": []
|
||||
}
|
||||
|
|
@ -109,14 +109,21 @@
|
|||
"img": "icons/skills/melee/strike-sword-slashing-red.webp",
|
||||
"changes": [
|
||||
{
|
||||
"key": "system.bonuses.roll.primaryWeapon.attack",
|
||||
"key": "system.bonuses.roll.primaryWeapon.bonus",
|
||||
"mode": 2,
|
||||
"value": "1"
|
||||
}
|
||||
],
|
||||
"_id": "xU0DD5ydbwmXMKtF",
|
||||
"_id": "228lcQpohdJ3Bbga",
|
||||
"type": "base",
|
||||
"system": {},
|
||||
"system": {
|
||||
"rangeDependence": {
|
||||
"enabled": false,
|
||||
"type": "withinRange",
|
||||
"target": "hostile",
|
||||
"range": "melee"
|
||||
}
|
||||
},
|
||||
"disabled": false,
|
||||
"duration": {
|
||||
"startTime": null,
|
||||
|
|
@ -132,14 +139,14 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1753829126416,
|
||||
"modifiedTime": 1753829126416,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1754814935810,
|
||||
"modifiedTime": 1754814935810,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items.effects!OcKeLJxvmdT81VBc.xU0DD5ydbwmXMKtF"
|
||||
"_key": "!items.effects!OcKeLJxvmdT81VBc.228lcQpohdJ3Bbga"
|
||||
}
|
||||
],
|
||||
"sort": 0,
|
||||
|
|
@ -152,12 +159,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1753829098118,
|
||||
"modifiedTime": 1753829367508,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754814935815,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items!OcKeLJxvmdT81VBc"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"folder": "fFuMdvpD1F3UshmM",
|
||||
"name": "Improved Heavy-Frame Wheelchair",
|
||||
"type": "weapon",
|
||||
"img": "icons/svg/item-bag.svg",
|
||||
"img": "systems/daggerheart/assets/icons/documents/items/HeavyWheelchair.webp",
|
||||
"system": {
|
||||
"description": "",
|
||||
"actions": {},
|
||||
|
|
@ -148,12 +148,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1753836674233,
|
||||
"modifiedTime": 1753836769685,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754845992757,
|
||||
"lastModifiedBy": "H02dtt2xvVJvYESk"
|
||||
},
|
||||
"_id": "L5KeCtrs768PmYWW",
|
||||
"sort": 0,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"folder": "fFuMdvpD1F3UshmM",
|
||||
"name": "Improved Light-Frame Wheelchair",
|
||||
"type": "weapon",
|
||||
"img": "icons/svg/item-bag.svg",
|
||||
"img": "systems/daggerheart/assets/icons/documents/items/LightWheelchair.webp",
|
||||
"system": {
|
||||
"description": "",
|
||||
"actions": {
|
||||
|
|
@ -141,12 +141,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1753836612291,
|
||||
"modifiedTime": 1753836778961,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754846018260,
|
||||
"lastModifiedBy": "H02dtt2xvVJvYESk"
|
||||
},
|
||||
"_id": "ZJsetdHKV77ygtCE",
|
||||
"sort": 0,
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@
|
|||
"parts": [
|
||||
{
|
||||
"value": {
|
||||
"dice": "d8",
|
||||
"dice": "d10",
|
||||
"bonus": 6,
|
||||
"multiplier": "prof",
|
||||
"flatMultiplier": 1,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
{
|
||||
"value": "paired",
|
||||
"effectIds": [
|
||||
"cSmiIOXeuw0xpjel"
|
||||
"9iHHwd9BxkBsV9lY"
|
||||
],
|
||||
"actionIds": []
|
||||
}
|
||||
|
|
@ -114,9 +114,16 @@
|
|||
"value": "ITEM.@system.tier + 1"
|
||||
}
|
||||
],
|
||||
"_id": "cSmiIOXeuw0xpjel",
|
||||
"system": {
|
||||
"rangeDependence": {
|
||||
"enabled": true,
|
||||
"range": "melee",
|
||||
"target": "hostile",
|
||||
"type": "withinRange"
|
||||
}
|
||||
},
|
||||
"_id": "9iHHwd9BxkBsV9lY",
|
||||
"type": "base",
|
||||
"system": {},
|
||||
"disabled": false,
|
||||
"duration": {
|
||||
"startTime": null,
|
||||
|
|
@ -132,14 +139,14 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1753794021004,
|
||||
"modifiedTime": 1753794021004,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1754814695260,
|
||||
"modifiedTime": 1754814695260,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items.effects!rSyBNRwemBVuTo3H.cSmiIOXeuw0xpjel"
|
||||
"_key": "!items.effects!rSyBNRwemBVuTo3H.9iHHwd9BxkBsV9lY"
|
||||
}
|
||||
],
|
||||
"sort": 0,
|
||||
|
|
@ -152,12 +159,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1753744566951,
|
||||
"modifiedTime": 1753794021010,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754814695265,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items!rSyBNRwemBVuTo3H"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
{
|
||||
"value": "paired",
|
||||
"effectIds": [
|
||||
"rOjtLkrnI9EZHJm8"
|
||||
"JHIUGyZH5q83ODvd"
|
||||
],
|
||||
"actionIds": []
|
||||
}
|
||||
|
|
@ -114,9 +114,16 @@
|
|||
"value": "ITEM.@system.tier + 1"
|
||||
}
|
||||
],
|
||||
"_id": "rOjtLkrnI9EZHJm8",
|
||||
"system": {
|
||||
"rangeDependence": {
|
||||
"enabled": true,
|
||||
"range": "melee",
|
||||
"target": "hostile",
|
||||
"type": "withinRange"
|
||||
}
|
||||
},
|
||||
"_id": "JHIUGyZH5q83ODvd",
|
||||
"type": "base",
|
||||
"system": {},
|
||||
"disabled": false,
|
||||
"duration": {
|
||||
"startTime": null,
|
||||
|
|
@ -132,14 +139,14 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1753794340876,
|
||||
"modifiedTime": 1753794340876,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1754814703717,
|
||||
"modifiedTime": 1754814703717,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items.effects!nMuF8ZDZ2aXZVTg6.rOjtLkrnI9EZHJm8"
|
||||
"_key": "!items.effects!nMuF8ZDZ2aXZVTg6.JHIUGyZH5q83ODvd"
|
||||
}
|
||||
],
|
||||
"sort": 0,
|
||||
|
|
@ -152,12 +159,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1753794291887,
|
||||
"modifiedTime": 1753794340879,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754814703719,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items!nMuF8ZDZ2aXZVTg6"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,15 +12,7 @@
|
|||
"equipped": false,
|
||||
"secondary": false,
|
||||
"burden": "twoHanded",
|
||||
"weaponFeatures": [
|
||||
{
|
||||
"value": "cumbersome",
|
||||
"effectIds": [
|
||||
"8twXPJELZpvFWA5K"
|
||||
],
|
||||
"actionIds": []
|
||||
}
|
||||
],
|
||||
"weaponFeatures": [],
|
||||
"attack": {
|
||||
"name": "Attack",
|
||||
"img": "icons/skills/melee/blood-slash-foam-red.webp",
|
||||
|
|
@ -51,8 +43,8 @@
|
|||
"parts": [
|
||||
{
|
||||
"value": {
|
||||
"dice": "d10",
|
||||
"bonus": 5,
|
||||
"dice": "d8",
|
||||
"bonus": 6,
|
||||
"multiplier": "prof",
|
||||
"flatMultiplier": 1,
|
||||
"custom": {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
{
|
||||
"value": "reliable",
|
||||
"effectIds": [
|
||||
"FlmOrbhYbieIAVJL"
|
||||
"LvxPAfrKuRfgubGV"
|
||||
],
|
||||
"actionIds": []
|
||||
}
|
||||
|
|
@ -109,14 +109,21 @@
|
|||
"img": "icons/skills/melee/strike-sword-slashing-red.webp",
|
||||
"changes": [
|
||||
{
|
||||
"key": "system.bonuses.roll.primaryWeapon.attack",
|
||||
"key": "system.bonuses.roll.primaryWeapon.bonus",
|
||||
"mode": 2,
|
||||
"value": "1"
|
||||
}
|
||||
],
|
||||
"_id": "FlmOrbhYbieIAVJL",
|
||||
"_id": "LvxPAfrKuRfgubGV",
|
||||
"type": "base",
|
||||
"system": {},
|
||||
"system": {
|
||||
"rangeDependence": {
|
||||
"enabled": false,
|
||||
"type": "withinRange",
|
||||
"target": "hostile",
|
||||
"range": "melee"
|
||||
}
|
||||
},
|
||||
"disabled": false,
|
||||
"duration": {
|
||||
"startTime": null,
|
||||
|
|
@ -132,14 +139,14 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1753831441325,
|
||||
"modifiedTime": 1753831441325,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1754815023490,
|
||||
"modifiedTime": 1754815023490,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items.effects!q382JqMkqLaaFLIr.FlmOrbhYbieIAVJL"
|
||||
"_key": "!items.effects!q382JqMkqLaaFLIr.LvxPAfrKuRfgubGV"
|
||||
}
|
||||
],
|
||||
"sort": 0,
|
||||
|
|
@ -152,12 +159,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1753831418620,
|
||||
"modifiedTime": 1753831441332,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754815023493,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items!q382JqMkqLaaFLIr"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,63 +7,22 @@
|
|||
"system": {
|
||||
"description": "",
|
||||
"actions": {
|
||||
"fRKcEeShWSLkoExh": {
|
||||
"B0uT1D1WRidiHxyh": {
|
||||
"type": "effect",
|
||||
"actionType": "action",
|
||||
"chatDisplay": true,
|
||||
"name": "Double Up",
|
||||
"description": "When you make an attack with your primary weapon, you can deal damage to another target within Melee range.",
|
||||
"img": "icons/skills/melee/strike-slashes-orange.webp",
|
||||
"_id": "fRKcEeShWSLkoExh",
|
||||
"_id": "B0uT1D1WRidiHxyh",
|
||||
"effects": [],
|
||||
"systemPath": "actions",
|
||||
"cost": [],
|
||||
"uses": {
|
||||
"value": null,
|
||||
"max": null,
|
||||
"recovery": null
|
||||
},
|
||||
"target": {
|
||||
"type": "any",
|
||||
"amount": null
|
||||
}
|
||||
},
|
||||
"lVsEmJwjYgpYL9l4": {
|
||||
"type": "effect",
|
||||
"actionType": "action",
|
||||
"chatDisplay": true,
|
||||
"name": "Double Up",
|
||||
"description": "When you make an attack with your primary weapon, you can deal damage to another target within Melee range.",
|
||||
"img": "icons/skills/melee/strike-slashes-orange.webp",
|
||||
"_id": "lVsEmJwjYgpYL9l4",
|
||||
"effects": [],
|
||||
"systemPath": "actions",
|
||||
"cost": [],
|
||||
"uses": {
|
||||
"value": null,
|
||||
"max": null,
|
||||
"recovery": null
|
||||
},
|
||||
"target": {
|
||||
"type": "any",
|
||||
"amount": null
|
||||
}
|
||||
},
|
||||
"2ndqofzTHsEUMxsm": {
|
||||
"type": "effect",
|
||||
"actionType": "action",
|
||||
"chatDisplay": true,
|
||||
"name": "Double Up",
|
||||
"description": "When you make an attack with your primary weapon, you can deal damage to another target within Melee range.",
|
||||
"img": "icons/skills/melee/strike-slashes-orange.webp",
|
||||
"_id": "2ndqofzTHsEUMxsm",
|
||||
"effects": [],
|
||||
"systemPath": "actions",
|
||||
"cost": [],
|
||||
"uses": {
|
||||
"value": null,
|
||||
"max": null,
|
||||
"recovery": null
|
||||
"recovery": null,
|
||||
"consumeOnSuccess": false
|
||||
},
|
||||
"target": {
|
||||
"type": "any",
|
||||
|
|
@ -81,7 +40,7 @@
|
|||
"value": "doubledUp",
|
||||
"effectIds": [],
|
||||
"actionIds": [
|
||||
"2ndqofzTHsEUMxsm"
|
||||
"B0uT1D1WRidiHxyh"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
|
@ -177,12 +136,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1753797258168,
|
||||
"modifiedTime": 1753798882899,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754814600761,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items!SFqganS8Du4aEKjQ"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"folder": "beilKE5ZPAihKg3O",
|
||||
"name": "Legendary Arcane-Frame Wheelchair",
|
||||
"type": "weapon",
|
||||
"img": "icons/svg/item-bag.svg",
|
||||
"img": "systems/daggerheart/assets/icons/documents/items/ArcaneWheelchair.webp",
|
||||
"system": {
|
||||
"description": "",
|
||||
"actions": {},
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
{
|
||||
"value": "reliable",
|
||||
"effectIds": [
|
||||
"G561ymlNGmaFAYFB"
|
||||
"TvsoAiqHCwgtYat1"
|
||||
],
|
||||
"actionIds": []
|
||||
}
|
||||
|
|
@ -108,14 +108,21 @@
|
|||
"img": "icons/skills/melee/strike-sword-slashing-red.webp",
|
||||
"changes": [
|
||||
{
|
||||
"key": "system.bonuses.roll.primaryWeapon.attack",
|
||||
"key": "system.bonuses.roll.primaryWeapon.bonus",
|
||||
"mode": 2,
|
||||
"value": "1"
|
||||
}
|
||||
],
|
||||
"_id": "G561ymlNGmaFAYFB",
|
||||
"_id": "TvsoAiqHCwgtYat1",
|
||||
"type": "base",
|
||||
"system": {},
|
||||
"system": {
|
||||
"rangeDependence": {
|
||||
"enabled": false,
|
||||
"type": "withinRange",
|
||||
"target": "hostile",
|
||||
"range": "melee"
|
||||
}
|
||||
},
|
||||
"disabled": false,
|
||||
"duration": {
|
||||
"startTime": null,
|
||||
|
|
@ -131,12 +138,14 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"lastModifiedBy": null
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1754815308723,
|
||||
"modifiedTime": 1754815308723,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items.effects!gA2tiET9VHGhwMoO.G561ymlNGmaFAYFB"
|
||||
"_key": "!items.effects!gA2tiET9VHGhwMoO.TvsoAiqHCwgtYat1"
|
||||
}
|
||||
],
|
||||
"ownership": {
|
||||
|
|
@ -148,12 +157,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1753836717240,
|
||||
"modifiedTime": 1753836812453,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754845972571,
|
||||
"lastModifiedBy": "H02dtt2xvVJvYESk"
|
||||
},
|
||||
"_id": "gA2tiET9VHGhwMoO",
|
||||
"sort": 0,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
{
|
||||
"value": "reliable",
|
||||
"effectIds": [
|
||||
"lmUzKw6J6RW3krRT"
|
||||
"ujb6VAqjyXmfnnjy"
|
||||
],
|
||||
"actionIds": []
|
||||
}
|
||||
|
|
@ -109,14 +109,21 @@
|
|||
"img": "icons/skills/melee/strike-sword-slashing-red.webp",
|
||||
"changes": [
|
||||
{
|
||||
"key": "system.bonuses.roll.primaryWeapon.attack",
|
||||
"key": "system.bonuses.roll.primaryWeapon.bonus",
|
||||
"mode": 2,
|
||||
"value": "1"
|
||||
}
|
||||
],
|
||||
"_id": "lmUzKw6J6RW3krRT",
|
||||
"_id": "ujb6VAqjyXmfnnjy",
|
||||
"type": "base",
|
||||
"system": {},
|
||||
"system": {
|
||||
"rangeDependence": {
|
||||
"enabled": false,
|
||||
"type": "withinRange",
|
||||
"target": "hostile",
|
||||
"range": "melee"
|
||||
}
|
||||
},
|
||||
"disabled": false,
|
||||
"duration": {
|
||||
"startTime": null,
|
||||
|
|
@ -132,14 +139,14 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1753834461846,
|
||||
"modifiedTime": 1753834461846,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1754814961439,
|
||||
"modifiedTime": 1754814961439,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items.effects!y3hfTPfZhMognyaJ.lmUzKw6J6RW3krRT"
|
||||
"_key": "!items.effects!y3hfTPfZhMognyaJ.ujb6VAqjyXmfnnjy"
|
||||
}
|
||||
],
|
||||
"sort": 0,
|
||||
|
|
@ -152,12 +159,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1753834430378,
|
||||
"modifiedTime": 1753834465538,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754814961454,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items!y3hfTPfZhMognyaJ"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"folder": "beilKE5ZPAihKg3O",
|
||||
"name": "Legendary Heavy-Frame Wheelchair",
|
||||
"type": "weapon",
|
||||
"img": "icons/svg/item-bag.svg",
|
||||
"img": "systems/daggerheart/assets/icons/documents/items/HeavyWheelchair.webp",
|
||||
"system": {
|
||||
"description": "",
|
||||
"actions": {},
|
||||
|
|
@ -148,12 +148,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1753836676831,
|
||||
"modifiedTime": 1753836820180,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754846000470,
|
||||
"lastModifiedBy": "H02dtt2xvVJvYESk"
|
||||
},
|
||||
"_id": "S6nB0CNlzdU05o5U",
|
||||
"sort": 0,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"folder": "beilKE5ZPAihKg3O",
|
||||
"name": "Legendary Light-Frame Wheelchair",
|
||||
"type": "weapon",
|
||||
"img": "icons/svg/item-bag.svg",
|
||||
"img": "systems/daggerheart/assets/icons/documents/items/LightWheelchair.webp",
|
||||
"system": {
|
||||
"description": "",
|
||||
"actions": {
|
||||
|
|
@ -141,12 +141,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1753836615437,
|
||||
"modifiedTime": 1753836826572,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754846023338,
|
||||
"lastModifiedBy": "H02dtt2xvVJvYESk"
|
||||
},
|
||||
"_id": "Xt8tVSn5Fu6ly6LF",
|
||||
"sort": 0,
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@
|
|||
"parts": [
|
||||
{
|
||||
"value": {
|
||||
"dice": "d8",
|
||||
"dice": "d10",
|
||||
"bonus": 12,
|
||||
"multiplier": "prof",
|
||||
"flatMultiplier": 1,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
{
|
||||
"value": "paired",
|
||||
"effectIds": [
|
||||
"DgZQSBJx9JmoOngB"
|
||||
"VFt61c2Apfbli2dG"
|
||||
],
|
||||
"actionIds": []
|
||||
}
|
||||
|
|
@ -114,9 +114,16 @@
|
|||
"value": "ITEM.@system.tier + 1"
|
||||
}
|
||||
],
|
||||
"_id": "DgZQSBJx9JmoOngB",
|
||||
"system": {
|
||||
"rangeDependence": {
|
||||
"enabled": true,
|
||||
"range": "melee",
|
||||
"target": "hostile",
|
||||
"type": "withinRange"
|
||||
}
|
||||
},
|
||||
"_id": "VFt61c2Apfbli2dG",
|
||||
"type": "base",
|
||||
"system": {},
|
||||
"disabled": false,
|
||||
"duration": {
|
||||
"startTime": null,
|
||||
|
|
@ -132,14 +139,14 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1753796957432,
|
||||
"modifiedTime": 1753796957432,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1754814544486,
|
||||
"modifiedTime": 1754814544486,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items.effects!dEumq3BIZBk5xYTk.DgZQSBJx9JmoOngB"
|
||||
"_key": "!items.effects!dEumq3BIZBk5xYTk.VFt61c2Apfbli2dG"
|
||||
}
|
||||
],
|
||||
"sort": 0,
|
||||
|
|
@ -152,12 +159,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1753796913551,
|
||||
"modifiedTime": 1753796957439,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754814544510,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items!dEumq3BIZBk5xYTk"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
{
|
||||
"value": "paired",
|
||||
"effectIds": [
|
||||
"Wjl3MEwNdQPeY63u"
|
||||
"rnVm0jSEtdWhKGCh"
|
||||
],
|
||||
"actionIds": []
|
||||
}
|
||||
|
|
@ -114,9 +114,16 @@
|
|||
"value": "ITEM.@system.tier + 1"
|
||||
}
|
||||
],
|
||||
"_id": "Wjl3MEwNdQPeY63u",
|
||||
"system": {
|
||||
"rangeDependence": {
|
||||
"enabled": true,
|
||||
"range": "melee",
|
||||
"target": "hostile",
|
||||
"type": "withinRange"
|
||||
}
|
||||
},
|
||||
"_id": "rnVm0jSEtdWhKGCh",
|
||||
"type": "base",
|
||||
"system": {},
|
||||
"disabled": false,
|
||||
"duration": {
|
||||
"startTime": null,
|
||||
|
|
@ -132,14 +139,14 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1753797088930,
|
||||
"modifiedTime": 1753797088930,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1754814562988,
|
||||
"modifiedTime": 1754814562988,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items.effects!Px3Rh3kIvAqyISxJ.Wjl3MEwNdQPeY63u"
|
||||
"_key": "!items.effects!Px3Rh3kIvAqyISxJ.rnVm0jSEtdWhKGCh"
|
||||
}
|
||||
],
|
||||
"sort": 0,
|
||||
|
|
@ -152,12 +159,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1753797057472,
|
||||
"modifiedTime": 1753797088933,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754814562995,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items!Px3Rh3kIvAqyISxJ"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,15 +12,7 @@
|
|||
"equipped": false,
|
||||
"secondary": false,
|
||||
"burden": "twoHanded",
|
||||
"weaponFeatures": [
|
||||
{
|
||||
"value": "cumbersome",
|
||||
"effectIds": [
|
||||
"f44KWDgCQeKYfccr"
|
||||
],
|
||||
"actionIds": []
|
||||
}
|
||||
],
|
||||
"weaponFeatures": [],
|
||||
"attack": {
|
||||
"name": "Attack",
|
||||
"img": "icons/skills/melee/blood-slash-foam-red.webp",
|
||||
|
|
@ -51,8 +43,8 @@
|
|||
"parts": [
|
||||
{
|
||||
"value": {
|
||||
"dice": "d10",
|
||||
"bonus": 11,
|
||||
"dice": "d8",
|
||||
"bonus": 12,
|
||||
"multiplier": "prof",
|
||||
"flatMultiplier": 1,
|
||||
"custom": {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"name": "Light-Frame Wheelchair",
|
||||
"type": "weapon",
|
||||
"_id": "iaGnlUkShBgdeMo0",
|
||||
"img": "icons/svg/item-bag.svg",
|
||||
"img": "systems/daggerheart/assets/icons/documents/items/LightWheelchair.webp",
|
||||
"system": {
|
||||
"description": "",
|
||||
"actions": {
|
||||
|
|
@ -143,12 +143,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1753836579296,
|
||||
"modifiedTime": 1753836587147,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754846015528,
|
||||
"lastModifiedBy": "H02dtt2xvVJvYESk"
|
||||
},
|
||||
"_key": "!items!iaGnlUkShBgdeMo0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@
|
|||
"parts": [
|
||||
{
|
||||
"value": {
|
||||
"dice": "d8",
|
||||
"dice": "d10",
|
||||
"bonus": 3,
|
||||
"multiplier": "prof",
|
||||
"flatMultiplier": 1,
|
||||
|
|
|
|||
|
|
@ -7,21 +7,22 @@
|
|||
"system": {
|
||||
"description": "",
|
||||
"actions": {
|
||||
"KGJJgz0SMdY0f0em": {
|
||||
"dNN2KOfUxGzQ2yjY": {
|
||||
"type": "effect",
|
||||
"actionType": "action",
|
||||
"chatDisplay": true,
|
||||
"name": "Lock On",
|
||||
"description": "On a successful attack, your next attack against the same target with your primary weapon automatically succeeds.",
|
||||
"img": "icons/skills/targeting/crosshair-arrowhead-blue.webp",
|
||||
"_id": "KGJJgz0SMdY0f0em",
|
||||
"_id": "dNN2KOfUxGzQ2yjY",
|
||||
"effects": [],
|
||||
"systemPath": "actions",
|
||||
"cost": [],
|
||||
"uses": {
|
||||
"value": null,
|
||||
"max": null,
|
||||
"recovery": null
|
||||
"recovery": null,
|
||||
"consumeOnSuccess": false
|
||||
},
|
||||
"target": {
|
||||
"type": "any",
|
||||
|
|
@ -36,10 +37,10 @@
|
|||
"burden": "oneHanded",
|
||||
"weaponFeatures": [
|
||||
{
|
||||
"value": "lockedon",
|
||||
"value": "lockedOn",
|
||||
"effectIds": [],
|
||||
"actionIds": [
|
||||
"KGJJgz0SMdY0f0em"
|
||||
"dNN2KOfUxGzQ2yjY"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
|
@ -135,12 +136,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1753797317938,
|
||||
"modifiedTime": 1753797376548,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754814518028,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items!SxcblanBvqaest3A"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
{
|
||||
"value": "paired",
|
||||
"effectIds": [
|
||||
"VII5oRJrQTsSir0E"
|
||||
"5RpOUFs0kDhzwltM"
|
||||
],
|
||||
"actionIds": []
|
||||
}
|
||||
|
|
@ -114,9 +114,16 @@
|
|||
"value": "ITEM.@system.tier + 1"
|
||||
}
|
||||
],
|
||||
"_id": "VII5oRJrQTsSir0E",
|
||||
"system": {
|
||||
"rangeDependence": {
|
||||
"enabled": true,
|
||||
"range": "melee",
|
||||
"target": "hostile",
|
||||
"type": "withinRange"
|
||||
}
|
||||
},
|
||||
"_id": "5RpOUFs0kDhzwltM",
|
||||
"type": "base",
|
||||
"system": {},
|
||||
"disabled": false,
|
||||
"duration": {
|
||||
"startTime": null,
|
||||
|
|
@ -132,14 +139,14 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1753793980974,
|
||||
"modifiedTime": 1753793980974,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1754814729443,
|
||||
"modifiedTime": 1754814729443,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items.effects!cjGZpXCoshEqi1FI.VII5oRJrQTsSir0E"
|
||||
"_key": "!items.effects!cjGZpXCoshEqi1FI.5RpOUFs0kDhzwltM"
|
||||
}
|
||||
],
|
||||
"sort": 0,
|
||||
|
|
@ -152,12 +159,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1753741549716,
|
||||
"modifiedTime": 1753793980983,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754814729447,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items!cjGZpXCoshEqi1FI"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
{
|
||||
"value": "paired",
|
||||
"effectIds": [
|
||||
"2OLsxbZJqMgk680J"
|
||||
"wK6ccFAirp9HYK5Q"
|
||||
],
|
||||
"actionIds": []
|
||||
}
|
||||
|
|
@ -114,9 +114,16 @@
|
|||
"value": "ITEM.@system.tier + 1"
|
||||
}
|
||||
],
|
||||
"_id": "2OLsxbZJqMgk680J",
|
||||
"system": {
|
||||
"rangeDependence": {
|
||||
"enabled": true,
|
||||
"range": "melee",
|
||||
"target": "hostile",
|
||||
"type": "withinRange"
|
||||
}
|
||||
},
|
||||
"_id": "wK6ccFAirp9HYK5Q",
|
||||
"type": "base",
|
||||
"system": {},
|
||||
"disabled": false,
|
||||
"duration": {
|
||||
"startTime": null,
|
||||
|
|
@ -132,14 +139,14 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1753794165509,
|
||||
"modifiedTime": 1753794165509,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1754814738851,
|
||||
"modifiedTime": 1754814738851,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items.effects!wKklDxs5nkzILNp4.2OLsxbZJqMgk680J"
|
||||
"_key": "!items.effects!wKklDxs5nkzILNp4.wK6ccFAirp9HYK5Q"
|
||||
}
|
||||
],
|
||||
"sort": 0,
|
||||
|
|
@ -152,12 +159,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1753744141625,
|
||||
"modifiedTime": 1753794165511,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754814738854,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items!wKklDxs5nkzILNp4"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,15 +12,7 @@
|
|||
"equipped": false,
|
||||
"secondary": false,
|
||||
"burden": "twoHanded",
|
||||
"weaponFeatures": [
|
||||
{
|
||||
"value": "cumbersome",
|
||||
"effectIds": [
|
||||
"Z5MnVI8EOOgzRdXC"
|
||||
],
|
||||
"actionIds": []
|
||||
}
|
||||
],
|
||||
"weaponFeatures": [],
|
||||
"attack": {
|
||||
"name": "Attack",
|
||||
"img": "icons/skills/melee/blood-slash-foam-red.webp",
|
||||
|
|
@ -51,8 +43,8 @@
|
|||
"parts": [
|
||||
{
|
||||
"value": {
|
||||
"dice": "d10",
|
||||
"bonus": 2,
|
||||
"dice": "d8",
|
||||
"bonus": 3,
|
||||
"multiplier": "prof",
|
||||
"flatMultiplier": 1,
|
||||
"custom": {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
{
|
||||
"value": "doubleDuty",
|
||||
"effectIds": [
|
||||
"qo4VPSV0VZha1ya2"
|
||||
"d3TJtlpoHBCztbom"
|
||||
],
|
||||
"actionIds": []
|
||||
}
|
||||
|
|
@ -119,9 +119,16 @@
|
|||
"value": "1"
|
||||
}
|
||||
],
|
||||
"_id": "qo4VPSV0VZha1ya2",
|
||||
"system": {
|
||||
"rangeDependence": {
|
||||
"enabled": true,
|
||||
"range": "melee",
|
||||
"target": "hostile",
|
||||
"type": "withinRange"
|
||||
}
|
||||
},
|
||||
"_id": "d3TJtlpoHBCztbom",
|
||||
"type": "base",
|
||||
"system": {},
|
||||
"disabled": false,
|
||||
"duration": {
|
||||
"startTime": null,
|
||||
|
|
@ -137,14 +144,14 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1753794551639,
|
||||
"modifiedTime": 1753794551639,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1754814481208,
|
||||
"modifiedTime": 1754814481208,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items.effects!vzyzFwLUniWZV1rt.qo4VPSV0VZha1ya2"
|
||||
"_key": "!items.effects!vzyzFwLUniWZV1rt.d3TJtlpoHBCztbom"
|
||||
}
|
||||
],
|
||||
"sort": 0,
|
||||
|
|
@ -157,12 +164,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1753794535926,
|
||||
"modifiedTime": 1753794665373,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754814481212,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items!vzyzFwLUniWZV1rt"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
{
|
||||
"value": "reliable",
|
||||
"effectIds": [
|
||||
"YA5tbB6XBISWsf0p"
|
||||
"G9mMGxBSexwjWTYV"
|
||||
],
|
||||
"actionIds": []
|
||||
}
|
||||
|
|
@ -109,14 +109,21 @@
|
|||
"img": "icons/skills/melee/strike-sword-slashing-red.webp",
|
||||
"changes": [
|
||||
{
|
||||
"key": "system.bonuses.roll.primaryWeapon.attack",
|
||||
"key": "system.bonuses.roll.primaryWeapon.bonus",
|
||||
"mode": 2,
|
||||
"value": "1"
|
||||
}
|
||||
],
|
||||
"_id": "YA5tbB6XBISWsf0p",
|
||||
"_id": "G9mMGxBSexwjWTYV",
|
||||
"type": "base",
|
||||
"system": {},
|
||||
"system": {
|
||||
"rangeDependence": {
|
||||
"enabled": false,
|
||||
"type": "withinRange",
|
||||
"target": "hostile",
|
||||
"range": "melee"
|
||||
}
|
||||
},
|
||||
"disabled": false,
|
||||
"duration": {
|
||||
"startTime": null,
|
||||
|
|
@ -132,14 +139,14 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1753836322206,
|
||||
"modifiedTime": 1753836322206,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1754815251133,
|
||||
"modifiedTime": 1754815251133,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items.effects!I1nDGpulg29GpWOW.YA5tbB6XBISWsf0p"
|
||||
"_key": "!items.effects!I1nDGpulg29GpWOW.G9mMGxBSexwjWTYV"
|
||||
}
|
||||
],
|
||||
"sort": 0,
|
||||
|
|
@ -152,12 +159,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1753836302436,
|
||||
"modifiedTime": 1753836334493,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754815251135,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items!I1nDGpulg29GpWOW"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
{
|
||||
"value": "reliable",
|
||||
"effectIds": [
|
||||
"Gt0tHtJDQwdSActw"
|
||||
"cOYeI9TxHXpDwszu"
|
||||
],
|
||||
"actionIds": []
|
||||
}
|
||||
|
|
@ -109,14 +109,21 @@
|
|||
"img": "icons/skills/melee/strike-sword-slashing-red.webp",
|
||||
"changes": [
|
||||
{
|
||||
"key": "system.bonuses.roll.primaryWeapon.attack",
|
||||
"key": "system.bonuses.roll.primaryWeapon.bonus",
|
||||
"mode": 2,
|
||||
"value": "1"
|
||||
}
|
||||
],
|
||||
"_id": "Gt0tHtJDQwdSActw",
|
||||
"_id": "cOYeI9TxHXpDwszu",
|
||||
"type": "base",
|
||||
"system": {},
|
||||
"system": {
|
||||
"rangeDependence": {
|
||||
"enabled": false,
|
||||
"type": "withinRange",
|
||||
"target": "hostile",
|
||||
"range": "melee"
|
||||
}
|
||||
},
|
||||
"disabled": false,
|
||||
"duration": {
|
||||
"startTime": null,
|
||||
|
|
@ -132,14 +139,14 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1753829771677,
|
||||
"modifiedTime": 1753829771677,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1754814992368,
|
||||
"modifiedTime": 1754814992368,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items.effects!z6yEdFYQJ5IzgTX3.Gt0tHtJDQwdSActw"
|
||||
"_key": "!items.effects!z6yEdFYQJ5IzgTX3.cOYeI9TxHXpDwszu"
|
||||
}
|
||||
],
|
||||
"sort": 0,
|
||||
|
|
@ -152,12 +159,12 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"systemVersion": "1.0.0",
|
||||
"createdTime": 1753829740082,
|
||||
"modifiedTime": 1753829771680,
|
||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
||||
"modifiedTime": 1754814992370,
|
||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
||||
},
|
||||
"_key": "!items!z6yEdFYQJ5IzgTX3"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@
|
|||
"image": {},
|
||||
"text": {
|
||||
"format": 1,
|
||||
"content": "<blockquote><p>This product includes materials from the Daggerheart System Reference Document 1.0, © Critical Role, LLC. under the terms of the Darrington Press Community Gaming (DPCGL) License. More information can be found at <a href=\"https://www.daggerheart.com/\" title=\"Daggerheart Official Website\">https://www.daggerheart.com</a>. There are no previous modifications by others.</p><p></p></blockquote><h1>The Foundryborne Team</h1><p></p><p>The Foundryborne Team consists of:</p><p></p><ul><li><p><strong>@harryfuralle</strong></p></li><li><p><strong>@cptn_cosmo</strong></p></li><li><p><strong>@molilo</strong></p></li><li><p><strong>@joaquinp98</strong></p></li><li><p><strong>@dapoulp</strong></p></li><li><p>@ikraik</p></li><li><p>@irktheimp</p></li><li><p>@jacobwojo</p></li><li><p>@vyrth</p></li><li><p>@.ontaro</p></li><li><p>@saatsin</p></li><li><p>@david.xyz</p></li></ul><p></p><p>With Art from:</p><p></p><ul><li><p>UsernameIsInUse</p></li></ul><p></p><p>We would also like to thank the FoundryVTT team for their support in publishing this system.</p><p>And, of course, special thanks to the teams at Critical Role and Darrington Press for making such a wonderful game and updating the license to allow a FoundryVTT version of the system.</p><p></p><h1>The Foundryborne Community</h1><p></p><p>Without our amazing community this project would not have been possible.</p><p>You kept us going with both direct contributions and just endless support!</p><p>We thank you with all our hearts.</p><p><a href=\"https://foundryborne.online/\" title=\"Foundryborne official website\">Come join us!</a></p>"
|
||||
"content": "<blockquote><p>This product includes materials from the Daggerheart System Reference Document 1.0, © Critical Role, LLC. under the terms of the Darrington Press Community Gaming (DPCGL) License. More information can be found at <a href=\"https://www.daggerheart.com/\" title=\"Daggerheart Official Website\">https://www.daggerheart.com</a>. There are no previous modifications by others.</p><p></p></blockquote><h1>The Foundryborne Team</h1><p></p><p>The Foundryborne Team consists of:</p><p></p><ul><li><p>@harryfuralle</p></li><li><p>@cptn_cosmo</p></li><li><p>@molilo</p></li><li><p>@joaquinp98</p></li><li><p>@dapoulp</p></li><li><p>@ikraik</p></li><li><p>@irktheimp</p></li><li><p>@jacobwojo</p></li><li><p>@vyrth</p></li><li><p>@.ontaro</p></li><li><p>@saatsin</p></li><li><p>@david.xyz</p></li><li><p>@lazjen</p></li></ul><p></p><p>With Art from:</p><p></p><ul><li><p>@molilo (Foundryborne User Interface & Domain Card Placeholder Artwork)</p></li><li><p>@UsernameIsInUse (Foundryborne Logo, Website Design, Duality Roll Icon</p></li><li><p>@CyrensMaps (Combat Wheelchair Icons)</p></li></ul><p></p><p>And special thanks to our hard working community testers:<br /></p><ul><li><p>@lazjen</p></li></ul><p></p><p>We would also like to thank the FoundryVTT team for their support in publishing this system, as well as providing countless Icons to be used in many places.</p><p>And, of course, special thanks to the teams at Critical Role and Darrington Press for making such a wonderful game and updating the license to allow a FoundryVTT version of the system.</p><p></p><h1>The Foundryborne Community</h1><p></p><p>Without our amazing community this project would not have been possible.</p><p>You kept us going with both direct contributions and just endless support!</p><p>We thank you with all our hearts.</p><p><a href=\"https://foundryborne.online/\" title=\"Foundryborne official website\">Come join us!</a></p>"
|
||||
},
|
||||
"video": {
|
||||
"controls": true,
|
||||
|
|
@ -153,8 +153,8 @@
|
|||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"createdTime": 1754225939902,
|
||||
"modifiedTime": 1754226994508,
|
||||
"lastModifiedBy": "l5jB3XmcVXOTQpRZ"
|
||||
"modifiedTime": 1754847043391,
|
||||
"lastModifiedBy": "9GFCfEY8m5Co2mHo"
|
||||
},
|
||||
"_key": "!journal.pages!g7NhKvwltwafmMyR.dP6xSKEld4TSqHhK"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
}
|
||||
},
|
||||
"_id": "RSmscgGyuHJucF6C",
|
||||
"img": "icons/magic/life/heart-cross-blue.webp",
|
||||
"img": "icons/sundries/documents/document-letter-blue.webp",
|
||||
"changes": [
|
||||
{
|
||||
"key": "system.bonuses.rally",
|
||||
|
|
@ -53,10 +53,11 @@
|
|||
"compendiumSource": null,
|
||||
"duplicateSource": null,
|
||||
"exportSource": null,
|
||||
"coreVersion": "13.346",
|
||||
"coreVersion": "13.347",
|
||||
"systemId": "daggerheart",
|
||||
"systemVersion": "0.0.1",
|
||||
"lastModifiedBy": null
|
||||
"lastModifiedBy": "49DaecTcBSc5d0DA",
|
||||
"modifiedTime": 1754669077252
|
||||
},
|
||||
"_key": "!items.effects!eCoEWkWuZPMZ9C6a.RSmscgGyuHJucF6C"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,3 +27,5 @@
|
|||
@import './damage-reduction/sheets.less';
|
||||
|
||||
@import './multiclass-choice/sheet.less';
|
||||
|
||||
@import './reroll-dialog/sheet.less';
|
||||
|
|
|
|||
125
styles/less/dialog/reroll-dialog/sheet.less
Normal file
125
styles/less/dialog/reroll-dialog/sheet.less
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
.daggerheart.dialog.dh-style.views.reroll-dialog {
|
||||
.window-content {
|
||||
max-width: 648px;
|
||||
}
|
||||
|
||||
.reroll-outer-container {
|
||||
h2 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.dices-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.dice-outer-container {
|
||||
width: 300px;
|
||||
|
||||
legend {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
|
||||
i {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.dice-container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;
|
||||
|
||||
.result-container {
|
||||
position: relative;
|
||||
aspect-ratio: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 22px;
|
||||
opacity: 0.8;
|
||||
|
||||
&.selected {
|
||||
opacity: 1;
|
||||
border: 1px solid;
|
||||
border-radius: 6px;
|
||||
border-color: light-dark(@dark-blue, @golden);
|
||||
filter: drop-shadow(0 0 3px @golden);
|
||||
}
|
||||
|
||||
&:before {
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: -1;
|
||||
mask: var(--svg-die) no-repeat center;
|
||||
mask-size: contain;
|
||||
background: linear-gradient(139.01deg, #efe6d8 3.51%, #372e1f 96.49%);
|
||||
}
|
||||
|
||||
&.d4:before {
|
||||
--svg-die: url(../assets/icons/dice/default/d4.svg);
|
||||
}
|
||||
&.d6:before {
|
||||
--svg-die: url(../assets/icons/dice/default/d6.svg);
|
||||
}
|
||||
&.d8:before {
|
||||
--svg-die: url(../assets/icons/dice/default/d8.svg);
|
||||
}
|
||||
&.d10:before {
|
||||
--svg-die: url(../assets/icons/dice/default/d10.svg);
|
||||
}
|
||||
&.d12:before {
|
||||
--svg-die: url('../assets/icons/dice/default/d12.svg');
|
||||
}
|
||||
&.d20:before {
|
||||
--svg-die: url(../assets/icons/dice/default/d20.svg);
|
||||
}
|
||||
|
||||
.to-reroll-result {
|
||||
position: absolute;
|
||||
bottom: -7px;
|
||||
gap: 2px;
|
||||
border: 1px solid;
|
||||
border-radius: 6px;
|
||||
background-image: url(../assets/parchments/dh-parchment-dark.png);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 2px 6px;
|
||||
|
||||
input {
|
||||
margin: 0;
|
||||
height: 12px;
|
||||
line-height: 0px;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
line-height: 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
i {
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -32,6 +32,7 @@
|
|||
.message-header-metadata {
|
||||
flex: none;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.message-metadata {
|
||||
font-family: @font-body;
|
||||
|
|
@ -73,6 +74,13 @@
|
|||
|
||||
.message-content {
|
||||
padding-bottom: 8px;
|
||||
.flavor-text {
|
||||
font-size: var(--font-size-12);
|
||||
line-height: 20px;
|
||||
color: var(--color-dark-4);
|
||||
text-align: center;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@
|
|||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
line-height: 17px;
|
||||
|
||||
white-space: nowrap;
|
||||
color: light-dark(@dark, @beige);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -571,6 +571,16 @@
|
|||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.button-container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 10px;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
|
|
|
|||
|
|
@ -14,8 +14,14 @@
|
|||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.feature-list {
|
||||
.feature-list,
|
||||
.features-dragger {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
font-family: @font-body;
|
||||
}
|
||||
|
||||
.feature-list {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
|
||||
|
|
@ -45,5 +51,16 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.features-dragger {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
height: 40px;
|
||||
margin-top: 10px;
|
||||
border: 1px dashed light-dark(@dark-blue-50, @beige-50);
|
||||
border-radius: 3px;
|
||||
color: light-dark(@dark-blue-50, @beige-50);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue