mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-04-22 23:43:37 +02:00
Merged with v14-Dev
This commit is contained in:
commit
88be00567e
650 changed files with 6323 additions and 4508 deletions
|
|
@ -20,7 +20,6 @@ import {
|
||||||
} from './module/systemRegistration/_module.mjs';
|
} from './module/systemRegistration/_module.mjs';
|
||||||
import { placeables, DhTokenLayer } from './module/canvas/_module.mjs';
|
import { placeables, DhTokenLayer } from './module/canvas/_module.mjs';
|
||||||
import './node_modules/@yaireo/tagify/dist/tagify.css';
|
import './node_modules/@yaireo/tagify/dist/tagify.css';
|
||||||
import TemplateManager from './module/documents/templateManager.mjs';
|
|
||||||
import TokenManager from './module/documents/tokenManager.mjs';
|
import TokenManager from './module/documents/tokenManager.mjs';
|
||||||
|
|
||||||
CONFIG.DH = SYSTEM;
|
CONFIG.DH = SYSTEM;
|
||||||
|
|
@ -55,11 +54,13 @@ CONFIG.ChatMessage.documentClass = documents.DhChatMessage;
|
||||||
CONFIG.ChatMessage.template = 'systems/daggerheart/templates/ui/chat/chat-message.hbs';
|
CONFIG.ChatMessage.template = 'systems/daggerheart/templates/ui/chat/chat-message.hbs';
|
||||||
|
|
||||||
CONFIG.Canvas.rulerClass = placeables.DhRuler;
|
CONFIG.Canvas.rulerClass = placeables.DhRuler;
|
||||||
CONFIG.Canvas.layers.templates.layerClass = placeables.DhTemplateLayer;
|
CONFIG.Canvas.layers.regions.layerClass = placeables.DhRegionLayer;
|
||||||
CONFIG.Canvas.layers.tokens.layerClass = DhTokenLayer;
|
CONFIG.Canvas.layers.tokens.layerClass = DhTokenLayer;
|
||||||
|
|
||||||
CONFIG.MeasuredTemplate.objectClass = placeables.DhMeasuredTemplate;
|
CONFIG.MeasuredTemplate.objectClass = placeables.DhMeasuredTemplate;
|
||||||
|
|
||||||
|
CONFIG.Region.objectClass = placeables.DhRegion;
|
||||||
|
|
||||||
CONFIG.RollTable.documentClass = documents.DhRollTable;
|
CONFIG.RollTable.documentClass = documents.DhRollTable;
|
||||||
CONFIG.RollTable.resultTemplate = 'systems/daggerheart/templates/ui/chat/table-result.hbs';
|
CONFIG.RollTable.resultTemplate = 'systems/daggerheart/templates/ui/chat/table-result.hbs';
|
||||||
|
|
||||||
|
|
@ -83,7 +84,6 @@ CONFIG.ui.resources = applications.ui.DhFearTracker;
|
||||||
CONFIG.ui.countdowns = applications.ui.DhCountdowns;
|
CONFIG.ui.countdowns = applications.ui.DhCountdowns;
|
||||||
CONFIG.ux.ContextMenu = applications.ux.DHContextMenu;
|
CONFIG.ux.ContextMenu = applications.ux.DHContextMenu;
|
||||||
CONFIG.ux.TooltipManager = documents.DhTooltipManager;
|
CONFIG.ux.TooltipManager = documents.DhTooltipManager;
|
||||||
CONFIG.ux.TemplateManager = new TemplateManager();
|
|
||||||
CONFIG.ux.TokenManager = new TokenManager();
|
CONFIG.ux.TokenManager = new TokenManager();
|
||||||
CONFIG.debug.triggers = false;
|
CONFIG.debug.triggers = false;
|
||||||
|
|
||||||
|
|
|
||||||
33
lang/en.json
33
lang/en.json
|
|
@ -14,7 +14,9 @@
|
||||||
"beastform": "Beastform"
|
"beastform": "Beastform"
|
||||||
},
|
},
|
||||||
"ActiveEffect": {
|
"ActiveEffect": {
|
||||||
"beastform": "Beastform"
|
"base": "Standard",
|
||||||
|
"beastform": "Beastform",
|
||||||
|
"horde": "Horde"
|
||||||
},
|
},
|
||||||
"Actor": {
|
"Actor": {
|
||||||
"character": "Character",
|
"character": "Character",
|
||||||
|
|
@ -703,6 +705,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"CONFIG": {
|
"CONFIG": {
|
||||||
|
"ActiveEffectDuration": {
|
||||||
|
"temporary": "Temporary",
|
||||||
|
"act": "Next Spotlight",
|
||||||
|
"scene": "Next Scene",
|
||||||
|
"shortRest": "Next Rest",
|
||||||
|
"longRest": "Next Long Rest",
|
||||||
|
"session": "Next Session",
|
||||||
|
"custom": "Custom"
|
||||||
|
},
|
||||||
"AdversaryTrait": {
|
"AdversaryTrait": {
|
||||||
"relentless": {
|
"relentless": {
|
||||||
"name": "Relentless",
|
"name": "Relentless",
|
||||||
|
|
@ -1064,6 +1075,10 @@
|
||||||
"fear": "Fear",
|
"fear": "Fear",
|
||||||
"spotlight": "Spotlight"
|
"spotlight": "Spotlight"
|
||||||
},
|
},
|
||||||
|
"DaggerheartDiceAnimationEvents": {
|
||||||
|
"critical": { "name": "Critical" },
|
||||||
|
"higher": { "name": "Highest Roll" }
|
||||||
|
},
|
||||||
"DamageType": {
|
"DamageType": {
|
||||||
"physical": {
|
"physical": {
|
||||||
"name": "Physical",
|
"name": "Physical",
|
||||||
|
|
@ -1238,8 +1253,8 @@
|
||||||
"cone": "Cone",
|
"cone": "Cone",
|
||||||
"emanation": "Emanation",
|
"emanation": "Emanation",
|
||||||
"inFront": "In Front",
|
"inFront": "In Front",
|
||||||
"rect": "Rectangle",
|
"rectangle": "Rectangle",
|
||||||
"ray": "Ray"
|
"line": "Line"
|
||||||
},
|
},
|
||||||
"TokenSize": {
|
"TokenSize": {
|
||||||
"tiny": "Tiny",
|
"tiny": "Tiny",
|
||||||
|
|
@ -2324,6 +2339,7 @@
|
||||||
"plurial": "Players"
|
"plurial": "Players"
|
||||||
},
|
},
|
||||||
"portrait": "Portrait",
|
"portrait": "Portrait",
|
||||||
|
"preview": "Preview",
|
||||||
"proficiency": "Proficiency",
|
"proficiency": "Proficiency",
|
||||||
"quantity": "Quantity",
|
"quantity": "Quantity",
|
||||||
"range": "Range",
|
"range": "Range",
|
||||||
|
|
@ -2572,6 +2588,10 @@
|
||||||
"hint": "Automatically increase the GM's fear pool on a fear duality roll result."
|
"hint": "Automatically increase the GM's fear pool on a fear duality roll result."
|
||||||
},
|
},
|
||||||
"FIELDS": {
|
"FIELDS": {
|
||||||
|
"autoExpireActiveEffects": {
|
||||||
|
"label": "Auto Expire Active Effects",
|
||||||
|
"hint": "Active Effects with set durations will automatically be removed when their durations are up"
|
||||||
|
},
|
||||||
"damageReductionRulesDefault": {
|
"damageReductionRulesDefault": {
|
||||||
"label": "Damage Reduction Rules Default",
|
"label": "Damage Reduction Rules Default",
|
||||||
"hint": "Wether using armor and reductions has rules on by default"
|
"hint": "Wether using armor and reductions has rules on by default"
|
||||||
|
|
@ -2777,7 +2797,12 @@
|
||||||
"colorset": "Theme",
|
"colorset": "Theme",
|
||||||
"material": "Material",
|
"material": "Material",
|
||||||
"system": "Dice Preset",
|
"system": "Dice Preset",
|
||||||
"font": "Font"
|
"font": "Font",
|
||||||
|
"critical": "Duality Critical Animation",
|
||||||
|
"diceAppearance": "Dice Appearance",
|
||||||
|
"animations": "Animations",
|
||||||
|
"defaultAnimations": "Set Animations As Player Defaults",
|
||||||
|
"previewAnimation": "Preview Animation"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"variantRules": {
|
"variantRules": {
|
||||||
|
|
|
||||||
|
|
@ -554,7 +554,7 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
|
||||||
experiences: {
|
experiences: {
|
||||||
...this.setup.experiences,
|
...this.setup.experiences,
|
||||||
...Object.keys(this.character.system.experiences).reduce((acc, key) => {
|
...Object.keys(this.character.system.experiences).reduce((acc, key) => {
|
||||||
acc[`-=${key}`] = null;
|
acc[`${key}`] = _del;
|
||||||
return acc;
|
return acc;
|
||||||
}, {})
|
}, {})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,8 +77,8 @@ export default class CharacterResetDialog extends HandlebarsApplicationMixin(App
|
||||||
|
|
||||||
if (!this.data.optional.portrait.keep) {
|
if (!this.data.optional.portrait.keep) {
|
||||||
foundry.utils.setProperty(update, 'img', this.actor.schema.fields.img.initial(this.actor));
|
foundry.utils.setProperty(update, 'img', this.actor.schema.fields.img.initial(this.actor));
|
||||||
foundry.utils.setProperty(update, 'prototypeToken.==texture', {});
|
foundry.utils.setProperty(update, 'prototypeToken.texture', _replace({}));
|
||||||
foundry.utils.setProperty(update, 'prototypeToken.==ring', {});
|
foundry.utils.setProperty(update, 'prototypeToken.ring', _replace({}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.data.optional.biography.keep)
|
if (this.data.optional.biography.keep)
|
||||||
|
|
@ -89,7 +89,7 @@ export default class CharacterResetDialog extends HandlebarsApplicationMixin(App
|
||||||
const { system, ...rest } = update;
|
const { system, ...rest } = update;
|
||||||
await this.actor.update({
|
await this.actor.update({
|
||||||
...rest,
|
...rest,
|
||||||
'==system': system ?? {}
|
system: _replace(system ?? {})
|
||||||
});
|
});
|
||||||
|
|
||||||
const inventoryItemTypes = ['weapon', 'armor', 'consumable', 'loot'];
|
const inventoryItemTypes = ['weapon', 'armor', 'consumable', 'loot'];
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { refreshIsAllowed } from '../../helpers/utils.mjs';
|
import { expireActiveEffects, refreshIsAllowed } from '../../helpers/utils.mjs';
|
||||||
|
|
||||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
||||||
|
|
@ -264,6 +264,8 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
||||||
await feature.update({ 'system.resource.value': resetValue });
|
await feature.update({ 'system.resource.value': resetValue });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expireActiveEffects(this.actor, [this.shortRest ? 'shortRest' : 'longRest']);
|
||||||
|
|
||||||
this.close();
|
this.close();
|
||||||
} else {
|
} else {
|
||||||
this.render();
|
this.render();
|
||||||
|
|
|
||||||
|
|
@ -245,7 +245,8 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
if (error) return error;
|
if (error) return error;
|
||||||
|
|
||||||
await this.party.update({
|
await this.party.update({
|
||||||
'system.==tagTeam': new game.system.api.data.TagTeamData({
|
'system.tagTeam': _replace(
|
||||||
|
new game.system.api.data.TagTeamData({
|
||||||
...this.party.system.tagTeam.toObject(),
|
...this.party.system.tagTeam.toObject(),
|
||||||
initiator: this.initiator,
|
initiator: this.initiator,
|
||||||
members: this.partyMembers.reduce((acc, member) => {
|
members: this.partyMembers.reduce((acc, member) => {
|
||||||
|
|
@ -258,6 +259,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
return acc;
|
return acc;
|
||||||
}, {})
|
}, {})
|
||||||
})
|
})
|
||||||
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
const hookData = { openForAllPlayers: this.openForAllPlayers, partyId: this.party.id };
|
const hookData = { openForAllPlayers: this.openForAllPlayers, partyId: this.party.id };
|
||||||
|
|
@ -566,7 +568,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
return mainRoll;
|
return mainRoll;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async #onCancelRoll(options = { confirm: true }) {
|
static async #onCancelRoll(_event, _button, options = { confirm: true }) {
|
||||||
this.cancelRoll(options);
|
this.cancelRoll(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -584,9 +586,9 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
|
|
||||||
await this.updatePartyData(
|
await this.updatePartyData(
|
||||||
{
|
{
|
||||||
'system.==tagTeam': {
|
'system.tagTeam': {
|
||||||
initiator: null,
|
initiator: null,
|
||||||
members: {}
|
members: _replace({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ render: false }
|
{ render: false }
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ export default class DhCompanionLevelUp extends BaseLevelUp {
|
||||||
break;
|
break;
|
||||||
case 'summary':
|
case 'summary':
|
||||||
const levelKeys = Object.keys(this.levelup.levels);
|
const levelKeys = Object.keys(this.levelup.levels);
|
||||||
const actorDamageDice = this.actor.system.attack.damage.parts[0].value.dice;
|
const actorDamageDice = this.actor.system.attack.damage.parts.hitPoints.value.dice;
|
||||||
const actorRange = this.actor.system.attack.range;
|
const actorRange = this.actor.system.attack.range;
|
||||||
|
|
||||||
let achievementExperiences = [];
|
let achievementExperiences = [];
|
||||||
|
|
|
||||||
|
|
@ -477,7 +477,7 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
||||||
const secondaryData = Object.keys(
|
const secondaryData = Object.keys(
|
||||||
foundry.utils.getProperty(this.levelup, `${target.dataset.path}.secondaryData`)
|
foundry.utils.getProperty(this.levelup, `${target.dataset.path}.secondaryData`)
|
||||||
).reduce((acc, key) => {
|
).reduce((acc, key) => {
|
||||||
acc[`-=${key}`] = null;
|
acc[key] = _del;
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
await this.levelup.updateSource({
|
await this.levelup.updateSource({
|
||||||
|
|
@ -511,9 +511,9 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
||||||
const current = foundry.utils.getProperty(this.levelup, `${basePath}.${button.dataset.option}`);
|
const current = foundry.utils.getProperty(this.levelup, `${basePath}.${button.dataset.option}`);
|
||||||
if (Number(button.dataset.cost) > 1 || Object.keys(current).length === 1) {
|
if (Number(button.dataset.cost) > 1 || Object.keys(current).length === 1) {
|
||||||
// Simple handling that doesn't cover potential Custom LevelTiers.
|
// Simple handling that doesn't cover potential Custom LevelTiers.
|
||||||
update[`${basePath}.-=${button.dataset.option}`] = null;
|
update[`${basePath}.${button.dataset.option}`] = _del;
|
||||||
} else {
|
} else {
|
||||||
update[`${basePath}.${button.dataset.option}.-=${button.dataset.checkboxNr}`] = null;
|
update[`${basePath}.${button.dataset.option}.${button.dataset.checkboxNr}`] = _del;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (this.levelup.levels[this.levelup.currentLevel].nrSelections.available < Number(button.dataset.cost)) {
|
if (this.levelup.levels[this.levelup.currentLevel].nrSelections.available < Number(button.dataset.cost)) {
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,15 @@ export default class DhSceneConfigSettings extends foundry.applications.sheets.S
|
||||||
}
|
}
|
||||||
|
|
||||||
async _onDrop(event) {
|
async _onDrop(event) {
|
||||||
|
event.stopPropagation();
|
||||||
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
|
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
|
||||||
|
if (data.type === 'Level') {
|
||||||
|
const level = await foundry.documents.Level.fromDropData(data);
|
||||||
|
if (level?.parent === this.document) return this._onSortLevel(event, level);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const item = await foundry.utils.fromUuid(data.uuid);
|
const item = await foundry.utils.fromUuid(data.uuid);
|
||||||
if (item instanceof game.system.api.documents.DhpActor && item.type === 'environment') {
|
if (item instanceof game.system.api.documents.DhpActor && item.type === 'environment') {
|
||||||
let sceneUuid = data.uuid;
|
let sceneUuid = data.uuid;
|
||||||
|
|
@ -114,7 +122,7 @@ export default class DhSceneConfigSettings extends foundry.applications.sheets.S
|
||||||
|
|
||||||
for (const key of Object.keys(this.document._source.flags.daggerheart?.sceneEnvironments ?? {})) {
|
for (const key of Object.keys(this.document._source.flags.daggerheart?.sceneEnvironments ?? {})) {
|
||||||
if (!submitData.flags.daggerheart.sceneEnvironments[key]) {
|
if (!submitData.flags.daggerheart.sceneEnvironments[key]) {
|
||||||
submitData.flags.daggerheart.sceneEnvironments[`-=${key}`] = null;
|
submitData.flags.daggerheart.sceneEnvironments[key] = _del;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
|
||||||
static DEFAULT_OPTIONS = {
|
static DEFAULT_OPTIONS = {
|
||||||
tag: 'form',
|
tag: 'form',
|
||||||
id: 'daggerheart-appearance-settings',
|
id: 'daggerheart-appearance-settings',
|
||||||
classes: ['daggerheart', 'dialog', 'dh-style', 'setting'],
|
classes: ['daggerheart', 'dialog', 'dh-style', 'setting', 'appearance-settings'],
|
||||||
position: { width: '600', height: 'auto' },
|
position: { width: '600', height: 'auto' },
|
||||||
window: {
|
window: {
|
||||||
title: 'DAGGERHEART.SETTINGS.Menu.title',
|
title: 'DAGGERHEART.SETTINGS.Menu.title',
|
||||||
|
|
@ -70,6 +70,14 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_attachPartListeners(partId, htmlElement, options) {
|
||||||
|
super._attachPartListeners(partId, htmlElement, options);
|
||||||
|
|
||||||
|
htmlElement
|
||||||
|
.querySelector('.default-animations-input')
|
||||||
|
?.addEventListener('change', this.toggleSFXOverride.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
_configureRenderParts(options) {
|
_configureRenderParts(options) {
|
||||||
const parts = super._configureRenderParts(options);
|
const parts = super._configureRenderParts(options);
|
||||||
|
|
@ -83,15 +91,20 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
|
||||||
/**@inheritdoc */
|
/**@inheritdoc */
|
||||||
async _prepareContext(options) {
|
async _prepareContext(options) {
|
||||||
const context = await super._prepareContext(options);
|
const context = await super._prepareContext(options);
|
||||||
if (options.isFirstRender)
|
if (options.isFirstRender) {
|
||||||
this.setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance);
|
this.setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance);
|
||||||
|
this.globalOverrides = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.GlobalOverrides);
|
||||||
|
}
|
||||||
|
|
||||||
context.setting = this.setting;
|
context.setting = this.setting;
|
||||||
|
context.globalOverrides = this.globalOverrides;
|
||||||
context.fields = this.setting.schema.fields;
|
context.fields = this.setting.schema.fields;
|
||||||
|
|
||||||
context.tabs = this._prepareTabs('general');
|
context.tabs = this._prepareTabs('general');
|
||||||
context.dsnTabs = this._prepareTabs('diceSoNice');
|
context.dsnTabs = this._prepareTabs('diceSoNice');
|
||||||
|
|
||||||
|
context.isGM = game.user.isGM;
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -120,6 +133,9 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
async prepareDiceSoNiceContext(context) {
|
async prepareDiceSoNiceContext(context) {
|
||||||
|
context.animationEvents = CONFIG.DH.GENERAL.daggerheartDiceAnimationEvents;
|
||||||
|
context.previewAnimation = this.previewAnimation;
|
||||||
|
|
||||||
context.diceSoNiceTextures = Object.entries(game.dice3d.exports.TEXTURELIST).reduce(
|
context.diceSoNiceTextures = Object.entries(game.dice3d.exports.TEXTURELIST).reduce(
|
||||||
(acc, [k, v]) => ({
|
(acc, [k, v]) => ({
|
||||||
...acc,
|
...acc,
|
||||||
|
|
@ -146,6 +162,13 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
|
||||||
);
|
);
|
||||||
context.diceSoNiceFonts = game.dice3d.exports.Utils.prepareFontList();
|
context.diceSoNiceFonts = game.dice3d.exports.Utils.prepareFontList();
|
||||||
|
|
||||||
|
const getAnimationsOptions = key => {
|
||||||
|
const fields = context.fields.diceSoNice.fields[key].fields.sfx.fields;
|
||||||
|
return {
|
||||||
|
higher: fields.higher.fields.class.choices
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
foundry.utils.mergeObject(
|
foundry.utils.mergeObject(
|
||||||
context.dsnTabs,
|
context.dsnTabs,
|
||||||
['hope', 'fear', 'advantage', 'disadvantage'].reduce(
|
['hope', 'fear', 'advantage', 'disadvantage'].reduce(
|
||||||
|
|
@ -153,7 +176,8 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
|
||||||
...acc,
|
...acc,
|
||||||
[key]: {
|
[key]: {
|
||||||
values: this.setting.diceSoNice[key],
|
values: this.setting.diceSoNice[key],
|
||||||
fields: this.setting.schema.getField(`diceSoNice.${key}`).fields
|
fields: this.setting.schema.getField(`diceSoNice.${key}`).fields,
|
||||||
|
animations: ['hope', 'fear'].includes(key) ? getAnimationsOptions(key) : {}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
{}
|
{}
|
||||||
|
|
@ -169,13 +193,20 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
|
||||||
* @param {foundry.applications.ux.FormDataExtended} formData
|
* @param {foundry.applications.ux.FormDataExtended} formData
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
static async #onSubmit(event, form, formData) {
|
static async #onSubmit(_event, _form, formData) {
|
||||||
const data = this.setting.schema.clean(foundry.utils.expandObject(formData.object));
|
const data = this.setting.schema.clean(foundry.utils.expandObject(formData.object));
|
||||||
|
|
||||||
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance, data);
|
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
async toggleSFXOverride(event) {
|
||||||
|
await this.globalOverrides.diceSoNiceSFXUpdate(this.setting, event.target.checked);
|
||||||
|
this.globalOverrides = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.GlobalOverrides);
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Submit the configuration form.
|
* Submit the configuration form.
|
||||||
* @this {DHAppearanceSettings}
|
* @this {DHAppearanceSettings}
|
||||||
|
|
@ -183,13 +214,25 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
|
||||||
*/
|
*/
|
||||||
static async #onPreview(_, target) {
|
static async #onPreview(_, target) {
|
||||||
const formData = new foundry.applications.ux.FormDataExtended(target.closest('form'));
|
const formData = new foundry.applications.ux.FormDataExtended(target.closest('form'));
|
||||||
const { diceSoNice } = foundry.utils.expandObject(formData.object);
|
const { diceSoNice, ...rest } = foundry.utils.expandObject(formData.object);
|
||||||
const { key } = target.dataset;
|
const { key } = target.dataset;
|
||||||
const faces = ['advantage', 'disadvantage'].includes(key) ? 'd6' : 'd12';
|
const faces = ['advantage', 'disadvantage'].includes(key) ? 'd6' : 'd12';
|
||||||
const preset = await getDiceSoNicePreset(diceSoNice[key], faces);
|
const preset = await getDiceSoNicePreset(diceSoNice[key], faces);
|
||||||
const diceSoNiceRoll = await new foundry.dice.Roll(`1${faces}`).evaluate();
|
const diceSoNiceRoll = await new foundry.dice.Roll(`1${faces}`).evaluate();
|
||||||
diceSoNiceRoll.dice[0].options.appearance = preset.appearance;
|
diceSoNiceRoll.dice[0].options.appearance = preset.appearance;
|
||||||
diceSoNiceRoll.dice[0].options.modelFile = preset.modelFile;
|
diceSoNiceRoll.dice[0].options.modelFile = preset.modelFile;
|
||||||
|
|
||||||
|
const previewAnimation = rest[`${key}PreviewAnimation`];
|
||||||
|
const events = CONFIG.DH.GENERAL.daggerheartDiceAnimationEvents;
|
||||||
|
if (previewAnimation) {
|
||||||
|
if (previewAnimation === events.critical.id && diceSoNice.sfx.critical.class) {
|
||||||
|
diceSoNiceRoll.dice[0].options.sfx = { specialEffect: diceSoNice.sfx.critical.class };
|
||||||
|
}
|
||||||
|
if (previewAnimation === events.higher.id && diceSoNice[key].sfx.higher) {
|
||||||
|
diceSoNiceRoll.dice[0].options.sfx = { specialEffect: diceSoNice[key].sfx.higher.class };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await game.dice3d.showForRoll(diceSoNiceRoll, game.user, false);
|
await game.dice3d.showForRoll(diceSoNiceRoll, game.user, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -298,7 +298,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
const isDowntime = ['shortRest', 'longRest'].includes(type);
|
const isDowntime = ['shortRest', 'longRest'].includes(type);
|
||||||
const path = isDowntime ? `restMoves.${type}.moves` : `itemFeatures.${type}`;
|
const path = isDowntime ? `restMoves.${type}.moves` : `itemFeatures.${type}`;
|
||||||
await this.settings.updateSource({
|
await this.settings.updateSource({
|
||||||
[`${path}.-=${id}`]: null
|
[`${path}.${id}`]: _del
|
||||||
});
|
});
|
||||||
|
|
||||||
game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
|
game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
|
||||||
|
|
@ -322,7 +322,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
const fields = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).schema.fields;
|
const fields = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).schema.fields;
|
||||||
|
|
||||||
const removeUpdate = Object.keys(this.settings.restMoves[target.dataset.type].moves).reduce((acc, key) => {
|
const removeUpdate = Object.keys(this.settings.restMoves[target.dataset.type].moves).reduce((acc, key) => {
|
||||||
acc[`-=${key}`] = null;
|
acc[key] = _del;
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
@ -382,7 +382,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
[`itemFeatures.${target.dataset.type}`]: Object.keys(
|
[`itemFeatures.${target.dataset.type}`]: Object.keys(
|
||||||
this.settings.itemFeatures[target.dataset.type]
|
this.settings.itemFeatures[target.dataset.type]
|
||||||
).reduce((acc, key) => {
|
).reduce((acc, key) => {
|
||||||
acc[`-=${key}`] = null;
|
acc[key] = _del;
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, {})
|
}, {})
|
||||||
|
|
@ -455,12 +455,12 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
if (!confirmed) return;
|
if (!confirmed) return;
|
||||||
|
|
||||||
await this.settings.updateSource({
|
await this.settings.updateSource({
|
||||||
[`domains.-=${this.selected.domain}`]: null
|
[`domains.${this.selected.domain}`]: _del
|
||||||
});
|
});
|
||||||
|
|
||||||
const currentSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew);
|
const currentSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew);
|
||||||
if (currentSettings.domains[this.selected.domain]) {
|
if (currentSettings.domains[this.selected.domain]) {
|
||||||
await currentSettings.updateSource({ [`domains.-=${this.selected.domain}`]: null });
|
await currentSettings.updateSource({ [`domains.${this.selected.domain}`]: _del });
|
||||||
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, currentSettings);
|
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, currentSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -507,7 +507,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
|
|
||||||
static async deleteAdversaryType(_, target) {
|
static async deleteAdversaryType(_, target) {
|
||||||
const { key } = target.dataset;
|
const { key } = target.dataset;
|
||||||
await this.settings.updateSource({ [`adversaryTypes.-=${key}`]: null });
|
await this.settings.updateSource({ [`adversaryTypes.${key}`]: _del });
|
||||||
|
|
||||||
this.selected.adversaryType = this.selected.adversaryType === key ? null : this.selected.adversaryType;
|
this.selected.adversaryType = this.selected.adversaryType === key ? null : this.selected.adversaryType;
|
||||||
this.render();
|
this.render();
|
||||||
|
|
@ -563,7 +563,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
|
|
||||||
const { actorType, resourceKey } = target.dataset;
|
const { actorType, resourceKey } = target.dataset;
|
||||||
await this.settings.updateSource({
|
await this.settings.updateSource({
|
||||||
[`resources.${actorType}.resources.-=${resourceKey}`]: null
|
[`resources.${actorType}.resources.${resourceKey}`]: _del
|
||||||
});
|
});
|
||||||
|
|
||||||
game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
|
game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { getUnusedDamageTypes } from '../../helpers/utils.mjs';
|
||||||
import DaggerheartSheet from '../sheets/daggerheart-sheet.mjs';
|
import DaggerheartSheet from '../sheets/daggerheart-sheet.mjs';
|
||||||
|
|
||||||
const { ApplicationV2 } = foundry.applications.api;
|
const { ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
@ -104,7 +105,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static CLEAN_ARRAYS = ['damage.parts', 'cost', 'effects', 'summon'];
|
static CLEAN_ARRAYS = ['cost', 'effects', 'summon'];
|
||||||
|
|
||||||
_getTabs(tabs) {
|
_getTabs(tabs) {
|
||||||
for (const v of Object.values(tabs)) {
|
for (const v of Object.values(tabs)) {
|
||||||
|
|
@ -172,6 +173,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
revealed: this.openTrigger === index
|
revealed: this.openTrigger === index
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
context.allDamageTypesUsed = !getUnusedDamageTypes(this.action.damage.parts).length;
|
||||||
|
|
||||||
const settingsTiers = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers;
|
const settingsTiers = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers;
|
||||||
context.tierOptions = [
|
context.tierOptions = [
|
||||||
|
|
@ -291,18 +293,61 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
|
|
||||||
static addDamage(_event) {
|
static addDamage(_event) {
|
||||||
if (!this.action.damage.parts) return;
|
if (!this.action.damage.parts) return;
|
||||||
const data = this.action.toObject(),
|
|
||||||
part = {};
|
const choices = getUnusedDamageTypes(this.action.damage.parts);
|
||||||
|
const content = new foundry.data.fields.StringField({
|
||||||
|
label: game.i18n.localize('Damage Type'),
|
||||||
|
choices,
|
||||||
|
required: true
|
||||||
|
}).toFormGroup(
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
name: 'type',
|
||||||
|
localize: true,
|
||||||
|
nameAttr: 'value',
|
||||||
|
labelAttr: 'label'
|
||||||
|
}
|
||||||
|
).outerHTML;
|
||||||
|
|
||||||
|
const callback = (_, button) => {
|
||||||
|
const data = this.action.toObject();
|
||||||
|
const type = choices[button.form.elements.type.value].value;
|
||||||
|
const part = { applyTo: type };
|
||||||
if (this.action.actor?.isNPC) part.value = { multiplier: 'flat' };
|
if (this.action.actor?.isNPC) part.value = { multiplier: 'flat' };
|
||||||
data.damage.parts.push(part);
|
data.damage.parts[type] = part;
|
||||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
|
};
|
||||||
|
|
||||||
|
const typeDialog = new foundry.applications.api.DialogV2({
|
||||||
|
buttons: [
|
||||||
|
foundry.utils.mergeObject(
|
||||||
|
{
|
||||||
|
action: 'ok',
|
||||||
|
label: 'Confirm',
|
||||||
|
icon: 'fas fa-check',
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
{ callback: callback }
|
||||||
|
)
|
||||||
|
],
|
||||||
|
content: content,
|
||||||
|
rejectClose: false,
|
||||||
|
modal: false,
|
||||||
|
window: {
|
||||||
|
title: game.i18n.localize('Add Damage')
|
||||||
|
},
|
||||||
|
position: { width: 300 }
|
||||||
|
});
|
||||||
|
|
||||||
|
typeDialog.render(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static removeDamage(_event, button) {
|
static removeDamage(_event, button) {
|
||||||
if (!this.action.damage.parts) return;
|
if (!this.action.damage.parts) return;
|
||||||
const data = this.action.toObject(),
|
const data = this.action.toObject();
|
||||||
index = button.dataset.index;
|
const key = button.dataset.key;
|
||||||
data.damage.parts.splice(index, 1);
|
delete data.damage.parts[key];
|
||||||
|
data.damage.parts[`${key}`] = _del;
|
||||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
||||||
settings: { template: 'systems/daggerheart/templates/sheets/activeEffect/settings.hbs' },
|
settings: { template: 'systems/daggerheart/templates/sheets/activeEffect/settings.hbs' },
|
||||||
changes: {
|
changes: {
|
||||||
template: 'systems/daggerheart/templates/sheets/activeEffect/changes.hbs',
|
template: 'systems/daggerheart/templates/sheets/activeEffect/changes.hbs',
|
||||||
|
templates: ['systems/daggerheart/templates/sheets/activeEffect/change.hbs'],
|
||||||
scrollable: ['ol[data-changes]']
|
scrollable: ['ol[data-changes]']
|
||||||
},
|
},
|
||||||
footer: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-form-footer.hbs' }
|
footer: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-form-footer.hbs' }
|
||||||
|
|
@ -173,8 +174,77 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'settings':
|
||||||
|
const groups = {
|
||||||
|
time: _loc('EFFECT.DURATION.UNITS.GROUPS.time'),
|
||||||
|
combat: _loc('EFFECT.DURATION.UNITS.GROUPS.combat')
|
||||||
|
};
|
||||||
|
partContext.durationUnits = CONST.ACTIVE_EFFECT_DURATION_UNITS.map(value => ({
|
||||||
|
value,
|
||||||
|
label: _loc(`EFFECT.DURATION.UNITS.${value}`),
|
||||||
|
group: CONST.ACTIVE_EFFECT_TIME_DURATION_UNITS.includes(value) ? groups.time : groups.combat
|
||||||
|
}));
|
||||||
|
break;
|
||||||
|
case 'changes':
|
||||||
|
const fields = this.document.system.schema.fields.changes.element.fields;
|
||||||
|
partContext.changes = await Promise.all(
|
||||||
|
foundry.utils
|
||||||
|
.deepClone(context.source.changes)
|
||||||
|
.map((c, i) => this._prepareChangeContext(c, i, fields))
|
||||||
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return partContext;
|
return partContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_prepareChangeContext(change, index, fields) {
|
||||||
|
if (typeof change.value !== 'string') change.value = JSON.stringify(change.value);
|
||||||
|
const defaultPriority = game.system.api.documents.DhActiveEffect.CHANGE_TYPES[change.type]?.defaultPriority;
|
||||||
|
Object.assign(
|
||||||
|
change,
|
||||||
|
['key', 'type', 'value', 'priority'].reduce((paths, fieldName) => {
|
||||||
|
paths[`${fieldName}Path`] = `system.changes.${index}.${fieldName}`;
|
||||||
|
return paths;
|
||||||
|
}, {})
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
game.system.api.documents.DhActiveEffect.CHANGE_TYPES[change.type].render?.(
|
||||||
|
change,
|
||||||
|
index,
|
||||||
|
defaultPriority
|
||||||
|
) ??
|
||||||
|
foundry.applications.handlebars.renderTemplate(
|
||||||
|
'systems/daggerheart/templates/sheets/activeEffect/change.hbs',
|
||||||
|
{
|
||||||
|
change,
|
||||||
|
index,
|
||||||
|
defaultPriority,
|
||||||
|
fields
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
_onChangeForm(_formConfig, event) {
|
||||||
|
if (foundry.utils.isElementInstanceOf(event.target, 'select') && event.target.name === 'system.duration.type') {
|
||||||
|
const durationSection = this.element.querySelector('.custom-duration-section');
|
||||||
|
if (event.target.value === 'custom') durationSection.classList.add('visible');
|
||||||
|
else durationSection.classList.remove('visible');
|
||||||
|
|
||||||
|
const durationDescription = this.element.querySelector('.duration-description');
|
||||||
|
if (event.target.value === 'temporary') durationDescription.classList.add('visible');
|
||||||
|
else durationDescription.classList.remove('visible');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
_processFormData(event, form, formData) {
|
||||||
|
const submitData = super._processFormData(event, form, formData);
|
||||||
|
if (submitData.start && !submitData.start.time) submitData.start.time = '0';
|
||||||
|
else if (!submitData) submitData.start = null;
|
||||||
|
|
||||||
|
return submitData;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ export default class DHAdversarySettings extends DHBaseActorSettings {
|
||||||
});
|
});
|
||||||
if (!confirmed) return;
|
if (!confirmed) return;
|
||||||
|
|
||||||
await this.actor.update({ [`system.experiences.-=${target.dataset.experience}`]: null });
|
await this.actor.update({ [`system.experiences.${target.dataset.experience}`]: _del });
|
||||||
}
|
}
|
||||||
|
|
||||||
async _onDragStart(event) {
|
async _onDragStart(event) {
|
||||||
|
|
|
||||||
|
|
@ -101,8 +101,8 @@ export default class DHCharacterSettings extends DHBaseActorSettings {
|
||||||
|
|
||||||
if (relinkAchievementData.length > 0) {
|
if (relinkAchievementData.length > 0) {
|
||||||
relinkAchievementData.forEach(data => {
|
relinkAchievementData.forEach(data => {
|
||||||
updates[`system.levelData.levelups.${data.levelKey}.achievements.experiences.-=${data.experience}`] =
|
updates[`system.levelData.levelups.${data.levelKey}.achievements.experiences.${data.experience}`] =
|
||||||
null;
|
_del;
|
||||||
});
|
});
|
||||||
} else if (relinkSelectionData.length > 0) {
|
} else if (relinkSelectionData.length > 0) {
|
||||||
relinkSelectionData.forEach(data => {
|
relinkSelectionData.forEach(data => {
|
||||||
|
|
@ -137,7 +137,7 @@ export default class DHCharacterSettings extends DHBaseActorSettings {
|
||||||
|
|
||||||
await this.actor.update({
|
await this.actor.update({
|
||||||
...updates,
|
...updates,
|
||||||
[`system.experiences.-=${target.dataset.experience}`]: null
|
[`system.experiences.${target.dataset.experience}`]: _del
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,6 @@ export default class DHCompanionSettings extends DHBaseActorSettings {
|
||||||
});
|
});
|
||||||
if (!confirmed) return;
|
if (!confirmed) return;
|
||||||
|
|
||||||
await this.actor.update({ [`system.experiences.-=${target.dataset.experience}`]: null });
|
await this.actor.update({ [`system.experiences.${target.dataset.experience}`]: _del });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,9 +68,9 @@ export default class DHEnvironmentSettings extends DHBaseActorSettings {
|
||||||
*/
|
*/
|
||||||
static async #addCategory() {
|
static async #addCategory() {
|
||||||
await this.actor.update({
|
await this.actor.update({
|
||||||
[`system.potentialAdversaries.${foundry.utils.randomID()}.label`]: game.i18n.localize(
|
[`system.potentialAdversaries.${foundry.utils.randomID()}`]: {
|
||||||
'DAGGERHEART.ACTORS.Environment.newAdversary'
|
label: game.i18n.localize('DAGGERHEART.ACTORS.Environment.newAdversary')
|
||||||
)
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,7 +79,7 @@ export default class DHEnvironmentSettings extends DHBaseActorSettings {
|
||||||
* @type {ApplicationClickAction}
|
* @type {ApplicationClickAction}
|
||||||
*/
|
*/
|
||||||
static async #removeCategory(_, target) {
|
static async #removeCategory(_, target) {
|
||||||
await this.actor.update({ [`system.potentialAdversaries.-=${target.dataset.categoryId}`]: null });
|
await this.actor.update({ [`system.potentialAdversaries.${target.dataset.categoryId}`]: _del });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -138,4 +138,8 @@ export default class DHEnvironmentSettings extends DHBaseActorSettings {
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _onDropItem(event, item) {
|
||||||
|
console.log(item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -205,7 +205,7 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await this.updateMove({ [`${this.actionsPath}.-=${target.dataset.id}`]: null });
|
await this.updateMove({ [`${this.actionsPath}.${target.dataset.id}`]: _del });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.render();
|
this.render();
|
||||||
|
|
|
||||||
|
|
@ -67,9 +67,9 @@ export default function DHTokenConfigMixin(Base) {
|
||||||
changes.height = tokenSize;
|
changes.height = tokenSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
const deletions = { '-=actorId': null, '-=actorLink': null };
|
const deletions = { actorId: _del, actorLink: _del };
|
||||||
const mergeOptions = { inplace: false, performDeletions: true };
|
const mergeOptions = { inplace: false, performDeletions: true };
|
||||||
this._preview.updateSource(mergeObject(changes, deletions, mergeOptions));
|
this._preview.updateSource(foundry.utils.mergeObject(changes, deletions, mergeOptions));
|
||||||
|
|
||||||
if (this._preview?.object?.destroyed === false) {
|
if (this._preview?.object?.destroyed === false) {
|
||||||
this._preview.object.initializeSources();
|
this._preview.object.initializeSources();
|
||||||
|
|
|
||||||
|
|
@ -72,19 +72,16 @@ const typeSettingsMap = {
|
||||||
*/
|
*/
|
||||||
export default function DHApplicationMixin(Base) {
|
export default function DHApplicationMixin(Base) {
|
||||||
class DHSheetV2 extends HandlebarsApplicationMixin(Base) {
|
class DHSheetV2 extends HandlebarsApplicationMixin(Base) {
|
||||||
|
#nonHeaderAttribution = ['environment', 'ancestry', 'community', 'domainCard'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {DHSheetV2Configuration} [options={}]
|
* @param {DHSheetV2Configuration} [options={}]
|
||||||
*/
|
*/
|
||||||
constructor(options = {}) {
|
constructor(options = {}) {
|
||||||
super(options);
|
super(options);
|
||||||
/**
|
|
||||||
* @type {foundry.applications.ux.DragDrop[]}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
this._dragDrop = this._createDragDropHandlers();
|
|
||||||
}
|
|
||||||
|
|
||||||
#nonHeaderAttribution = ['environment', 'ancestry', 'community', 'domainCard'];
|
this._setupDragDrop();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default options for the sheet.
|
* The default options for the sheet.
|
||||||
|
|
@ -177,7 +174,9 @@ export default function DHApplicationMixin(Base) {
|
||||||
/**@inheritdoc */
|
/**@inheritdoc */
|
||||||
_attachPartListeners(partId, htmlElement, options) {
|
_attachPartListeners(partId, htmlElement, options) {
|
||||||
super._attachPartListeners(partId, htmlElement, options);
|
super._attachPartListeners(partId, htmlElement, options);
|
||||||
this._dragDrop.forEach(d => d.bind(htmlElement));
|
|
||||||
|
/* Core dragDrop from ActorDocument is always only 1. Possible we could refactor our own */
|
||||||
|
if (Array.isArray(this._dragDrop)) this._dragDrop.forEach(d => d.bind(htmlElement));
|
||||||
|
|
||||||
// Handle delta inputs
|
// Handle delta inputs
|
||||||
for (const deltaInput of htmlElement.querySelectorAll('input[data-allow-delta]')) {
|
for (const deltaInput of htmlElement.querySelectorAll('input[data-allow-delta]')) {
|
||||||
|
|
@ -355,8 +354,12 @@ export default function DHApplicationMixin(Base) {
|
||||||
* @returns {foundry.applications.ux.DragDrop[]}
|
* @returns {foundry.applications.ux.DragDrop[]}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_createDragDropHandlers() {
|
_setupDragDrop() {
|
||||||
return this.options.dragDrop.map(d => {
|
if (this._dragDrop) {
|
||||||
|
this._dragDrop.callbacks.dragStart = this._onDragStart;
|
||||||
|
this._dragDrop.callback.drop = this._onDrop;
|
||||||
|
} else {
|
||||||
|
this._dragDrop = this.options.dragDrop.map(d => {
|
||||||
d.callbacks = {
|
d.callbacks = {
|
||||||
dragstart: this._onDragStart.bind(this),
|
dragstart: this._onDragStart.bind(this),
|
||||||
drop: this._onDrop.bind(this)
|
drop: this._onDrop.bind(this)
|
||||||
|
|
@ -364,6 +367,7 @@ export default function DHApplicationMixin(Base) {
|
||||||
return new foundry.applications.ux.DragDrop.implementation(d);
|
return new foundry.applications.ux.DragDrop.implementation(d);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle dragStart event.
|
* Handle dragStart event.
|
||||||
|
|
@ -499,7 +503,10 @@ export default function DHApplicationMixin(Base) {
|
||||||
icon: 'fa-solid fa-explosion',
|
icon: 'fa-solid fa-explosion',
|
||||||
condition: target => {
|
condition: target => {
|
||||||
const doc = getDocFromElementSync(target);
|
const doc = getDocFromElementSync(target);
|
||||||
return doc?.system?.attack?.damage.parts.length || doc?.damage?.parts.length;
|
return (
|
||||||
|
!foundry.utils.isEmpty(doc?.system?.attack?.damage.parts) ||
|
||||||
|
!foundry.utils.isEmpty(doc?.damage?.parts)
|
||||||
|
);
|
||||||
},
|
},
|
||||||
callback: async (target, event) => {
|
callback: async (target, event) => {
|
||||||
const doc = await getDocFromElement(target),
|
const doc = await getDocFromElement(target),
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ export default class BeastformSheet extends DHBaseItemSheet {
|
||||||
|
|
||||||
async advantageOnRemove(event) {
|
async advantageOnRemove(event) {
|
||||||
await this.document.update({
|
await this.document.update({
|
||||||
[`system.advantageOn.-=${event.detail.data.value}`]: null
|
[`system.advantageOn.${event.detail.data.value}`]: _del
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -108,14 +108,15 @@ export default class DhRollTableSheet extends foundry.applications.sheets.RollTa
|
||||||
getSystemFlagUpdate() {
|
getSystemFlagUpdate() {
|
||||||
const deleteUpdate = Object.keys(this.document._source.flags.daggerheart?.altFormula ?? {}).reduce(
|
const deleteUpdate = Object.keys(this.document._source.flags.daggerheart?.altFormula ?? {}).reduce(
|
||||||
(acc, formulaKey) => {
|
(acc, formulaKey) => {
|
||||||
if (!this.daggerheartFlag.altFormula[formulaKey]) acc.altFormula[`-=${formulaKey}`] = null;
|
if (!this.daggerheartFlag.altFormula[formulaKey]) acc.altFormula[formulaKey] = _del;
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
},
|
},
|
||||||
{ altFormula: {} }
|
{ altFormula: {} }
|
||||||
);
|
);
|
||||||
|
|
||||||
return { ['flags.daggerheart']: foundry.utils.mergeObject(this.daggerheartFlag.toObject(), deleteUpdate) };
|
const flagData = this.daggerheartFlag.toObject();
|
||||||
|
return { ...flagData, altFormula: { ...flagData.altFormula, ...deleteUpdate.altFormula } };
|
||||||
}
|
}
|
||||||
|
|
||||||
static async #addFormula() {
|
static async #addFormula() {
|
||||||
|
|
@ -127,7 +128,7 @@ export default class DhRollTableSheet extends foundry.applications.sheets.RollTa
|
||||||
|
|
||||||
static async #removeFormula(_event, target) {
|
static async #removeFormula(_event, target) {
|
||||||
await this.daggerheartFlag.updateSource({
|
await this.daggerheartFlag.updateSource({
|
||||||
[`altFormula.-=${target.dataset.key}`]: null
|
[`altFormula.${target.dataset.key}`]: _del
|
||||||
});
|
});
|
||||||
this.render({ internalRefresh: true });
|
this.render({ internalRefresh: true });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,52 +1,19 @@
|
||||||
export default class DhSidebar extends foundry.applications.sidebar.Sidebar {
|
export default class DhSidebar extends foundry.applications.sidebar.Sidebar {
|
||||||
/** @override */
|
static buildTabs() {
|
||||||
static TABS = {
|
const { settings, ...tabs } = super.TABS;
|
||||||
chat: {
|
return {
|
||||||
documentName: 'ChatMessage'
|
...tabs,
|
||||||
},
|
|
||||||
combat: {
|
|
||||||
documentName: 'Combat'
|
|
||||||
},
|
|
||||||
scenes: {
|
|
||||||
documentName: 'Scene',
|
|
||||||
gmOnly: true
|
|
||||||
},
|
|
||||||
actors: {
|
|
||||||
documentName: 'Actor'
|
|
||||||
},
|
|
||||||
items: {
|
|
||||||
documentName: 'Item'
|
|
||||||
},
|
|
||||||
journal: {
|
|
||||||
documentName: 'JournalEntry',
|
|
||||||
tooltip: 'SIDEBAR.TabJournal'
|
|
||||||
},
|
|
||||||
tables: {
|
|
||||||
documentName: 'RollTable'
|
|
||||||
},
|
|
||||||
cards: {
|
|
||||||
documentName: 'Cards'
|
|
||||||
},
|
|
||||||
macros: {
|
|
||||||
documentName: 'Macro'
|
|
||||||
},
|
|
||||||
playlists: {
|
|
||||||
documentName: 'Playlist'
|
|
||||||
},
|
|
||||||
compendium: {
|
|
||||||
tooltip: 'SIDEBAR.TabCompendium',
|
|
||||||
icon: 'fa-solid fa-book-atlas'
|
|
||||||
},
|
|
||||||
daggerheartMenu: {
|
daggerheartMenu: {
|
||||||
tooltip: 'DAGGERHEART.UI.Sidebar.daggerheartMenu.title',
|
tooltip: 'DAGGERHEART.UI.Sidebar.daggerheartMenu.title',
|
||||||
img: 'systems/daggerheart/assets/logos/FoundryBorneLogoWhite.svg',
|
img: 'systems/daggerheart/assets/logos/FoundryBorneLogoWhite.svg',
|
||||||
gmOnly: true
|
gmOnly: true
|
||||||
},
|
},
|
||||||
settings: {
|
settings
|
||||||
tooltip: 'SIDEBAR.TabSettings',
|
|
||||||
icon: 'fa-solid fa-gears'
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static TABS = DhSidebar.buildTabs();
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
static PARTS = {
|
static PARTS = {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { AdversaryBPPerEncounter } from '../../config/encounterConfig.mjs';
|
import { AdversaryBPPerEncounter } from '../../config/encounterConfig.mjs';
|
||||||
|
import { expireActiveEffects } from '../../helpers/utils.mjs';
|
||||||
|
|
||||||
export default class DhCombatTracker extends foundry.applications.sidebar.tabs.CombatTracker {
|
export default class DhCombatTracker extends foundry.applications.sidebar.tabs.CombatTracker {
|
||||||
static DEFAULT_OPTIONS = {
|
static DEFAULT_OPTIONS = {
|
||||||
|
|
@ -177,6 +178,8 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
|
||||||
if (autoPoints) {
|
if (autoPoints) {
|
||||||
update.system.actionTokens = Math.max(combatant.system.actionTokens - 1, 0);
|
update.system.actionTokens = Math.max(combatant.system.actionTokens - 1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (combatant.actor) expireActiveEffects(combatant.actor, [CONFIG.DH.GENERAL.activeEffectDurations.act.id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.viewed.update({
|
await this.viewed.update({
|
||||||
|
|
|
||||||
|
|
@ -233,6 +233,6 @@ export default class CountdownEdit extends HandlebarsApplicationMixin(Applicatio
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.editingCountdowns.has(countdownId)) this.editingCountdowns.delete(countdownId);
|
if (this.editingCountdowns.has(countdownId)) this.editingCountdowns.delete(countdownId);
|
||||||
this.updateSetting({ [`countdowns.-=${countdownId}`]: null });
|
this.updateSetting({ [`countdowns.${countdownId}`]: _del });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,10 +52,6 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
get element() {
|
|
||||||
return document.body.querySelector('.daggerheart.dh-style.countdowns');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**@inheritdoc */
|
/**@inheritdoc */
|
||||||
async _renderFrame(options) {
|
async _renderFrame(options) {
|
||||||
const frame = await super._renderFrame(options);
|
const frame = await super._renderFrame(options);
|
||||||
|
|
@ -68,6 +64,7 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
|
||||||
|
|
||||||
const header = frame.querySelector('.window-header');
|
const header = frame.querySelector('.window-header');
|
||||||
header.querySelector('button[data-action="close"]').remove();
|
header.querySelector('button[data-action="close"]').remove();
|
||||||
|
header.querySelector('button[data-action="toggleControls"]').remove();
|
||||||
|
|
||||||
if (game.user.isGM) {
|
if (game.user.isGM) {
|
||||||
const editTooltip = game.i18n.localize('DAGGERHEART.APPLICATIONS.CountdownEdit.editTitle');
|
const editTooltip = game.i18n.localize('DAGGERHEART.APPLICATIONS.CountdownEdit.editTitle');
|
||||||
|
|
@ -278,9 +275,7 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
|
||||||
return acc;
|
return acc;
|
||||||
}, {})
|
}, {})
|
||||||
};
|
};
|
||||||
await emitAsGM(GMUpdateEvent.UpdateCountdowns,
|
await emitAsGM(GMUpdateEvent.UpdateCountdowns, DhCountdowns.gmSetSetting.bind(settings), settings, null, {
|
||||||
DhCountdowns.gmSetSetting.bind(settings),
|
|
||||||
settings, null, {
|
|
||||||
refreshType: RefreshType.Countdown
|
refreshType: RefreshType.Countdown
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { getIconVisibleActiveEffects } from '../../helpers/utils.mjs';
|
||||||
import { RefreshType } from '../../systemRegistration/socket.mjs';
|
import { RefreshType } from '../../systemRegistration/socket.mjs';
|
||||||
|
|
||||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
@ -72,7 +73,7 @@ export default class DhEffectsDisplay extends HandlebarsApplicationMixin(Applica
|
||||||
? game.user.character
|
? game.user.character
|
||||||
: null
|
: null
|
||||||
: canvas.tokens.controlled[0].actor;
|
: canvas.tokens.controlled[0].actor;
|
||||||
return actor?.getActiveEffects() ?? [];
|
return getIconVisibleActiveEffects(actor?.getActiveEffects() ?? []);
|
||||||
};
|
};
|
||||||
|
|
||||||
toggleHidden(token, focused) {
|
toggleHidden(token, focused) {
|
||||||
|
|
|
||||||
|
|
@ -251,10 +251,10 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
|
|
||||||
/* If any noticeable slowdown occurs, consider replacing with enriching description on clicking to expand descriptions */
|
/* If any noticeable slowdown occurs, consider replacing with enriching description on clicking to expand descriptions */
|
||||||
for (const item of this.items) {
|
for (const item of this.items) {
|
||||||
if (["weapon", "armor"].includes(item.type)) {
|
if (['weapon', 'armor'].includes(item.type)) {
|
||||||
item.system.enrichedTags = await foundry.applications.handlebars.renderTemplate(
|
item.system.enrichedTags = await foundry.applications.handlebars.renderTemplate(
|
||||||
'systems/daggerheart/templates/sheets/global/partials/item-tags.hbs',
|
'systems/daggerheart/templates/sheets/global/partials/item-tags.hbs',
|
||||||
item.system,
|
item.system
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
item.system.enrichedDescription =
|
item.system.enrichedDescription =
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ export default class DhSceneNavigation extends foundry.applications.ui.SceneNavi
|
||||||
const environments = daggerheartInfo.sceneEnvironments.filter(
|
const environments = daggerheartInfo.sceneEnvironments.filter(
|
||||||
x => x && x.testUserPermission(game.user, 'LIMITED')
|
x => x && x.testUserPermission(game.user, 'LIMITED')
|
||||||
);
|
);
|
||||||
const hasEnvironments = environments.length > 0 && x.isView;
|
const hasEnvironments = environments.length > 0 && x.active;
|
||||||
return {
|
return {
|
||||||
...x,
|
...x,
|
||||||
hasEnvironments,
|
hasEnvironments,
|
||||||
|
|
@ -39,9 +39,10 @@ export default class DhSceneNavigation extends foundry.applications.ui.SceneNavi
|
||||||
environments: environments
|
environments: environments
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
context.scenes.active = extendScenes(context.scenes.active);
|
context.scenes.active = extendScenes(context.scenes.active);
|
||||||
context.scenes.inactive = extendScenes(context.scenes.inactive);
|
context.scenes.inactive = extendScenes(context.scenes.inactive);
|
||||||
|
context.scenes.viewed = context.scenes.viewed ? extendScenes([context.scenes.viewed])[0] : null;
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,46 +52,6 @@
|
||||||
* @extends {foundry.applications.ux.ContextMenu}
|
* @extends {foundry.applications.ux.ContextMenu}
|
||||||
*/
|
*/
|
||||||
export default class DHContextMenu extends foundry.applications.ux.ContextMenu {
|
export default class DHContextMenu extends foundry.applications.ux.ContextMenu {
|
||||||
/**
|
|
||||||
* @param {HTMLElement|jQuery} container - The HTML element that contains the context menu targets.
|
|
||||||
* @param {string} selector - A CSS selector which activates the context menu.
|
|
||||||
* @param {ContextMenuEntry[]} menuItems - An Array of entries to display in the menu
|
|
||||||
* @param {ContextMenuOptions} [options] - Additional options to configure the context menu.
|
|
||||||
*/
|
|
||||||
constructor(container, selector, menuItems, options) {
|
|
||||||
super(container, selector, menuItems, options);
|
|
||||||
|
|
||||||
/** @deprecated since v13 until v15 */
|
|
||||||
this.#jQuery = options.jQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to pass jQuery objects or HTMLElement instances to callback.
|
|
||||||
* @type {boolean}
|
|
||||||
*/
|
|
||||||
#jQuery;
|
|
||||||
|
|
||||||
/**@inheritdoc */
|
|
||||||
activateListeners(menu) {
|
|
||||||
menu.addEventListener('click', this.#onClickItem.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle click events on context menu items.
|
|
||||||
* @param {PointerEvent} event The click event
|
|
||||||
*/
|
|
||||||
#onClickItem(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
const element = event.target.closest('.context-item');
|
|
||||||
if (!element) return;
|
|
||||||
const item = this.menuItems.find(i => i.element === element);
|
|
||||||
item?.callback(this.#jQuery ? $(this.target) : this.target, event);
|
|
||||||
this.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trigger a context menu event in response to a normal click on a additional options button.
|
* Trigger a context menu event in response to a normal click on a additional options button.
|
||||||
* @param {PointerEvent} event
|
* @param {PointerEvent} event
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
export { default as DhMeasuredTemplate } from './measuredTemplate.mjs';
|
export { default as DhMeasuredTemplate } from './measuredTemplate.mjs';
|
||||||
export { default as DhRuler } from './ruler.mjs';
|
export { default as DhRuler } from './ruler.mjs';
|
||||||
export { default as DhTemplateLayer } from './templateLayer.mjs';
|
export { default as DhRegion } from './region.mjs';
|
||||||
|
export { default as DhRegionLayer } from './regionLayer.mjs';
|
||||||
export { default as DhTokenPlaceable } from './token.mjs';
|
export { default as DhTokenPlaceable } from './token.mjs';
|
||||||
export { default as DhTokenRuler } from './tokenRuler.mjs';
|
export { default as DhTokenRuler } from './tokenRuler.mjs';
|
||||||
|
|
|
||||||
12
module/canvas/placeables/region.mjs
Normal file
12
module/canvas/placeables/region.mjs
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import DhMeasuredTemplate from './measuredTemplate.mjs';
|
||||||
|
|
||||||
|
export default class DhRegion extends foundry.canvas.placeables.Region {
|
||||||
|
/**@inheritdoc */
|
||||||
|
_formatMeasuredDistance(distance) {
|
||||||
|
const range = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).rangeMeasurement;
|
||||||
|
if (!range.enabled) return super._formatMeasuredDistance(distance);
|
||||||
|
|
||||||
|
const { distance: resultDistance, units } = DhMeasuredTemplate.getRangeLabels(distance, range);
|
||||||
|
return `${resultDistance} ${units}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
98
module/canvas/placeables/regionLayer.mjs
Normal file
98
module/canvas/placeables/regionLayer.mjs
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
export default class DhRegionLayer extends foundry.canvas.layers.RegionLayer {
|
||||||
|
static prepareSceneControls() {
|
||||||
|
const sc = foundry.applications.ui.SceneControls;
|
||||||
|
const { tools, ...rest } = super.prepareSceneControls();
|
||||||
|
|
||||||
|
return {
|
||||||
|
...rest,
|
||||||
|
tools: {
|
||||||
|
select: tools.select,
|
||||||
|
templateMode: tools.templateMode,
|
||||||
|
rectangle: tools.rectangle,
|
||||||
|
circle: tools.circle,
|
||||||
|
ellipse: tools.ellipse,
|
||||||
|
cone: tools.cone,
|
||||||
|
inFront: {
|
||||||
|
name: 'inFront',
|
||||||
|
order: 7,
|
||||||
|
title: 'CONTROLS.inFront',
|
||||||
|
icon: 'fa-solid fa-eye',
|
||||||
|
toolclip: {
|
||||||
|
src: 'toolclips/tools/measure-cone.webm',
|
||||||
|
heading: 'CONTROLS.inFront',
|
||||||
|
items: sc.buildToolclipItems(['create', 'move', 'edit', 'hide', 'delete', 'rotate'])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ring: { ...tools.ring, order: 8 },
|
||||||
|
line: { ...tools.line, order: 9 },
|
||||||
|
emanation: { ...tools.emanation, order: 10 },
|
||||||
|
polygon: { ...tools.polygon, order: 11 },
|
||||||
|
hole: { ...tools.hole, order: 12 },
|
||||||
|
snap: { ...tools.snap, order: 13 },
|
||||||
|
clear: { ...tools.clear, order: 14 }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
_isCreationToolActive() {
|
||||||
|
return this.active && (game.activeTool === 'inFront' || game.activeTool in foundry.data.BaseShapeData.TYPES);
|
||||||
|
}
|
||||||
|
|
||||||
|
_createDragShapeData(event) {
|
||||||
|
const hole = ui.controls.controls[this.options.name].tools.hole?.active ?? false;
|
||||||
|
if (game.activeTool === 'inFront') return { type: 'cone', x: 0, y: 0, radius: 0, angle: 180, hole };
|
||||||
|
|
||||||
|
const shape = super._createDragShapeData(event);
|
||||||
|
const token =
|
||||||
|
shape?.type === 'emanation' && shape.base?.type === 'token'
|
||||||
|
? this.#findTokenInBounds(event.interactionData.origin)
|
||||||
|
: null;
|
||||||
|
if (token) {
|
||||||
|
shape.base.width = token.width;
|
||||||
|
shape.base.height = token.height;
|
||||||
|
event.interactionData.origin = token.getCenterPoint();
|
||||||
|
}
|
||||||
|
return shape;
|
||||||
|
}
|
||||||
|
|
||||||
|
async placeRegion(data, options = {}) {
|
||||||
|
const preConfirm = ({ _event, document, _create, _options }) => {
|
||||||
|
const shape = document.shapes[0];
|
||||||
|
const isEmanation = shape.type === 'emanation';
|
||||||
|
if (isEmanation) {
|
||||||
|
const token = this.#findTokenInBounds(shape.base.origin);
|
||||||
|
if (!token) return options.preConfirm?.() ?? true;
|
||||||
|
const shapeData = shape.toObject();
|
||||||
|
document.updateSource({
|
||||||
|
shapes: [
|
||||||
|
{
|
||||||
|
...shapeData,
|
||||||
|
base: {
|
||||||
|
...shapeData.base,
|
||||||
|
height: token.height,
|
||||||
|
width: token.width,
|
||||||
|
x: token.x,
|
||||||
|
y: token.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return options?.preConfirm?.() ?? true;
|
||||||
|
};
|
||||||
|
|
||||||
|
super.placeRegion(data, { ...options, preConfirm });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Searches for token at origin point, returning null if there are no tokens or multiple overlapping tokens */
|
||||||
|
#findTokenInBounds(origin) {
|
||||||
|
const { x, y } = origin;
|
||||||
|
const gridSize = canvas.grid.size;
|
||||||
|
const inBounds = canvas.scene.tokens.filter(t => {
|
||||||
|
return x.between(t.x, t.x + t.width * gridSize) && y.between(t.y, t.y + t.height * gridSize);
|
||||||
|
});
|
||||||
|
return inBounds.length === 1 ? inBounds[0] : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,116 +0,0 @@
|
||||||
export default class DhTemplateLayer extends foundry.canvas.layers.TemplateLayer {
|
|
||||||
static prepareSceneControls() {
|
|
||||||
const sc = foundry.applications.ui.SceneControls;
|
|
||||||
return {
|
|
||||||
name: 'templates',
|
|
||||||
order: 2,
|
|
||||||
title: 'CONTROLS.GroupMeasure',
|
|
||||||
icon: 'fa-solid fa-ruler-combined',
|
|
||||||
visible: game.user.can('TEMPLATE_CREATE'),
|
|
||||||
onChange: (event, active) => {
|
|
||||||
if (active) canvas.templates.activate();
|
|
||||||
},
|
|
||||||
onToolChange: () => canvas.templates.setAllRenderFlags({ refreshState: true }),
|
|
||||||
tools: {
|
|
||||||
circle: {
|
|
||||||
name: 'circle',
|
|
||||||
order: 1,
|
|
||||||
title: 'CONTROLS.MeasureCircle',
|
|
||||||
icon: 'fa-regular fa-circle',
|
|
||||||
toolclip: {
|
|
||||||
src: 'toolclips/tools/measure-circle.webm',
|
|
||||||
heading: 'CONTROLS.MeasureCircle',
|
|
||||||
items: sc.buildToolclipItems(['create', 'move', 'edit', 'hide', 'delete'])
|
|
||||||
}
|
|
||||||
},
|
|
||||||
cone: {
|
|
||||||
name: 'cone',
|
|
||||||
order: 2,
|
|
||||||
title: 'CONTROLS.MeasureCone',
|
|
||||||
icon: 'fa-solid fa-angle-left',
|
|
||||||
toolclip: {
|
|
||||||
src: 'toolclips/tools/measure-cone.webm',
|
|
||||||
heading: 'CONTROLS.MeasureCone',
|
|
||||||
items: sc.buildToolclipItems(['create', 'move', 'edit', 'hide', 'delete', 'rotate'])
|
|
||||||
}
|
|
||||||
},
|
|
||||||
inFront: {
|
|
||||||
name: 'inFront',
|
|
||||||
order: 3,
|
|
||||||
title: 'CONTROLS.inFront',
|
|
||||||
icon: 'fa-solid fa-eye',
|
|
||||||
toolclip: {
|
|
||||||
src: 'toolclips/tools/measure-cone.webm',
|
|
||||||
heading: 'CONTROLS.inFront',
|
|
||||||
items: sc.buildToolclipItems(['create', 'move', 'edit', 'hide', 'delete', 'rotate'])
|
|
||||||
}
|
|
||||||
},
|
|
||||||
rect: {
|
|
||||||
name: 'rect',
|
|
||||||
order: 4,
|
|
||||||
title: 'CONTROLS.MeasureRect',
|
|
||||||
icon: 'fa-regular fa-square',
|
|
||||||
toolclip: {
|
|
||||||
src: 'toolclips/tools/measure-rect.webm',
|
|
||||||
heading: 'CONTROLS.MeasureRect',
|
|
||||||
items: sc.buildToolclipItems(['create', 'move', 'edit', 'hide', 'delete', 'rotate'])
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ray: {
|
|
||||||
name: 'ray',
|
|
||||||
order: 5,
|
|
||||||
title: 'CONTROLS.MeasureRay',
|
|
||||||
icon: 'fa-solid fa-up-down',
|
|
||||||
toolclip: {
|
|
||||||
src: 'toolclips/tools/measure-ray.webm',
|
|
||||||
heading: 'CONTROLS.MeasureRay',
|
|
||||||
items: sc.buildToolclipItems(['create', 'move', 'edit', 'hide', 'delete', 'rotate'])
|
|
||||||
}
|
|
||||||
},
|
|
||||||
clear: {
|
|
||||||
name: 'clear',
|
|
||||||
order: 6,
|
|
||||||
title: 'CONTROLS.MeasureClear',
|
|
||||||
icon: 'fa-solid fa-trash',
|
|
||||||
visible: game.user.isGM,
|
|
||||||
onChange: () => canvas.templates.deleteAll(),
|
|
||||||
button: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
activeTool: 'circle'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_onDragLeftStart(event) {
|
|
||||||
const interaction = event.interactionData;
|
|
||||||
|
|
||||||
// Snap the origin to the grid
|
|
||||||
if (!event.shiftKey) interaction.origin = this.getSnappedPoint(interaction.origin);
|
|
||||||
|
|
||||||
// Create a pending MeasuredTemplateDocument
|
|
||||||
const tool = game.activeTool === 'inFront' ? 'cone' : game.activeTool;
|
|
||||||
const previewData = {
|
|
||||||
user: game.user.id,
|
|
||||||
t: tool,
|
|
||||||
x: interaction.origin.x,
|
|
||||||
y: interaction.origin.y,
|
|
||||||
sort: Math.max(this.getMaxSort() + 1, 0),
|
|
||||||
distance: 1,
|
|
||||||
direction: 0,
|
|
||||||
fillColor: game.user.color || '#FF0000',
|
|
||||||
hidden: event.altKey
|
|
||||||
};
|
|
||||||
const defaults = CONFIG.MeasuredTemplate.defaults;
|
|
||||||
if (game.activeTool === 'cone') previewData.angle = defaults.angle;
|
|
||||||
else if (game.activeTool === 'inFront') previewData.angle = 180;
|
|
||||||
else if (game.activeTool === 'ray') previewData.width = defaults.width * canvas.dimensions.distance;
|
|
||||||
const cls = foundry.utils.getDocumentClass('MeasuredTemplate');
|
|
||||||
const doc = new cls(previewData, { parent: canvas.scene });
|
|
||||||
|
|
||||||
// Create a preview MeasuredTemplate object
|
|
||||||
const template = new this.constructor.placeableClass(doc);
|
|
||||||
doc._object = template;
|
|
||||||
interaction.preview = this.preview.addChild(template);
|
|
||||||
template.draw();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { getIconVisibleActiveEffects } from '../../helpers/utils.mjs';
|
||||||
import DhMeasuredTemplate from './measuredTemplate.mjs';
|
import DhMeasuredTemplate from './measuredTemplate.mjs';
|
||||||
|
|
||||||
export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
|
export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
|
||||||
|
|
@ -20,7 +21,7 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
|
||||||
this.effects.overlay = null;
|
this.effects.overlay = null;
|
||||||
|
|
||||||
// Categorize effects
|
// Categorize effects
|
||||||
const activeEffects = this.actor?.getActiveEffects() ?? [];
|
const activeEffects = getIconVisibleActiveEffects(Array.from(this.actor?.allApplicableEffects() ?? []));
|
||||||
const overlayEffect = activeEffects.findLast(e => e.img && e.getFlag?.('core', 'overlay'));
|
const overlayEffect = activeEffects.findLast(e => e.img && e.getFlag?.('core', 'overlay'));
|
||||||
|
|
||||||
// Draw effects
|
// Draw effects
|
||||||
|
|
|
||||||
|
|
@ -70,8 +70,12 @@ export const range = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* circle|cone|rect|ray used to be CONST.MEASURED_TEMPLATE_TYPES. Hardcoded for now */
|
||||||
export const templateTypes = {
|
export const templateTypes = {
|
||||||
...CONST.MEASURED_TEMPLATE_TYPES,
|
CIRCLE: 'circle',
|
||||||
|
CONE: 'cone',
|
||||||
|
RECTANGLE: 'rectangle',
|
||||||
|
LINE: 'line',
|
||||||
EMANATION: 'emanation',
|
EMANATION: 'emanation',
|
||||||
INFRONT: 'inFront'
|
INFRONT: 'inFront'
|
||||||
};
|
};
|
||||||
|
|
@ -241,8 +245,8 @@ export const defaultRestOptions = {
|
||||||
type: 'friendly'
|
type: 'friendly'
|
||||||
},
|
},
|
||||||
damage: {
|
damage: {
|
||||||
parts: [
|
parts: {
|
||||||
{
|
hitPoints: {
|
||||||
applyTo: healingTypes.hitPoints.id,
|
applyTo: healingTypes.hitPoints.id,
|
||||||
value: {
|
value: {
|
||||||
custom: {
|
custom: {
|
||||||
|
|
@ -251,7 +255,7 @@ export const defaultRestOptions = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -275,8 +279,8 @@ export const defaultRestOptions = {
|
||||||
type: 'self'
|
type: 'self'
|
||||||
},
|
},
|
||||||
damage: {
|
damage: {
|
||||||
parts: [
|
parts: {
|
||||||
{
|
stress: {
|
||||||
applyTo: healingTypes.stress.id,
|
applyTo: healingTypes.stress.id,
|
||||||
value: {
|
value: {
|
||||||
custom: {
|
custom: {
|
||||||
|
|
@ -285,7 +289,7 @@ export const defaultRestOptions = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -310,8 +314,8 @@ export const defaultRestOptions = {
|
||||||
type: 'friendly'
|
type: 'friendly'
|
||||||
},
|
},
|
||||||
damage: {
|
damage: {
|
||||||
parts: [
|
parts: {
|
||||||
{
|
armor: {
|
||||||
applyTo: healingTypes.armor.id,
|
applyTo: healingTypes.armor.id,
|
||||||
value: {
|
value: {
|
||||||
custom: {
|
custom: {
|
||||||
|
|
@ -320,7 +324,7 @@ export const defaultRestOptions = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -344,8 +348,8 @@ export const defaultRestOptions = {
|
||||||
type: 'self'
|
type: 'self'
|
||||||
},
|
},
|
||||||
damage: {
|
damage: {
|
||||||
parts: [
|
parts: {
|
||||||
{
|
hope: {
|
||||||
applyTo: healingTypes.hope.id,
|
applyTo: healingTypes.hope.id,
|
||||||
value: {
|
value: {
|
||||||
custom: {
|
custom: {
|
||||||
|
|
@ -354,7 +358,7 @@ export const defaultRestOptions = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
prepareWithFriends: {
|
prepareWithFriends: {
|
||||||
|
|
@ -368,8 +372,8 @@ export const defaultRestOptions = {
|
||||||
type: 'self'
|
type: 'self'
|
||||||
},
|
},
|
||||||
damage: {
|
damage: {
|
||||||
parts: [
|
parts: {
|
||||||
{
|
hope: {
|
||||||
applyTo: healingTypes.hope.id,
|
applyTo: healingTypes.hope.id,
|
||||||
value: {
|
value: {
|
||||||
custom: {
|
custom: {
|
||||||
|
|
@ -378,7 +382,7 @@ export const defaultRestOptions = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -405,8 +409,8 @@ export const defaultRestOptions = {
|
||||||
type: 'friendly'
|
type: 'friendly'
|
||||||
},
|
},
|
||||||
damage: {
|
damage: {
|
||||||
parts: [
|
parts: {
|
||||||
{
|
hitPoints: {
|
||||||
applyTo: healingTypes.hitPoints.id,
|
applyTo: healingTypes.hitPoints.id,
|
||||||
value: {
|
value: {
|
||||||
custom: {
|
custom: {
|
||||||
|
|
@ -415,7 +419,7 @@ export const defaultRestOptions = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -439,8 +443,8 @@ export const defaultRestOptions = {
|
||||||
type: 'self'
|
type: 'self'
|
||||||
},
|
},
|
||||||
damage: {
|
damage: {
|
||||||
parts: [
|
parts: {
|
||||||
{
|
stress: {
|
||||||
applyTo: healingTypes.stress.id,
|
applyTo: healingTypes.stress.id,
|
||||||
value: {
|
value: {
|
||||||
custom: {
|
custom: {
|
||||||
|
|
@ -449,7 +453,7 @@ export const defaultRestOptions = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -474,8 +478,8 @@ export const defaultRestOptions = {
|
||||||
type: 'friendly'
|
type: 'friendly'
|
||||||
},
|
},
|
||||||
damage: {
|
damage: {
|
||||||
parts: [
|
parts: {
|
||||||
{
|
armor: {
|
||||||
applyTo: healingTypes.armor.id,
|
applyTo: healingTypes.armor.id,
|
||||||
value: {
|
value: {
|
||||||
custom: {
|
custom: {
|
||||||
|
|
@ -484,7 +488,7 @@ export const defaultRestOptions = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -508,8 +512,8 @@ export const defaultRestOptions = {
|
||||||
type: 'self'
|
type: 'self'
|
||||||
},
|
},
|
||||||
damage: {
|
damage: {
|
||||||
parts: [
|
parts: {
|
||||||
{
|
hope: {
|
||||||
applyTo: healingTypes.hope.id,
|
applyTo: healingTypes.hope.id,
|
||||||
value: {
|
value: {
|
||||||
custom: {
|
custom: {
|
||||||
|
|
@ -518,7 +522,7 @@ export const defaultRestOptions = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
prepareWithFriends: {
|
prepareWithFriends: {
|
||||||
|
|
@ -532,8 +536,8 @@ export const defaultRestOptions = {
|
||||||
type: 'self'
|
type: 'self'
|
||||||
},
|
},
|
||||||
damage: {
|
damage: {
|
||||||
parts: [
|
parts: {
|
||||||
{
|
hope: {
|
||||||
applyTo: healingTypes.hope.id,
|
applyTo: healingTypes.hope.id,
|
||||||
value: {
|
value: {
|
||||||
custom: {
|
custom: {
|
||||||
|
|
@ -542,7 +546,7 @@ export const defaultRestOptions = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -630,7 +634,95 @@ export const diceSetNumbers = {
|
||||||
flat: 'Flat'
|
flat: 'Flat'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getDiceSoNicePreset = async (type, faces) => {
|
export const diceSoNiceSFXClasses = {
|
||||||
|
PlayAnimationBright: {
|
||||||
|
id: 'PlayAnimationBright',
|
||||||
|
label: 'DICESONICE.PlayAnimationBright'
|
||||||
|
},
|
||||||
|
PlayAnimationDark: {
|
||||||
|
id: 'PlayAnimationDark',
|
||||||
|
label: 'DICESONICE.PlayAnimationDark'
|
||||||
|
},
|
||||||
|
PlayAnimationOutline: {
|
||||||
|
id: 'PlayAnimationOutline',
|
||||||
|
label: 'DICESONICE.PlayAnimationOutline'
|
||||||
|
},
|
||||||
|
PlayAnimationImpact: {
|
||||||
|
id: 'PlayAnimationImpact',
|
||||||
|
label: 'DICESONICE.PlayAnimationImpact'
|
||||||
|
},
|
||||||
|
// PlayConfettiStrength1: {
|
||||||
|
// id: 'PlayConfettiStrength1',
|
||||||
|
// label: 'DICESONICE.PlayConfettiStrength1'
|
||||||
|
// },
|
||||||
|
// PlayConfettiStrength2: {
|
||||||
|
// id: 'PlayConfettiStrength2',
|
||||||
|
// label: 'DICESONICE.PlayConfettiStrength2'
|
||||||
|
// },
|
||||||
|
// PlayConfettiStrength3: {
|
||||||
|
// id: 'PlayConfettiStrength3',
|
||||||
|
// label: 'DICESONICE.PlayConfettiStrength3'
|
||||||
|
// },
|
||||||
|
PlayAnimationThormund: {
|
||||||
|
id: 'PlayAnimationThormund',
|
||||||
|
label: 'DICESONICE.PlayAnimationThormund'
|
||||||
|
},
|
||||||
|
PlayAnimationParticleSpiral: {
|
||||||
|
id: 'PlayAnimationParticleSpiral',
|
||||||
|
label: 'DICESONICE.PlayAnimationParticleSpiral'
|
||||||
|
},
|
||||||
|
PlayAnimationParticleSparkles: {
|
||||||
|
id: 'PlayAnimationParticleSparkles',
|
||||||
|
label: 'DICESONICE.PlayAnimationParticleSparkles'
|
||||||
|
},
|
||||||
|
PlayAnimationParticleVortex: {
|
||||||
|
id: 'PlayAnimationParticleVortex',
|
||||||
|
label: 'DICESONICE.PlayAnimationParticleVortex'
|
||||||
|
},
|
||||||
|
PlaySoundEpicWin: {
|
||||||
|
id: 'PlaySoundEpicWin',
|
||||||
|
label: 'DICESONICE.PlaySoundEpicWin'
|
||||||
|
},
|
||||||
|
PlaySoundEpicFail: {
|
||||||
|
id: 'PlaySoundEpicFail',
|
||||||
|
label: 'DICESONICE.PlaySoundEpicFail'
|
||||||
|
}
|
||||||
|
// "PlaySoundCustom",
|
||||||
|
// "PlayMacro"
|
||||||
|
};
|
||||||
|
|
||||||
|
export const daggerheartDiceAnimationEvents = {
|
||||||
|
critical: {
|
||||||
|
id: 'critical',
|
||||||
|
label: 'DAGGERHEART.CONFIG.DaggerheartDiceAnimationEvents.critical.name'
|
||||||
|
},
|
||||||
|
higher: {
|
||||||
|
id: 'higher',
|
||||||
|
label: 'DAGGERHEART.CONFIG.DaggerheartDiceAnimationEvents.higher.name'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDiceSoNiceSFX = sfxOptions => {
|
||||||
|
const diceSoNice = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance).diceSoNiceData;
|
||||||
|
const criticalAnimationData = diceSoNice.sfx.critical;
|
||||||
|
if (sfxOptions.critical && criticalAnimationData.class) {
|
||||||
|
return {
|
||||||
|
specialEffect: criticalAnimationData.class,
|
||||||
|
options: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sfxOptions.higher && sfxOptions.data.higher) {
|
||||||
|
return {
|
||||||
|
specialEffect: sfxOptions.data.higher.class,
|
||||||
|
options: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getDiceSoNicePreset = async (type, faces, sfxOptions = {}) => {
|
||||||
const system = game.dice3d.DiceFactory.systems.get(type.system).dice.get(faces);
|
const system = game.dice3d.DiceFactory.systems.get(type.system).dice.get(faces);
|
||||||
if (!system) {
|
if (!system) {
|
||||||
ui.notifications.error(
|
ui.notifications.error(
|
||||||
|
|
@ -653,16 +745,33 @@ export const getDiceSoNicePreset = async (type, faces) => {
|
||||||
appearance: {
|
appearance: {
|
||||||
...system.appearance,
|
...system.appearance,
|
||||||
...type
|
...type
|
||||||
}
|
},
|
||||||
|
sfx: getDiceSoNiceSFX(sfxOptions)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getDiceSoNicePresets = async (hopeFaces, fearFaces, advantageFaces = 'd6', disadvantageFaces = 'd6') => {
|
export const getDiceSoNicePresets = async (
|
||||||
const { diceSoNice } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance);
|
result,
|
||||||
|
hopeFaces,
|
||||||
|
fearFaces,
|
||||||
|
advantageFaces = 'd6',
|
||||||
|
disadvantageFaces = 'd6'
|
||||||
|
) => {
|
||||||
|
const diceSoNice = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance).diceSoNiceData;
|
||||||
|
|
||||||
|
const { isCritical, withHope, withFear } = result;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
hope: await getDiceSoNicePreset(diceSoNice.hope, hopeFaces),
|
hope: await getDiceSoNicePreset(diceSoNice.hope, hopeFaces, {
|
||||||
fear: await getDiceSoNicePreset(diceSoNice.fear, fearFaces),
|
critical: isCritical,
|
||||||
|
higher: withHope,
|
||||||
|
data: diceSoNice.hope.sfx
|
||||||
|
}),
|
||||||
|
fear: await getDiceSoNicePreset(diceSoNice.fear, fearFaces, {
|
||||||
|
critical: isCritical,
|
||||||
|
higher: withFear,
|
||||||
|
data: diceSoNice.fear.sfx
|
||||||
|
}),
|
||||||
advantage: await getDiceSoNicePreset(diceSoNice.advantage, advantageFaces),
|
advantage: await getDiceSoNicePreset(diceSoNice.advantage, advantageFaces),
|
||||||
disadvantage: await getDiceSoNicePreset(diceSoNice.disadvantage, disadvantageFaces)
|
disadvantage: await getDiceSoNicePreset(diceSoNice.disadvantage, disadvantageFaces)
|
||||||
};
|
};
|
||||||
|
|
@ -864,3 +973,72 @@ export const tagTeamRollTypes = {
|
||||||
label: 'DAGGERHEART.CONFIG.TagTeamRollTypes.damageAbility'
|
label: 'DAGGERHEART.CONFIG.TagTeamRollTypes.damageAbility'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const activeEffectModes = {
|
||||||
|
custom: {
|
||||||
|
id: 'custom',
|
||||||
|
priority: 0,
|
||||||
|
label: 'EFFECT.CHANGES.TYPES.custom'
|
||||||
|
},
|
||||||
|
multiply: {
|
||||||
|
id: 'multiply',
|
||||||
|
priority: 10,
|
||||||
|
label: 'EFFECT.CHANGES.TYPES.multiply'
|
||||||
|
},
|
||||||
|
add: {
|
||||||
|
id: 'add',
|
||||||
|
priority: 20,
|
||||||
|
label: 'EFFECT.CHANGES.TYPES.add'
|
||||||
|
},
|
||||||
|
subtract: {
|
||||||
|
id: 'subtract',
|
||||||
|
priority: 20,
|
||||||
|
label: 'EFFECT.CHANGES.TYPES.subtract'
|
||||||
|
},
|
||||||
|
downgrade: {
|
||||||
|
id: 'downgrade',
|
||||||
|
priority: 30,
|
||||||
|
label: 'EFFECT.CHANGES.TYPES.downgrade'
|
||||||
|
},
|
||||||
|
upgrade: {
|
||||||
|
id: 'upgrade',
|
||||||
|
priority: 40,
|
||||||
|
label: 'EFFECT.CHANGES.TYPES.upgrade'
|
||||||
|
},
|
||||||
|
override: {
|
||||||
|
id: 'override',
|
||||||
|
priority: 50,
|
||||||
|
label: 'EFFECT.CHANGES.TYPES.override'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const activeEffectDurations = {
|
||||||
|
temporary: {
|
||||||
|
id: 'temporary',
|
||||||
|
label: 'DAGGERHEART.CONFIG.ActiveEffectDuration.temporary'
|
||||||
|
},
|
||||||
|
act: {
|
||||||
|
id: 'act',
|
||||||
|
label: 'DAGGERHEART.CONFIG.ActiveEffectDuration.act'
|
||||||
|
},
|
||||||
|
scene: {
|
||||||
|
id: 'scene',
|
||||||
|
label: 'DAGGERHEART.CONFIG.ActiveEffectDuration.scene'
|
||||||
|
},
|
||||||
|
shortRest: {
|
||||||
|
id: 'shortRest',
|
||||||
|
label: 'DAGGERHEART.CONFIG.ActiveEffectDuration.shortRest'
|
||||||
|
},
|
||||||
|
longRest: {
|
||||||
|
id: 'longRest',
|
||||||
|
label: 'DAGGERHEART.CONFIG.ActiveEffectDuration.longRest'
|
||||||
|
},
|
||||||
|
session: {
|
||||||
|
id: 'session',
|
||||||
|
label: 'DAGGERHEART.CONFIG.ActiveEffectDuration.session'
|
||||||
|
},
|
||||||
|
custom: {
|
||||||
|
id: 'custom',
|
||||||
|
label: 'DAGGERHEART.CONFIG.ActiveEffectDuration.custom'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ export const armorFeatures = {
|
||||||
type: 'hostile'
|
type: 'hostile'
|
||||||
},
|
},
|
||||||
damage: {
|
damage: {
|
||||||
parts: [
|
parts: {
|
||||||
{
|
stress: {
|
||||||
applyTo: 'stress',
|
applyTo: 'stress',
|
||||||
value: {
|
value: {
|
||||||
custom: {
|
custom: {
|
||||||
|
|
@ -24,7 +24,7 @@ export const armorFeatures = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -732,8 +732,8 @@ export const weaponFeatures = {
|
||||||
type: 'hostile'
|
type: 'hostile'
|
||||||
},
|
},
|
||||||
damage: {
|
damage: {
|
||||||
parts: [
|
parts: {
|
||||||
{
|
stress: {
|
||||||
applyTo: 'stress',
|
applyTo: 'stress',
|
||||||
value: {
|
value: {
|
||||||
custom: {
|
custom: {
|
||||||
|
|
@ -742,7 +742,7 @@ export const weaponFeatures = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
@ -914,8 +914,8 @@ export const weaponFeatures = {
|
||||||
type: 'self'
|
type: 'self'
|
||||||
},
|
},
|
||||||
damage: {
|
damage: {
|
||||||
parts: [
|
parts: {
|
||||||
{
|
hitPoints: {
|
||||||
applyTo: 'hitPoints',
|
applyTo: 'hitPoints',
|
||||||
value: {
|
value: {
|
||||||
custom: {
|
custom: {
|
||||||
|
|
@ -924,7 +924,7 @@ export const weaponFeatures = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -72,17 +72,17 @@ const companionBaseResources = Object.freeze({
|
||||||
export const character = {
|
export const character = {
|
||||||
base: characterBaseResources,
|
base: characterBaseResources,
|
||||||
custom: {}, // module stuff goes here
|
custom: {}, // module stuff goes here
|
||||||
all: { ...characterBaseResources },
|
all: { ...characterBaseResources }
|
||||||
};
|
};
|
||||||
|
|
||||||
export const adversary = {
|
export const adversary = {
|
||||||
base: adversaryBaseResources,
|
base: adversaryBaseResources,
|
||||||
custom: {}, // module stuff goes here
|
custom: {}, // module stuff goes here
|
||||||
all: { ...adversaryBaseResources },
|
all: { ...adversaryBaseResources }
|
||||||
};
|
};
|
||||||
|
|
||||||
export const companion = {
|
export const companion = {
|
||||||
base: companionBaseResources,
|
base: companionBaseResources,
|
||||||
custom: {}, // module stuff goes here
|
custom: {}, // module stuff goes here
|
||||||
all: { ...companionBaseResources },
|
all: { ...companionBaseResources }
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ export const gameSettings = {
|
||||||
Metagaming: 'Metagaming',
|
Metagaming: 'Metagaming',
|
||||||
Homebrew: 'Homebrew',
|
Homebrew: 'Homebrew',
|
||||||
appearance: 'Appearance',
|
appearance: 'Appearance',
|
||||||
|
GlobalOverrides: 'GlobalOverrides',
|
||||||
variantRules: 'VariantRules',
|
variantRules: 'VariantRules',
|
||||||
Resources: {
|
Resources: {
|
||||||
Fear: 'ResourcesFear'
|
Fear: 'ResourcesFear'
|
||||||
|
|
|
||||||
|
|
@ -26,23 +26,23 @@ export default class DHAttackAction extends DHDamageAction {
|
||||||
return {
|
return {
|
||||||
value: {
|
value: {
|
||||||
multiplier: 'prof',
|
multiplier: 'prof',
|
||||||
dice: this.item?.system?.attack.damage.parts[0].value.dice,
|
dice: this.item?.system?.attack.damage.parts.hitPoints.value.dice,
|
||||||
bonus: this.item?.system?.attack.damage.parts[0].value.bonus ?? 0
|
bonus: this.item?.system?.attack.damage.parts.hitPoints.value.bonus ?? 0
|
||||||
},
|
},
|
||||||
type: this.item?.system?.attack.damage.parts[0].type,
|
type: this.item?.system?.attack.damage.parts.hitPoints.type,
|
||||||
base: true
|
base: true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
get damageFormula() {
|
get damageFormula() {
|
||||||
const hitPointsPart = this.damage.parts.find(x => x.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id);
|
const hitPointsPart = this.damage.parts.hitPoints;
|
||||||
if (!hitPointsPart) return '0';
|
if (!hitPointsPart) return '0';
|
||||||
|
|
||||||
return hitPointsPart.value.getFormula();
|
return hitPointsPart.value.getFormula();
|
||||||
}
|
}
|
||||||
|
|
||||||
get altDamageFormula() {
|
get altDamageFormula() {
|
||||||
const hitPointsPart = this.damage.parts.find(x => x.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id);
|
const hitPointsPart = this.damage.parts.hitPoints;
|
||||||
if (!hitPointsPart) return '0';
|
if (!hitPointsPart) return '0';
|
||||||
|
|
||||||
return hitPointsPart.valueAlt.getFormula();
|
return hitPointsPart.valueAlt.getFormula();
|
||||||
|
|
|
||||||
|
|
@ -355,11 +355,11 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasDamage() {
|
get hasDamage() {
|
||||||
return this.damage?.parts?.length && this.type !== 'healing';
|
return !foundry.utils.isEmpty(this.damage?.parts) && this.type !== 'healing';
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasHealing() {
|
get hasHealing() {
|
||||||
return this.damage?.parts?.length && this.type === 'healing';
|
return !foundry.utils.isEmpty(this.damage?.parts) && this.type === 'healing';
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasSave() {
|
get hasSave() {
|
||||||
|
|
@ -379,6 +379,15 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
|
|
||||||
return tags;
|
return tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static migrateData(source) {
|
||||||
|
if (source.damage?.parts && Array.isArray(source.damage.parts)) {
|
||||||
|
source.damage.parts = source.damage.parts.reduce((acc, part) => {
|
||||||
|
acc[part.applyTo] = part;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ResourceUpdateMap extends Map {
|
export class ResourceUpdateMap extends Map {
|
||||||
|
|
|
||||||
|
|
@ -2,84 +2,4 @@ import DHBaseAction from './baseAction.mjs';
|
||||||
|
|
||||||
export default class DhBeastformAction extends DHBaseAction {
|
export default class DhBeastformAction extends DHBaseAction {
|
||||||
static extraSchemas = [...super.extraSchemas, 'beastform'];
|
static extraSchemas = [...super.extraSchemas, 'beastform'];
|
||||||
|
|
||||||
/* async use(event, options) {
|
|
||||||
const beastformConfig = this.prepareBeastformConfig();
|
|
||||||
|
|
||||||
const abort = await this.handleActiveTransformations();
|
|
||||||
if (abort) return;
|
|
||||||
|
|
||||||
const calcCosts = game.system.api.fields.ActionFields.CostField.calcCosts.call(this, this.cost);
|
|
||||||
const hasCost = game.system.api.fields.ActionFields.CostField.hasCost.call(this, calcCosts);
|
|
||||||
if (!hasCost) {
|
|
||||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.insufficientResources'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { selected, evolved, hybrid } = await BeastformDialog.configure(beastformConfig, this.item);
|
|
||||||
if (!selected) return;
|
|
||||||
|
|
||||||
const result = await super.use(event, options);
|
|
||||||
if (!result) return;
|
|
||||||
|
|
||||||
await this.transform(selected, evolved, hybrid);
|
|
||||||
}
|
|
||||||
|
|
||||||
prepareBeastformConfig(config) {
|
|
||||||
const settingsTiers = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers;
|
|
||||||
const actorLevel = this.actor.system.levelData.level.current;
|
|
||||||
const actorTier =
|
|
||||||
Object.values(settingsTiers).find(
|
|
||||||
tier => actorLevel >= tier.levels.start && actorLevel <= tier.levels.end
|
|
||||||
) ?? 1;
|
|
||||||
|
|
||||||
return {
|
|
||||||
tierLimit: this.beastform.tierAccess.exact ?? actorTier
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async transform(selectedForm, evolvedData, hybridData) {
|
|
||||||
const formData = evolvedData?.form ? evolvedData.form.toObject() : selectedForm.toObject();
|
|
||||||
const beastformEffect = formData.effects.find(x => x.type === 'beastform');
|
|
||||||
if (!beastformEffect) {
|
|
||||||
ui.notifications.error('DAGGERHEART.UI.Notifications.beastformMissingEffect');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (evolvedData?.form) {
|
|
||||||
const evolvedForm = selectedForm.effects.find(x => x.type === 'beastform');
|
|
||||||
if (!evolvedForm) {
|
|
||||||
ui.notifications.error('DAGGERHEART.UI.Notifications.beastformMissingEffect');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
beastformEffect.changes = [...beastformEffect.changes, ...evolvedForm.changes];
|
|
||||||
formData.system.features = [...formData.system.features, ...selectedForm.system.features.map(x => x.uuid)];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedForm.system.beastformType === CONFIG.DH.ITEM.beastformTypes.hybrid.id) {
|
|
||||||
formData.system.advantageOn = Object.values(hybridData.advantages).reduce((advantages, formCategory) => {
|
|
||||||
Object.keys(formCategory).forEach(advantageKey => {
|
|
||||||
advantages[advantageKey] = formCategory[advantageKey];
|
|
||||||
});
|
|
||||||
return advantages;
|
|
||||||
}, {});
|
|
||||||
formData.system.features = [
|
|
||||||
...formData.system.features,
|
|
||||||
...Object.values(hybridData.features).flatMap(x => Object.keys(x))
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
this.actor.createEmbeddedDocuments('Item', [formData]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async handleActiveTransformations() {
|
|
||||||
const beastformEffects = this.actor.effects.filter(x => x.type === 'beastform');
|
|
||||||
const existingEffects = beastformEffects.length > 0;
|
|
||||||
await this.actor.deleteEmbeddedDocuments(
|
|
||||||
'ActiveEffect',
|
|
||||||
beastformEffects.map(x => x.id)
|
|
||||||
);
|
|
||||||
return existingEffects;
|
|
||||||
} */
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,35 @@
|
||||||
* "Anything that uses another data model value as its value": +1 - Effects that increase traits have to be calculated first at Base priority. (EX: Raise evasion by half your agility)
|
* "Anything that uses another data model value as its value": +1 - Effects that increase traits have to be calculated first at Base priority. (EX: Raise evasion by half your agility)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default class BaseEffect extends foundry.abstract.TypeDataModel {
|
export default class BaseEffect extends foundry.data.ActiveEffectTypeDataModel {
|
||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
...super.defineSchema(),
|
||||||
|
changes: new fields.ArrayField(
|
||||||
|
new fields.SchemaField({
|
||||||
|
key: new fields.StringField({ required: true }),
|
||||||
|
type: new fields.StringField({
|
||||||
|
required: true,
|
||||||
|
blank: false,
|
||||||
|
choices: CONFIG.DH.GENERAL.activeEffectModes,
|
||||||
|
initial: CONFIG.DH.GENERAL.activeEffectModes.add.id,
|
||||||
|
validate: BaseEffect.#validateType
|
||||||
|
}),
|
||||||
|
value: new fields.AnyField({ required: true, nullable: true, serializable: true, initial: '' }),
|
||||||
|
phase: new fields.StringField({ required: true, blank: false, initial: 'initial' }),
|
||||||
|
priority: new fields.NumberField()
|
||||||
|
})
|
||||||
|
),
|
||||||
|
duration: new fields.SchemaField({
|
||||||
|
type: new fields.StringField({
|
||||||
|
choices: CONFIG.DH.GENERAL.activeEffectDurations,
|
||||||
|
blank: true,
|
||||||
|
label: 'DAGGERHEART.GENERAL.type'
|
||||||
|
}),
|
||||||
|
description: new fields.HTMLField({ label: 'DAGGERHEART.GENERAL.description' })
|
||||||
|
}),
|
||||||
rangeDependence: new fields.SchemaField({
|
rangeDependence: new fields.SchemaField({
|
||||||
enabled: new fields.BooleanField({
|
enabled: new fields.BooleanField({
|
||||||
required: true,
|
required: true,
|
||||||
|
|
@ -45,6 +69,23 @@ export default class BaseEffect extends foundry.abstract.TypeDataModel {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate that an {@link EffectChangeData#type} string is well-formed.
|
||||||
|
* @param {string} type The string to be validated
|
||||||
|
* @returns {true}
|
||||||
|
* @throws {Error} An error if the type string is malformed
|
||||||
|
*/
|
||||||
|
static #validateType(type) {
|
||||||
|
if (type.length < 3) throw new Error('must be at least three characters long');
|
||||||
|
if (!/^custom\.-?\d+$/.test(type) && !type.split('.').every(s => /^[a-z0-9]+$/i.test(s))) {
|
||||||
|
throw new Error(
|
||||||
|
'A change type must either be a sequence of dot-delimited, alpha-numeric substrings or of the form' +
|
||||||
|
' "custom.{number}"'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static getDefaultObject() {
|
static getDefaultObject() {
|
||||||
return {
|
return {
|
||||||
name: 'New Effect',
|
name: 'New Effect',
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ export default class BeastformEffect extends BaseEffect {
|
||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
return {
|
return {
|
||||||
|
...super.defineSchema(),
|
||||||
characterTokenData: new fields.SchemaField({
|
characterTokenData: new fields.SchemaField({
|
||||||
usesDynamicToken: new fields.BooleanField({ initial: false }),
|
usesDynamicToken: new fields.BooleanField({ initial: false }),
|
||||||
tokenImg: new fields.FilePathField({
|
tokenImg: new fields.FilePathField({
|
||||||
|
|
@ -99,7 +100,7 @@ export default class BeastformEffect extends BaseEffect {
|
||||||
token.flags.daggerheart?.beastformSubjectTexture ?? this.characterTokenData.tokenRingImg
|
token.flags.daggerheart?.beastformSubjectTexture ?? this.characterTokenData.tokenRingImg
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'flags.daggerheart': { '-=beastformTokenImg': null, '-=beastformSubjectTexture': null }
|
'flags.daggerheart': { beastformTokenImg: _del, beastformSubjectTexture: _del }
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -85,14 +85,14 @@ export default class DhpAdversary extends DhCreature {
|
||||||
type: 'attack'
|
type: 'attack'
|
||||||
},
|
},
|
||||||
damage: {
|
damage: {
|
||||||
parts: [
|
parts: {
|
||||||
{
|
hitPoints: {
|
||||||
type: ['physical'],
|
type: ['physical'],
|
||||||
value: {
|
value: {
|
||||||
multiplier: 'flat'
|
multiplier: 'flat'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
@ -265,12 +265,12 @@ export default class DhpAdversary extends DhCreature {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update damage in item actions
|
// Update damage in item actions
|
||||||
for (const action of Object.values(item.system.actions)) {
|
|
||||||
if (!action.damage) continue;
|
|
||||||
|
|
||||||
// Parse damage, and convert all formula matches in the descriptions to the new damage
|
// Parse damage, and convert all formula matches in the descriptions to the new damage
|
||||||
|
for (const action of Object.values(item.system.actions)) {
|
||||||
try {
|
try {
|
||||||
const result = this.#adjustActionDamage(action, { ...damageMeta, type: 'action' });
|
const result = this.#adjustActionDamage(action, { ...damageMeta, type: 'action' });
|
||||||
|
if (!result) continue;
|
||||||
|
|
||||||
for (const { previousFormula, formula } of Object.values(result)) {
|
for (const { previousFormula, formula } of Object.values(result)) {
|
||||||
const oldFormulaRegexp = new RegExp(
|
const oldFormulaRegexp = new RegExp(
|
||||||
previousFormula.replace(' ', '').replace('+', '(?:\\s)?\\+(?:\\s)?')
|
previousFormula.replace(' ', '').replace('+', '(?:\\s)?\\+(?:\\s)?')
|
||||||
|
|
@ -372,16 +372,14 @@ export default class DhpAdversary extends DhCreature {
|
||||||
/**
|
/**
|
||||||
* Updates damage to reflect a specific value.
|
* Updates damage to reflect a specific value.
|
||||||
* @throws if damage structure is invalid for conversion
|
* @throws if damage structure is invalid for conversion
|
||||||
* @returns the converted formula and value as a simplified term
|
* @returns the converted formula and value as a simplified term, or null if it doesn't deal HP damage
|
||||||
*/
|
*/
|
||||||
#adjustActionDamage(action, damageMeta) {
|
#adjustActionDamage(action, damageMeta) {
|
||||||
// The current algorithm only returns a value if there is a single damage part
|
if (!action.damage?.parts.hitPoints) return null;
|
||||||
const hpDamageParts = action.damage.parts.filter(d => d.applyTo === 'hitPoints');
|
|
||||||
if (hpDamageParts.length !== 1) throw new Error('incorrect number of hp parts');
|
|
||||||
|
|
||||||
const result = {};
|
const result = {};
|
||||||
for (const property of ['value', 'valueAlt']) {
|
for (const property of ['value', 'valueAlt']) {
|
||||||
const data = hpDamageParts[0][property];
|
const data = action.damage.parts.hitPoints[property];
|
||||||
const previousFormula = data.custom.enabled
|
const previousFormula = data.custom.enabled
|
||||||
? data.custom.formula
|
? data.custom.formula
|
||||||
: [data.flatMultiplier ? `${data.flatMultiplier}${data.dice}` : 0, data.bonus ?? 0]
|
: [data.flatMultiplier ? `${data.flatMultiplier}${data.dice}` : 0, data.bonus ?? 0]
|
||||||
|
|
|
||||||
|
|
@ -96,8 +96,8 @@ export default class DhCharacter extends DhCreature {
|
||||||
trait: 'strength'
|
trait: 'strength'
|
||||||
},
|
},
|
||||||
damage: {
|
damage: {
|
||||||
parts: [
|
parts: {
|
||||||
{
|
hitPoints: {
|
||||||
type: ['physical'],
|
type: ['physical'],
|
||||||
value: {
|
value: {
|
||||||
custom: {
|
custom: {
|
||||||
|
|
@ -106,7 +106,7 @@ export default class DhCharacter extends DhCreature {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
@ -685,7 +685,7 @@ export default class DhCharacter extends DhCreature {
|
||||||
isReversed: true
|
isReversed: true
|
||||||
};
|
};
|
||||||
|
|
||||||
this.attack.damage.parts[0].value.custom.formula = `@prof${this.basicAttackDamageDice}${this.rules.attack.damage.bonus ? ` + ${this.rules.attack.damage.bonus}` : ''}`;
|
this.attack.damage.parts.hitPoints.value.custom.formula = `@prof${this.basicAttackDamageDice}${this.rules.attack.damage.bonus ? ` + ${this.rules.attack.damage.bonus}` : ''}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
getRollData() {
|
getRollData() {
|
||||||
|
|
@ -721,7 +721,8 @@ export default class DhCharacter extends DhCreature {
|
||||||
const newHopeMax = this.system.resources.hope.max + diff;
|
const newHopeMax = this.system.resources.hope.max + diff;
|
||||||
const newHopeValue = Math.min(newHopeMax, this.system.resources.hope.value);
|
const newHopeValue = Math.min(newHopeMax, this.system.resources.hope.value);
|
||||||
if (newHopeValue != this.system.resources.hope.value) {
|
if (newHopeValue != this.system.resources.hope.value) {
|
||||||
if (!changes.system.resources) changes.system.resources = { hope: { value: 0 } };
|
if (!changes.system.resources.hope) changes.system.resources.hope = { value: 0 };
|
||||||
|
|
||||||
changes.system.resources.hope = {
|
changes.system.resources.hope = {
|
||||||
...changes.system.resources.hope,
|
...changes.system.resources.hope,
|
||||||
value: changes.system.resources.hope.value + newHopeValue
|
value: changes.system.resources.hope.value + newHopeValue
|
||||||
|
|
|
||||||
|
|
@ -81,15 +81,15 @@ export default class DhCompanion extends DhCreature {
|
||||||
bonus: 0
|
bonus: 0
|
||||||
},
|
},
|
||||||
damage: {
|
damage: {
|
||||||
parts: [
|
parts: {
|
||||||
{
|
hitPoints: {
|
||||||
type: ['physical'],
|
type: ['physical'],
|
||||||
value: {
|
value: {
|
||||||
dice: 'd6',
|
dice: 'd6',
|
||||||
multiplier: 'prof'
|
multiplier: 'prof'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
@ -135,7 +135,9 @@ export default class DhCompanion extends DhCreature {
|
||||||
break;
|
break;
|
||||||
case 'vicious':
|
case 'vicious':
|
||||||
if (selection.data[0] === 'damage') {
|
if (selection.data[0] === 'damage') {
|
||||||
this.attack.damage.parts[0].value.dice = adjustDice(this.attack.damage.parts[0].value.dice);
|
this.attack.damage.parts.hitPoints.value.dice = adjustDice(
|
||||||
|
this.attack.damage.parts.hitPoints.value.dice
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
this.attack.range = adjustRange(this.attack.range).id;
|
this.attack.range = adjustRange(this.attack.range).id;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
export { ActionCollection } from './actionField.mjs';
|
export { ActionCollection } from './actionField.mjs';
|
||||||
|
export { default as IterableTypedObjectField } from './iterableTypedObjectField.mjs';
|
||||||
export { default as FormulaField } from './formulaField.mjs';
|
export { default as FormulaField } from './formulaField.mjs';
|
||||||
export { default as ForeignDocumentUUIDField } from './foreignDocumentUUIDField.mjs';
|
export { default as ForeignDocumentUUIDField } from './foreignDocumentUUIDField.mjs';
|
||||||
export { default as ForeignDocumentUUIDArrayField } from './foreignDocumentUUIDArrayField.mjs';
|
export { default as ForeignDocumentUUIDArrayField } from './foreignDocumentUUIDArrayField.mjs';
|
||||||
export { default as TriggerField } from './triggerField.mjs';
|
export { default as TriggerField } from './triggerField.mjs';
|
||||||
export { default as MappingField } from './mappingField.mjs';
|
|
||||||
export * as ActionFields from './action/_module.mjs';
|
export * as ActionFields from './action/_module.mjs';
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import FormulaField from '../formulaField.mjs';
|
import FormulaField from '../formulaField.mjs';
|
||||||
import { setsEqual } from '../../../helpers/utils.mjs';
|
import { setsEqual } from '../../../helpers/utils.mjs';
|
||||||
|
import IterableTypedObjectField from '../iterableTypedObjectField.mjs';
|
||||||
|
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
|
|
@ -12,7 +13,7 @@ export default class DamageField extends fields.SchemaField {
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
constructor(options, context = {}) {
|
constructor(options, context = {}) {
|
||||||
const damageFields = {
|
const damageFields = {
|
||||||
parts: new fields.ArrayField(new fields.EmbeddedDataField(DHDamageData)),
|
parts: new IterableTypedObjectField(DHDamageData),
|
||||||
includeBase: new fields.BooleanField({
|
includeBase: new fields.BooleanField({
|
||||||
initial: false,
|
initial: false,
|
||||||
label: 'DAGGERHEART.ACTIONS.Settings.includeBase.label'
|
label: 'DAGGERHEART.ACTIONS.Settings.includeBase.label'
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import DHActionConfig from '../../applications/sheets-configs/action-config.mjs';
|
import DHActionConfig from '../../applications/sheets-configs/action-config.mjs';
|
||||||
import { itemAbleRollParse } from '../../helpers/utils.mjs';
|
import { itemAbleRollParse } from '../../helpers/utils.mjs';
|
||||||
import MappingField from './mappingField.mjs';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specialized collection type for stored actions.
|
* Specialized collection type for stored actions.
|
||||||
|
|
@ -11,9 +10,9 @@ export class ActionCollection extends Collection {
|
||||||
constructor(model, entries) {
|
constructor(model, entries) {
|
||||||
super();
|
super();
|
||||||
this.#model = model;
|
this.#model = model;
|
||||||
for (const entry of entries) {
|
for (const [key, value] of entries) {
|
||||||
if (!(entry instanceof game.system.api.models.actions.actionsTypes.base)) continue;
|
if (!(value instanceof game.system.api.models.actions.actionsTypes.base)) continue;
|
||||||
this.set(entry._id, entry);
|
this.set(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -61,7 +60,7 @@ export class ActionCollection extends Collection {
|
||||||
/**
|
/**
|
||||||
* Field that stores actions.
|
* Field that stores actions.
|
||||||
*/
|
*/
|
||||||
export class ActionsField extends MappingField {
|
export class ActionsField extends foundry.data.fields.TypedObjectField {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
super(new ActionField(), options);
|
super(new ActionField(), options);
|
||||||
}
|
}
|
||||||
|
|
@ -70,7 +69,7 @@ export class ActionsField extends MappingField {
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
initialize(value, model, options) {
|
initialize(value, model, options) {
|
||||||
const actions = Object.values(super.initialize(value, model, options));
|
const actions = Object.entries(super.initialize(value, model, options));
|
||||||
return new ActionCollection(model, actions);
|
return new ActionCollection(model, actions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -88,10 +87,10 @@ export class ActionField extends foundry.data.fields.ObjectField {
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
_cleanType(value, options) {
|
_cleanType(value, options, _state) {
|
||||||
if (!(typeof value === 'object')) value = {};
|
if (!(typeof value === 'object')) value = {};
|
||||||
const cls = this.getModel(value);
|
const cls = this.getModel(value);
|
||||||
if (cls) return cls.cleanData(value, options);
|
if (cls) return cls.cleanData(value, options, _state);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,9 +110,17 @@ export class ActionField extends foundry.data.fields.ObjectField {
|
||||||
* @param {object} sourceData Candidate source data of the root model.
|
* @param {object} sourceData Candidate source data of the root model.
|
||||||
* @param {any} fieldData The value of this field within the source data.
|
* @param {any} fieldData The value of this field within the source data.
|
||||||
*/
|
*/
|
||||||
migrateSource(sourceData, fieldData) {
|
_migrate(sourceData, _fieldData) {
|
||||||
const cls = this.getModel(fieldData);
|
const source = sourceData ?? this.options.initial;
|
||||||
if (cls) cls.migrateDataSafe(fieldData);
|
if (!source) return sourceData;
|
||||||
|
|
||||||
|
const cls = this.getModel(source);
|
||||||
|
if (cls) {
|
||||||
|
cls.migrateDataSafe(source);
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sourceData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -237,11 +244,11 @@ export function ActionMixin(Base) {
|
||||||
: foundry.utils.getProperty(result, basePath);
|
: foundry.utils.getProperty(result, basePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete() {
|
async delete() {
|
||||||
if (!this.inCollection) return this.item;
|
if (!this.inCollection) return this.item;
|
||||||
const action = foundry.utils.getProperty(this.item, `system.${this.systemPath}`)?.get(this.id);
|
const action = foundry.utils.getProperty(this.item, `system.${this.systemPath}`)?.get(this.id);
|
||||||
if (!action) return this.item;
|
if (!action) return this.item;
|
||||||
this.item.update({ [`system.${this.systemPath}.-=${this.id}`]: null });
|
await this.item.update({ [`system.${this.systemPath}.${this.id}`]: _del }); // Does not work. Unsure why. It worked in v13 <_<'
|
||||||
this.constructor._sheets.get(this.uuid)?.close();
|
this.constructor._sheets.get(this.uuid)?.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,8 +52,8 @@ class ResourcesField extends fields.TypedObjectField {
|
||||||
return key in CONFIG.DH.RESOURCE[this.actorType].all;
|
return key in CONFIG.DH.RESOURCE[this.actorType].all;
|
||||||
}
|
}
|
||||||
|
|
||||||
_cleanType(value, options) {
|
_cleanType(value, options, _state) {
|
||||||
value = super._cleanType(value, options);
|
value = super._cleanType(value, options, _state);
|
||||||
|
|
||||||
// If not partial, ensure all data exists
|
// If not partial, ensure all data exists
|
||||||
if (!options.partial) {
|
if (!options.partial) {
|
||||||
|
|
@ -78,10 +78,28 @@ class ResourcesField extends fields.TypedObjectField {
|
||||||
const resource = resources[key];
|
const resource = resources[key];
|
||||||
value.label = resource.label;
|
value.label = resource.label;
|
||||||
value.isReversed = resources[key].reverse;
|
value.isReversed = resources[key].reverse;
|
||||||
value.max = typeof resource.max === 'number' ? value.max ?? resource.max : null;
|
value.max = typeof resource.max === 'number' ? (value.max ?? resource.max) : null;
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Foundry bar attributes are unable to handle finding the schema field nor the label normally.
|
||||||
|
* This returns the element if its a valid resource key and overwrites the element's label for that retrieval.
|
||||||
|
*/
|
||||||
|
_getField(path) {
|
||||||
|
if (path.length === 0) return this;
|
||||||
|
const first = path.shift();
|
||||||
|
if (first === this.element.name) return this.element_getField(path);
|
||||||
|
|
||||||
|
const resources = CONFIG.DH.RESOURCE[this.actorType].all;
|
||||||
|
if (first in resources) {
|
||||||
|
this.element.label = resources[first].label;
|
||||||
|
return this.element._getField(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { attributeField, ResourcesField, stressDamageReductionRule, bonusField };
|
export { attributeField, ResourcesField, stressDamageReductionRule, bonusField };
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ export default class ForeignDocumentUUIDArrayField extends foundry.data.fields.A
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
initialize(value, model, options = {}) {
|
initialize(value, model, options = {}) {
|
||||||
const v = super.initialize(value, model, options);
|
const v = super.initialize(value ?? [], model, options);
|
||||||
return () => {
|
return () => {
|
||||||
const data = v.map(entry => (typeof entry === 'function' ? entry() : entry));
|
const data = v.map(entry => (typeof entry === 'function' ? entry() : entry));
|
||||||
return this.options.prune ? data.filter(d => !!d) : data;
|
return this.options.prune ? data.filter(d => !!d) : data;
|
||||||
|
|
|
||||||
32
module/data/fields/iterableTypedObjectField.mjs
Normal file
32
module/data/fields/iterableTypedObjectField.mjs
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
export default class IterableTypedObjectField extends foundry.data.fields.TypedObjectField {
|
||||||
|
constructor(model, options = { collectionClass: foundry.utils.Collection }, context = {}) {
|
||||||
|
super(new foundry.data.fields.EmbeddedDataField(model), options, context);
|
||||||
|
this.#elementClass = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elementClass;
|
||||||
|
|
||||||
|
/** Initializes an object with an iterator. This modifies the prototype instead of */
|
||||||
|
initialize(values) {
|
||||||
|
const object = Object.create(IterableObjectPrototype);
|
||||||
|
for (const [key, value] of Object.entries(values)) {
|
||||||
|
object[key] = new this.#elementClass(value);
|
||||||
|
}
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The prototype of an iterable object.
|
||||||
|
* This allows the functionality of a class but also allows foundry.utils.getType() to return "Object" instead of "Unknown".
|
||||||
|
*/
|
||||||
|
const IterableObjectPrototype = {
|
||||||
|
[Symbol.iterator]: function* () {
|
||||||
|
for (const value of Object.values(this)) {
|
||||||
|
yield value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
map: function (func) {
|
||||||
|
return Array.from(this, func);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1,128 +0,0 @@
|
||||||
/**
|
|
||||||
* A subclass of ObjectField that represents a mapping of keys to the provided DataField type.
|
|
||||||
*
|
|
||||||
* @param {DataField} model The class of DataField which should be embedded in this field.
|
|
||||||
* @param {MappingFieldOptions} [options={}] Options which configure the behavior of the field.
|
|
||||||
* @property {string[]} [initialKeys] Keys that will be created if no data is provided.
|
|
||||||
* @property {MappingFieldInitialValueBuilder} [initialValue] Function to calculate the initial value for a key.
|
|
||||||
* @property {boolean} [initialKeysOnly=false] Should the keys in the initialized data be limited to the keys provided
|
|
||||||
* by `options.initialKeys`?
|
|
||||||
*/
|
|
||||||
export default class MappingField extends foundry.data.fields.ObjectField {
|
|
||||||
constructor(model, options) {
|
|
||||||
if (!(model instanceof foundry.data.fields.DataField)) {
|
|
||||||
throw new Error('MappingField must have a DataField as its contained element');
|
|
||||||
}
|
|
||||||
super(options);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The embedded DataField definition which is contained in this field.
|
|
||||||
* @type {DataField}
|
|
||||||
*/
|
|
||||||
this.model = model;
|
|
||||||
model.parent = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/** @inheritDoc */
|
|
||||||
static get _defaults() {
|
|
||||||
return foundry.utils.mergeObject(super._defaults, {
|
|
||||||
initialKeys: null,
|
|
||||||
initialValue: null,
|
|
||||||
initialKeysOnly: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/** @inheritDoc */
|
|
||||||
_cleanType(value, options) {
|
|
||||||
Object.entries(value).forEach(([k, v]) => {
|
|
||||||
if (k.startsWith('-=')) return;
|
|
||||||
value[k] = this.model.clean(v, options);
|
|
||||||
});
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/** @inheritDoc */
|
|
||||||
getInitialValue(data) {
|
|
||||||
let keys = this.initialKeys;
|
|
||||||
const initial = super.getInitialValue(data);
|
|
||||||
if (!keys || !foundry.utils.isEmpty(initial)) return initial;
|
|
||||||
if (!(keys instanceof Array)) keys = Object.keys(keys);
|
|
||||||
for (const key of keys) initial[key] = this._getInitialValueForKey(key);
|
|
||||||
return initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the initial value for the provided key.
|
|
||||||
* @param {string} key Key within the object being built.
|
|
||||||
* @param {object} [object] Any existing mapping data.
|
|
||||||
* @returns {*} Initial value based on provided field type.
|
|
||||||
*/
|
|
||||||
_getInitialValueForKey(key, object) {
|
|
||||||
const initial = this.model.getInitialValue();
|
|
||||||
return this.initialValue?.(key, initial, object) ?? initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
_validateType(value, options = {}) {
|
|
||||||
if (foundry.utils.getType(value) !== 'Object') throw new Error('must be an Object');
|
|
||||||
const errors = this._validateValues(value, options);
|
|
||||||
if (!foundry.utils.isEmpty(errors)) {
|
|
||||||
const failure = new foundry.data.validation.DataModelValidationFailure();
|
|
||||||
failure.elements = Object.entries(errors).map(([id, failure]) => ({ id, failure }));
|
|
||||||
throw failure.asError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate each value of the object.
|
|
||||||
* @param {object} value The object to validate.
|
|
||||||
* @param {object} options Validation options.
|
|
||||||
* @returns {Record<string, Error>} An object of value-specific errors by key.
|
|
||||||
*/
|
|
||||||
_validateValues(value, options) {
|
|
||||||
const errors = {};
|
|
||||||
for (const [k, v] of Object.entries(value)) {
|
|
||||||
if (k.startsWith('-=')) continue;
|
|
||||||
const error = this.model.validate(v, options);
|
|
||||||
if (error) errors[k] = error;
|
|
||||||
}
|
|
||||||
return errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
initialize(value, model, options = {}) {
|
|
||||||
if (!value) return value;
|
|
||||||
const obj = {};
|
|
||||||
const initialKeys = this.initialKeys instanceof Array ? this.initialKeys : Object.keys(this.initialKeys ?? {});
|
|
||||||
const keys = this.initialKeysOnly ? initialKeys : Object.keys(value);
|
|
||||||
for (const key of keys) {
|
|
||||||
const data = value[key] ?? this._getInitialValueForKey(key, value);
|
|
||||||
obj[key] = this.model.initialize(data, model, options);
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/** @inheritDoc */
|
|
||||||
_getField(path) {
|
|
||||||
if (path.length === 0) return this;
|
|
||||||
else if (path.length === 1) return this.model;
|
|
||||||
path.shift();
|
|
||||||
return this.model._getField(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -84,7 +84,7 @@ export default class DHArmor extends AttachableItem {
|
||||||
}
|
}
|
||||||
await this.parent.deleteEmbeddedDocuments('ActiveEffect', effectIds);
|
await this.parent.deleteEmbeddedDocuments('ActiveEffect', effectIds);
|
||||||
changes.system.actions = actionIds.reduce((acc, id) => {
|
changes.system.actions = actionIds.reduce((acc, id) => {
|
||||||
acc[`-=${id}`] = null;
|
acc[id] = _del;
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -230,9 +230,9 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
||||||
|
|
||||||
if (changed.system?.actions) {
|
if (changed.system?.actions) {
|
||||||
const triggersToRemove = Object.keys(changed.system.actions).reduce((acc, key) => {
|
const triggersToRemove = Object.keys(changed.system.actions).reduce((acc, key) => {
|
||||||
if (!changed.system.actions[key]) {
|
const action = changed.system.actions[key];
|
||||||
const strippedKey = key.replace('-=', '');
|
if (action && Object.keys(action).length === 0) {
|
||||||
acc.push(...this.actions.get(strippedKey).triggers.map(x => x.trigger));
|
acc.push(...this.actions.get(key).triggers.map(x => x.trigger));
|
||||||
}
|
}
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
|
|
|
||||||
|
|
@ -101,12 +101,13 @@ export default class DHBeastform extends BaseDataItem {
|
||||||
const effect = this.parent.effects.find(x => x.type === 'beastform');
|
const effect = this.parent.effects.find(x => x.type === 'beastform');
|
||||||
if (!effect) return null;
|
if (!effect) return null;
|
||||||
|
|
||||||
const traitBonus = effect.changes.find(x => x.key === `system.traits.${this.mainTrait}.value`)?.value ?? 0;
|
const traitBonus =
|
||||||
const evasionBonus = effect.changes.find(x => x.key === 'system.evasion')?.value ?? 0;
|
effect.system.changes.find(x => x.key === `system.traits.${this.mainTrait}.value`)?.value ?? 0;
|
||||||
|
const evasionBonus = effect.system.changes.find(x => x.key === 'system.evasion')?.value ?? 0;
|
||||||
|
|
||||||
const damageDiceIndex = effect.changes.find(x => x.key === 'system.rules.attack.damage.diceIndex');
|
const damageDiceIndex = effect.system.changes.find(x => x.key === 'system.rules.attack.damage.diceIndex');
|
||||||
const damageDice = damageDiceIndex ? Object.keys(CONFIG.DH.GENERAL.diceTypes)[damageDiceIndex.value] : null;
|
const damageDice = damageDiceIndex ? Object.keys(CONFIG.DH.GENERAL.diceTypes)[damageDiceIndex.value] : null;
|
||||||
const damageBonus = effect.changes.find(x => x.key === 'system.rules.attack.damage.bonus')?.value ?? 0;
|
const damageBonus = effect.system.changes.find(x => x.key === 'system.rules.attack.damage.bonus')?.value ?? 0;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
trait: game.i18n.localize(CONFIG.DH.ACTOR.abilities[this.mainTrait].label),
|
trait: game.i18n.localize(CONFIG.DH.ACTOR.abilities[this.mainTrait].label),
|
||||||
|
|
@ -169,8 +170,9 @@ export default class DHBeastform extends BaseDataItem {
|
||||||
|
|
||||||
const beastformEffect = this.parent.effects.find(x => x.type === 'beastform');
|
const beastformEffect = this.parent.effects.find(x => x.type === 'beastform');
|
||||||
await beastformEffect.updateSource({
|
await beastformEffect.updateSource({
|
||||||
|
system: {
|
||||||
changes: [
|
changes: [
|
||||||
...beastformEffect.changes,
|
...beastformEffect.system.changes,
|
||||||
{
|
{
|
||||||
key: 'system.advantageSources',
|
key: 'system.advantageSources',
|
||||||
mode: 2,
|
mode: 2,
|
||||||
|
|
@ -179,7 +181,6 @@ export default class DHBeastform extends BaseDataItem {
|
||||||
.join(', ')
|
.join(', ')
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
system: {
|
|
||||||
characterTokenData: {
|
characterTokenData: {
|
||||||
usesDynamicToken: this.parent.parent.prototypeToken.ring.enabled,
|
usesDynamicToken: this.parent.parent.prototypeToken.ring.enabled,
|
||||||
tokenImg: this.parent.parent.prototypeToken.texture.src,
|
tokenImg: this.parent.parent.prototypeToken.texture.src,
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,9 @@ export default class DHSubclass extends BaseDataItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
async _preCreate(data, options, user) {
|
async _preCreate(data, options, user) {
|
||||||
|
const allowed = await super._preCreate(data, options, user);
|
||||||
|
if (allowed === false) return;
|
||||||
|
|
||||||
if (this.actor?.type === 'character') {
|
if (this.actor?.type === 'character') {
|
||||||
const dataUuid = data.uuid ?? data._stats.compendiumSource ?? `Item.${data._id}`;
|
const dataUuid = data.uuid ?? data._stats.compendiumSource ?? `Item.${data._id}`;
|
||||||
if (this.actor.system.class.subclass) {
|
if (this.actor.system.class.subclass) {
|
||||||
|
|
@ -86,9 +89,6 @@ export default class DHSubclass extends BaseDataItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const allowed = await super._preCreate(data, options, user);
|
|
||||||
if (allowed === false) return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**@inheritdoc */
|
/**@inheritdoc */
|
||||||
|
|
|
||||||
|
|
@ -63,15 +63,15 @@ export default class DHWeapon extends AttachableItem {
|
||||||
type: 'attack'
|
type: 'attack'
|
||||||
},
|
},
|
||||||
damage: {
|
damage: {
|
||||||
parts: [
|
parts: {
|
||||||
{
|
hitPoints: {
|
||||||
type: ['physical'],
|
type: ['physical'],
|
||||||
value: {
|
value: {
|
||||||
multiplier: 'prof',
|
multiplier: 'prof',
|
||||||
dice: 'd8'
|
dice: 'd8'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
@ -148,7 +148,7 @@ export default class DHWeapon extends AttachableItem {
|
||||||
|
|
||||||
await this.parent.deleteEmbeddedDocuments('ActiveEffect', removedEffectsUpdate);
|
await this.parent.deleteEmbeddedDocuments('ActiveEffect', removedEffectsUpdate);
|
||||||
changes.system.actions = removedActionsUpdate.reduce((acc, id) => {
|
changes.system.actions = removedActionsUpdate.reduce((acc, id) => {
|
||||||
acc[`-=${id}`] = null;
|
acc[id] = _del;
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,16 @@
|
||||||
export default class DhAppearance extends foundry.abstract.DataModel {
|
export default class DhAppearance extends foundry.abstract.DataModel {
|
||||||
static LOCALIZATION_PREFIXES = ['DAGGERHEART.SETTINGS.Appearance'];
|
static LOCALIZATION_PREFIXES = ['DAGGERHEART.SETTINGS.Appearance'];
|
||||||
|
|
||||||
|
static sfxSchema = () =>
|
||||||
|
new foundry.data.fields.SchemaField({
|
||||||
|
class: new foundry.data.fields.StringField({
|
||||||
|
nullable: true,
|
||||||
|
initial: null,
|
||||||
|
blank: true,
|
||||||
|
choices: CONFIG.DH.GENERAL.diceSoNiceSFXClasses
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
const { StringField, ColorField, BooleanField, SchemaField } = foundry.data.fields;
|
const { StringField, ColorField, BooleanField, SchemaField } = foundry.data.fields;
|
||||||
|
|
||||||
|
|
@ -15,7 +25,10 @@ export default class DhAppearance extends foundry.abstract.DataModel {
|
||||||
colorset: new StringField({ initial: 'inspired', required: true, blank: false }),
|
colorset: new StringField({ initial: 'inspired', required: true, blank: false }),
|
||||||
material: new StringField({ initial: 'metal', required: true, blank: false }),
|
material: new StringField({ initial: 'metal', required: true, blank: false }),
|
||||||
system: new StringField({ initial: 'standard', required: true, blank: false }),
|
system: new StringField({ initial: 'standard', required: true, blank: false }),
|
||||||
font: new StringField({ initial: 'auto', required: true, blank: false })
|
font: new StringField({ initial: 'auto', required: true, blank: false }),
|
||||||
|
sfx: new SchemaField({
|
||||||
|
higher: DhAppearance.sfxSchema()
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -30,7 +43,10 @@ export default class DhAppearance extends foundry.abstract.DataModel {
|
||||||
hope: diceStyle({ fg: '#ffffff', bg: '#ffe760', outline: '#000000', edge: '#ffffff' }),
|
hope: diceStyle({ fg: '#ffffff', bg: '#ffe760', outline: '#000000', edge: '#ffffff' }),
|
||||||
fear: diceStyle({ fg: '#000000', bg: '#0032b1', outline: '#ffffff', edge: '#000000' }),
|
fear: diceStyle({ fg: '#000000', bg: '#0032b1', outline: '#ffffff', edge: '#000000' }),
|
||||||
advantage: diceStyle({ fg: '#ffffff', bg: '#008000', outline: '#000000', edge: '#ffffff' }),
|
advantage: diceStyle({ fg: '#ffffff', bg: '#008000', outline: '#000000', edge: '#ffffff' }),
|
||||||
disadvantage: diceStyle({ fg: '#000000', bg: '#b30000', outline: '#ffffff', edge: '#000000' })
|
disadvantage: diceStyle({ fg: '#000000', bg: '#b30000', outline: '#ffffff', edge: '#000000' }),
|
||||||
|
sfx: new SchemaField({
|
||||||
|
critical: DhAppearance.sfxSchema()
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
extendCharacterDescriptions: new BooleanField(),
|
extendCharacterDescriptions: new BooleanField(),
|
||||||
extendAdversaryDescriptions: new BooleanField(),
|
extendAdversaryDescriptions: new BooleanField(),
|
||||||
|
|
@ -65,4 +81,48 @@ export default class DhAppearance extends foundry.abstract.DataModel {
|
||||||
showGenericStatusEffects: new BooleanField({ initial: true })
|
showGenericStatusEffects: new BooleanField({ initial: true })
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get diceSoNiceData() {
|
||||||
|
const globalOverrides = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.GlobalOverrides);
|
||||||
|
const getSFX = (baseClientData, overrideKey) => {
|
||||||
|
if (!globalOverrides.diceSoNice.sfx.overrideEnabled) return baseClientData;
|
||||||
|
const overrideData = globalOverrides.diceSoNice.sfx[overrideKey];
|
||||||
|
const clientData = foundry.utils.deepClone(baseClientData);
|
||||||
|
return Object.keys(clientData).reduce((acc, key) => {
|
||||||
|
const data = clientData[key];
|
||||||
|
acc[key] = Object.keys(data).reduce((acc, dataKey) => {
|
||||||
|
const value = data[dataKey];
|
||||||
|
acc[dataKey] = value ? value : overrideData[key][dataKey];
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
...this.diceSoNice,
|
||||||
|
sfx: getSFX(this.diceSoNice.sfx, 'global'),
|
||||||
|
hope: {
|
||||||
|
...this.diceSoNice.hope,
|
||||||
|
sfx: getSFX(this.diceSoNice.hope.sfx, 'hope')
|
||||||
|
},
|
||||||
|
fear: {
|
||||||
|
...this.diceSoNice.fear,
|
||||||
|
sfx: getSFX(this.diceSoNice.fear.sfx, 'fear')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Invoked by the setting when data changes */
|
||||||
|
handleChange() {
|
||||||
|
if (this.displayFear) {
|
||||||
|
if (ui.resources) {
|
||||||
|
if (this.displayFear === 'hide') ui.resources.close({ allowed: true });
|
||||||
|
else ui.resources.render({ force: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const globalOverrides = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.GlobalOverrides);
|
||||||
|
globalOverrides.diceSoNiceSFXUpdate(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -196,6 +196,11 @@ export default class DhAutomation extends foundry.abstract.DataModel {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
|
autoExpireActiveEffects: new fields.BooleanField({
|
||||||
|
required: true,
|
||||||
|
initial: true,
|
||||||
|
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.autoExpireActiveEffects.label'
|
||||||
|
}),
|
||||||
triggers: new fields.SchemaField({
|
triggers: new fields.SchemaField({
|
||||||
enabled: new fields.BooleanField({
|
enabled: new fields.BooleanField({
|
||||||
nullable: false,
|
nullable: false,
|
||||||
|
|
|
||||||
55
module/data/settings/GlobalOverrides.mjs
Normal file
55
module/data/settings/GlobalOverrides.mjs
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
import DhAppearance from './Appearance.mjs';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A setting to handle cases where we want to allow the GM to set a global default for client settings.
|
||||||
|
*/
|
||||||
|
export default class DhGlobalOverrides extends foundry.abstract.DataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
return {
|
||||||
|
diceSoNice: new fields.SchemaField({
|
||||||
|
sfx: new fields.SchemaField({
|
||||||
|
overrideEnabled: new fields.BooleanField(),
|
||||||
|
global: new fields.SchemaField({
|
||||||
|
critical: DhAppearance.sfxSchema()
|
||||||
|
}),
|
||||||
|
hope: new fields.SchemaField({
|
||||||
|
higher: DhAppearance.sfxSchema()
|
||||||
|
}),
|
||||||
|
fear: new fields.SchemaField({
|
||||||
|
higher: DhAppearance.sfxSchema()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async diceSoNiceSFXUpdate(appearanceSettings, enabled) {
|
||||||
|
if (!game.user.isGM) return;
|
||||||
|
|
||||||
|
const newEnabled = enabled !== undefined ? enabled : this.diceSoNice.sfx.overrideEnabled;
|
||||||
|
if (newEnabled) {
|
||||||
|
const newOverrides = foundry.utils.mergeObject(this.toObject(), {
|
||||||
|
diceSoNice: {
|
||||||
|
sfx: {
|
||||||
|
overrideEnabled: true,
|
||||||
|
global: appearanceSettings.diceSoNice.sfx,
|
||||||
|
hope: appearanceSettings.diceSoNice.hope.sfx,
|
||||||
|
fear: appearanceSettings.diceSoNice.fear.sfx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.GlobalOverrides, newOverrides);
|
||||||
|
} else {
|
||||||
|
const newOverrides = {
|
||||||
|
...this.toObject(),
|
||||||
|
diceSoNice: {
|
||||||
|
sfx: {
|
||||||
|
overrideEnabled: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.GlobalOverrides, newOverrides);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -220,7 +220,7 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
|
||||||
return result;
|
return result;
|
||||||
}, {}),
|
}, {}),
|
||||||
...config.custom,
|
...config.custom,
|
||||||
...config.base,
|
...config.base
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,3 +3,4 @@ export { default as DhAutomation } from './Automation.mjs';
|
||||||
export { default as DhHomebrew } from './Homebrew.mjs';
|
export { default as DhHomebrew } from './Homebrew.mjs';
|
||||||
export { default as DhMetagaming } from './Metagaming.mjs';
|
export { default as DhMetagaming } from './Metagaming.mjs';
|
||||||
export { default as DhVariantRules } from './VariantRules.mjs';
|
export { default as DhVariantRules } from './VariantRules.mjs';
|
||||||
|
export { default as DhGlobalOverrides } from './GlobalOverrides.mjs';
|
||||||
|
|
|
||||||
|
|
@ -197,7 +197,7 @@ export default class DamageRoll extends DHRoll {
|
||||||
if (config.data.parent.appliedEffects) {
|
if (config.data.parent.appliedEffects) {
|
||||||
// Bardic Rally
|
// Bardic Rally
|
||||||
const rallyChoices = config.data?.parent?.appliedEffects.reduce((a, c) => {
|
const rallyChoices = config.data?.parent?.appliedEffects.reduce((a, c) => {
|
||||||
const change = c.changes.find(ch => ch.key === 'system.bonuses.rally');
|
const change = c.system.changes.find(ch => ch.key === 'system.bonuses.rally');
|
||||||
if (change) a.push({ value: c.id, label: parseRallyDice(change.value, c) });
|
if (change) a.push({ value: c.id, label: parseRallyDice(change.value, c) });
|
||||||
return a;
|
return a;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
|
||||||
|
|
@ -140,7 +140,11 @@ export default class DHRoll extends Roll {
|
||||||
|
|
||||||
const metagamingSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Metagaming);
|
const metagamingSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Metagaming);
|
||||||
const chatData = await this._prepareChatRenderContext({ flavor, isPrivate, ...options });
|
const chatData = await this._prepareChatRenderContext({ flavor, isPrivate, ...options });
|
||||||
return foundry.applications.handlebars.renderTemplate(template, { ...chatData, metagamingSettings });
|
return foundry.applications.handlebars.renderTemplate(template, {
|
||||||
|
...chatData,
|
||||||
|
parent: chatData.parent,
|
||||||
|
metagamingSettings
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
|
|
@ -261,12 +265,12 @@ export default class DHRoll extends Roll {
|
||||||
const changeKeys = this.getActionChangeKeys();
|
const changeKeys = this.getActionChangeKeys();
|
||||||
return (
|
return (
|
||||||
this.options.effects?.reduce((acc, effect) => {
|
this.options.effects?.reduce((acc, effect) => {
|
||||||
if (effect.changes.some(x => changeKeys.some(key => x.key.includes(key)))) {
|
if (effect.system.changes.some(x => changeKeys.some(key => x.key.includes(key)))) {
|
||||||
acc[effect.id] = {
|
acc[effect.id] = {
|
||||||
id: effect.id,
|
id: effect.id,
|
||||||
name: effect.name,
|
name: effect.name,
|
||||||
description: effect.description,
|
description: effect.description,
|
||||||
changes: effect.changes,
|
changes: effect.system.changes,
|
||||||
origEffect: effect,
|
origEffect: effect,
|
||||||
selected: !effect.disabled
|
selected: !effect.disabled
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ export default class DualityRoll extends D20Roll {
|
||||||
|
|
||||||
setRallyChoices() {
|
setRallyChoices() {
|
||||||
return this.data?.parent?.appliedEffects.reduce((a, c) => {
|
return this.data?.parent?.appliedEffects.reduce((a, c) => {
|
||||||
const change = c.changes.find(ch => ch.key === 'system.bonuses.rally');
|
const change = c.system.changes.find(ch => ch.key === 'system.bonuses.rally');
|
||||||
if (change) a.push({ value: c.id, label: parseRallyDice(change.value, c) });
|
if (change) a.push({ value: c.id, label: parseRallyDice(change.value, c) });
|
||||||
return a;
|
return a;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
@ -179,7 +179,7 @@ export default class DualityRoll extends D20Roll {
|
||||||
static async buildConfigure(config = {}, message = {}) {
|
static async buildConfigure(config = {}, message = {}) {
|
||||||
config.dialog ??= {};
|
config.dialog ??= {};
|
||||||
config.guaranteedCritical = config.data?.parent?.appliedEffects.reduce((a, c) => {
|
config.guaranteedCritical = config.data?.parent?.appliedEffects.reduce((a, c) => {
|
||||||
const change = c.changes.find(ch => ch.key === 'system.rules.roll.guaranteedCritical');
|
const change = c.system.changes.find(ch => ch.key === 'system.rules.roll.guaranteedCritical');
|
||||||
if (change) a = true;
|
if (change) a = true;
|
||||||
return a;
|
return a;
|
||||||
}, false);
|
}, false);
|
||||||
|
|
@ -378,6 +378,8 @@ export default class DualityRoll extends D20Roll {
|
||||||
let parsedRoll = game.system.api.dice.DualityRoll.fromData({ ...rollBase, evaluated: false });
|
let parsedRoll = game.system.api.dice.DualityRoll.fromData({ ...rollBase, evaluated: false });
|
||||||
const term = parsedRoll.terms[dieIndex];
|
const term = parsedRoll.terms[dieIndex];
|
||||||
await term.reroll(`/r1=${term.total}`);
|
await term.reroll(`/r1=${term.total}`);
|
||||||
|
const result = await parsedRoll.evaluate();
|
||||||
|
|
||||||
if (game.modules.get('dice-so-nice')?.active) {
|
if (game.modules.get('dice-so-nice')?.active) {
|
||||||
const diceSoNiceRoll = {
|
const diceSoNiceRoll = {
|
||||||
_evaluated: true,
|
_evaluated: true,
|
||||||
|
|
@ -401,8 +403,6 @@ export default class DualityRoll extends D20Roll {
|
||||||
foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice });
|
foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice });
|
||||||
}
|
}
|
||||||
|
|
||||||
await parsedRoll.evaluate();
|
|
||||||
|
|
||||||
const newRoll = game.system.api.dice.DualityRoll.postEvaluate(parsedRoll, {
|
const newRoll = game.system.api.dice.DualityRoll.postEvaluate(parsedRoll, {
|
||||||
targets: parsedRoll.options.targets ?? [],
|
targets: parsedRoll.options.targets ?? [],
|
||||||
roll: {
|
roll: {
|
||||||
|
|
|
||||||
|
|
@ -8,5 +8,4 @@ export { default as DhRollTable } from './rollTable.mjs';
|
||||||
export { default as DhScene } from './scene.mjs';
|
export { default as DhScene } from './scene.mjs';
|
||||||
export { default as DhToken } from './token.mjs';
|
export { default as DhToken } from './token.mjs';
|
||||||
export { default as DhTooltipManager } from './tooltipManager.mjs';
|
export { default as DhTooltipManager } from './tooltipManager.mjs';
|
||||||
export { default as DhTemplateManager } from './templateManager.mjs';
|
|
||||||
export { default as DhTokenManager } from './tokenManager.mjs';
|
export { default as DhTokenManager } from './tokenManager.mjs';
|
||||||
|
|
|
||||||
|
|
@ -50,10 +50,55 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this Active Effect is eligible to be registered with the {@link ActiveEffectRegistry}
|
||||||
|
*/
|
||||||
|
get isExpiryTrackable() {
|
||||||
|
return (
|
||||||
|
this.persisted &&
|
||||||
|
!this.inCompendium &&
|
||||||
|
this.modifiesActor &&
|
||||||
|
this.start &&
|
||||||
|
this.isTemporary &&
|
||||||
|
!this.isExpired
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
/* Event Handlers */
|
/* Event Handlers */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
static async createDialog(data = {}, createOptions = {}, options = {}) {
|
||||||
|
const { folders, types, template, context = {}, ...dialogOptions } = options;
|
||||||
|
|
||||||
|
if (types?.length === 0) {
|
||||||
|
throw new Error('The array of sub-types to restrict to must not be empty.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const creatableEffects = ['base'];
|
||||||
|
const documentTypes = this.TYPES.filter(type => creatableEffects.includes(type)).map(type => {
|
||||||
|
const labelKey = `TYPES.ActiveEffect.${type}`;
|
||||||
|
const label = game.i18n.has(labelKey) ? game.i18n.localize(labelKey) : type;
|
||||||
|
|
||||||
|
return { value: type, label };
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!documentTypes.length) {
|
||||||
|
throw new Error('No document types were permitted to be created.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const sortedTypes = documentTypes.sort((a, b) => a.label.localeCompare(b.label, game.i18n.lang));
|
||||||
|
|
||||||
|
return await super.createDialog(data, createOptions, {
|
||||||
|
folders,
|
||||||
|
types,
|
||||||
|
template,
|
||||||
|
context: { types: sortedTypes, ...context },
|
||||||
|
...dialogOptions
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**@inheritdoc*/
|
/**@inheritdoc*/
|
||||||
async _preCreate(data, options, user) {
|
async _preCreate(data, options, user) {
|
||||||
const update = {};
|
const update = {};
|
||||||
|
|
@ -109,9 +154,11 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
/**@inheritdoc*/
|
/**@inheritdoc*/
|
||||||
static applyField(model, change, field) {
|
static applyChangeField(model, change, field) {
|
||||||
change.value = DhActiveEffect.getChangeValue(model, change, change.effect);
|
change.value = Number.isNumeric(change.value)
|
||||||
super.applyField(model, change, field);
|
? change.value
|
||||||
|
: DhActiveEffect.getChangeValue(model, change, change.effect);
|
||||||
|
super.applyChangeField(model, change, field);
|
||||||
}
|
}
|
||||||
|
|
||||||
_applyLegacy(actor, change, changes) {
|
_applyLegacy(actor, change, changes) {
|
||||||
|
|
@ -119,13 +166,12 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
|
||||||
super._applyLegacy(actor, change, changes);
|
super._applyLegacy(actor, change, changes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
|
||||||
static getChangeValue(model, change, effect) {
|
static getChangeValue(model, change, effect) {
|
||||||
let value = change.value;
|
let key = change.value.toString();
|
||||||
const isOriginTarget = value.toLowerCase().includes('origin.@');
|
const isOriginTarget = key.toLowerCase().includes('origin.@');
|
||||||
let parseModel = model;
|
let parseModel = model;
|
||||||
if (isOriginTarget && effect.origin) {
|
if (isOriginTarget && effect.origin) {
|
||||||
value = change.value.replaceAll(/origin\.@/gi, '@');
|
key = change.key.replaceAll(/origin\.@/gi, '@');
|
||||||
try {
|
try {
|
||||||
const originEffect = foundry.utils.fromUuidSync(effect.origin);
|
const originEffect = foundry.utils.fromUuidSync(effect.origin);
|
||||||
const doc =
|
const doc =
|
||||||
|
|
@ -136,8 +182,8 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const evalValue = this.effectSafeEval(itemAbleRollParse(value, parseModel, effect.parent));
|
const evalValue = this.effectSafeEval(itemAbleRollParse(key, parseModel, effect.parent));
|
||||||
return evalValue ?? value;
|
return evalValue ?? key;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,7 @@ export default class DhpActor extends Actor {
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedLevelups = Object.keys(this.system.levelData.levelups).reduce((acc, level) => {
|
const updatedLevelups = Object.keys(this.system.levelData.levelups).reduce((acc, level) => {
|
||||||
if (Number(level) > usedLevel) acc[`-=${level}`] = null;
|
if (Number(level) > usedLevel) acc[level] = _del;
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
@ -188,7 +188,7 @@ export default class DhpActor extends Actor {
|
||||||
if (experiences.length > 0) {
|
if (experiences.length > 0) {
|
||||||
const getUpdate = () => ({
|
const getUpdate = () => ({
|
||||||
'system.experiences': experiences.reduce((acc, key) => {
|
'system.experiences': experiences.reduce((acc, key) => {
|
||||||
acc[`-=${key}`] = null;
|
acc[key] = _del;
|
||||||
return acc;
|
return acc;
|
||||||
}, {})
|
}, {})
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,105 +0,0 @@
|
||||||
/**
|
|
||||||
* A singleton class that handles preview templates.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default class DhTemplateManager {
|
|
||||||
#activePreview;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a template preview, deactivating any existing ones.
|
|
||||||
* @param {object} data
|
|
||||||
*/
|
|
||||||
async createPreview(data) {
|
|
||||||
const template = await canvas.templates._createPreview(data, { renderSheet: false });
|
|
||||||
|
|
||||||
this.#activePreview = {
|
|
||||||
document: template.document,
|
|
||||||
object: template,
|
|
||||||
origin: { x: template.document.x, y: template.document.y }
|
|
||||||
};
|
|
||||||
|
|
||||||
this.#activePreview.events = {
|
|
||||||
contextmenu: this.#cancelTemplate.bind(this),
|
|
||||||
mousedown: this.#confirmTemplate.bind(this),
|
|
||||||
mousemove: this.#onDragMouseMove.bind(this),
|
|
||||||
wheel: this.#onMouseWheel.bind(this)
|
|
||||||
};
|
|
||||||
canvas.stage.on('mousemove', this.#activePreview.events.mousemove);
|
|
||||||
canvas.stage.on('mousedown', this.#activePreview.events.mousedown);
|
|
||||||
|
|
||||||
canvas.app.view.addEventListener('wheel', this.#activePreview.events.wheel, true);
|
|
||||||
canvas.app.view.addEventListener('contextmenu', this.#activePreview.events.contextmenu);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the movement of the temlate preview on mousedrag.
|
|
||||||
* @param {mousemove Event} event
|
|
||||||
*/
|
|
||||||
#onDragMouseMove(event) {
|
|
||||||
event.stopPropagation();
|
|
||||||
const { moveTime, object } = this.#activePreview;
|
|
||||||
const update = {};
|
|
||||||
|
|
||||||
const now = Date.now();
|
|
||||||
if (now - (moveTime || 0) <= 16) return;
|
|
||||||
this.#activePreview.moveTime = now;
|
|
||||||
|
|
||||||
let cursor = event.getLocalPosition(canvas.templates);
|
|
||||||
|
|
||||||
Object.assign(update, canvas.grid.getCenterPoint(cursor));
|
|
||||||
|
|
||||||
object.document.updateSource(update);
|
|
||||||
object.renderFlags.set({ refresh: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the rotation of the preview template on scrolling.
|
|
||||||
* @param {wheel Event} event
|
|
||||||
*/
|
|
||||||
#onMouseWheel(event) {
|
|
||||||
if (!this.#activePreview) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!event.shiftKey && !event.ctrlKey) return;
|
|
||||||
event.stopPropagation();
|
|
||||||
event.preventDefault();
|
|
||||||
const { moveTime, object } = this.#activePreview;
|
|
||||||
|
|
||||||
const now = Date.now();
|
|
||||||
if (now - (moveTime || 0) <= 16) return;
|
|
||||||
this.#activePreview.moveTime = now;
|
|
||||||
|
|
||||||
const multiplier = event.shiftKey ? 0.2 : 0.1;
|
|
||||||
|
|
||||||
object.document.updateSource({
|
|
||||||
direction: object.document.direction + event.deltaY * multiplier
|
|
||||||
});
|
|
||||||
object.renderFlags.set({ refresh: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancels the preview template on right-click.
|
|
||||||
* @param {contextmenu Event} event
|
|
||||||
*/
|
|
||||||
#cancelTemplate(event) {
|
|
||||||
const { mousemove, mousedown, contextmenu, wheel } = this.#activePreview.events;
|
|
||||||
canvas.templates._onDragLeftCancel(event);
|
|
||||||
|
|
||||||
canvas.stage.off('mousemove', mousemove);
|
|
||||||
canvas.stage.off('mousedown', mousedown);
|
|
||||||
canvas.app.view.removeEventListener('contextmenu', contextmenu);
|
|
||||||
canvas.app.view.removeEventListener('wheel', wheel);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a real MeasuredTemplate at the preview location and cancels the preview.
|
|
||||||
* @param {click Event} event
|
|
||||||
*/
|
|
||||||
#confirmTemplate(event) {
|
|
||||||
event.stopPropagation();
|
|
||||||
this.#cancelTemplate(event);
|
|
||||||
|
|
||||||
canvas.scene.createEmbeddedDocuments('MeasuredTemplate', [this.#activePreview.document.toObject()]);
|
|
||||||
this.#activePreview = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -494,4 +494,62 @@ export default class DHToken extends CONFIG.Token.documentClass {
|
||||||
game.system.registeredTriggers.unregisterItemTriggers(this.actor.items);
|
game.system.registeredTriggers.unregisterItemTriggers(this.actor.items);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* V14 TEMP until foundry fixes: https://discord.com/channels/170995199584108546/1421197211194228907/1467296028700049566 */
|
||||||
|
_onRelatedUpdate(update = {}, operation = {}) {
|
||||||
|
this.#refreshOverrides(operation);
|
||||||
|
this._prepareBars();
|
||||||
|
|
||||||
|
// Update tracked Combat resource
|
||||||
|
const combatant = this.combatant;
|
||||||
|
if (combatant) {
|
||||||
|
const isActorUpdate = [this, null, undefined].includes(operation.parent);
|
||||||
|
const resource = game.combat.settings.resource;
|
||||||
|
const updates = Array.isArray(update) ? update : [update];
|
||||||
|
if (isActorUpdate && resource && updates.some(u => foundry.utils.hasProperty(u.system ?? {}, resource))) {
|
||||||
|
combatant.updateResource();
|
||||||
|
}
|
||||||
|
ui.combat.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger redraws on the token
|
||||||
|
if (this.parent.isView) {
|
||||||
|
if (this.object?.hasActiveHUD) canvas.tokens.hud.render();
|
||||||
|
this.object?.renderFlags.set({ redrawEffects: true });
|
||||||
|
for (const key of ['bar1', 'bar2']) {
|
||||||
|
const name = `${this.object?.objectId}.animate${key.capitalize()}`;
|
||||||
|
const easing = foundry.canvas.animation.CanvasAnimation.easeInOutCosine;
|
||||||
|
this.object?.animate({ [key]: this[key] }, { name, easing });
|
||||||
|
}
|
||||||
|
for (const app of foundry.applications.sheets.TokenConfig.instances()) {
|
||||||
|
app._preview?.updateSource({ delta: this.toObject().delta }, { diff: false, recursive: false });
|
||||||
|
app._preview?.object?.renderFlags.set({ refreshBars: true, redrawEffects: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* V14 TEMP until foundry fixes: https://discord.com/channels/170995199584108546/1421197211194228907/1467296028700049566 */
|
||||||
|
#refreshOverrides(operation) {
|
||||||
|
if (!this.actor) return;
|
||||||
|
|
||||||
|
const { deepClone, mergeObject, equals, isEmpty } = foundry.utils;
|
||||||
|
const oldOverrides = deepClone(this._overrides) ?? {};
|
||||||
|
const newOverrides = deepClone(this.actor?.tokenOverrides ?? {}, { prune: true });
|
||||||
|
if (!equals(oldOverrides, newOverrides)) {
|
||||||
|
this._overrides = newOverrides;
|
||||||
|
this.reset();
|
||||||
|
|
||||||
|
// Send emulated update data to the PlaceableObject
|
||||||
|
if (!canvas.ready || canvas.scene !== this.scene) return;
|
||||||
|
const { width, height, depth, ...changes } = mergeObject(
|
||||||
|
mergeObject(oldOverrides, this, { insertKeys: false, insertValues: false }),
|
||||||
|
this._overrides
|
||||||
|
);
|
||||||
|
this.object?._onUpdate(changes, {}, game.user.id);
|
||||||
|
|
||||||
|
// Hand off size changes to a secondary handler requiring downstream implementation.
|
||||||
|
const sizeChanges = deepClone({ width, height, depth }, { prune: true });
|
||||||
|
if (!isEmpty(sizeChanges)) this._onOverrideSize(sizeChanges, operation);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,8 @@ export default function DhTemplateEnricher(match, _options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const renderMeasuredTemplate = async event => {
|
export const renderMeasuredTemplate = async event => {
|
||||||
|
const { LINE, RECTANGLE, INFRONT, CONE } = CONFIG.DH.GENERAL.templateTypes;
|
||||||
|
|
||||||
const button = event.currentTarget,
|
const button = event.currentTarget,
|
||||||
type = button.dataset.type,
|
type = button.dataset.type,
|
||||||
range = button.dataset.range,
|
range = button.dataset.range,
|
||||||
|
|
@ -57,13 +59,9 @@ export const renderMeasuredTemplate = async event => {
|
||||||
|
|
||||||
if (!type || !range || !game.canvas.scene) return;
|
if (!type || !range || !game.canvas.scene) return;
|
||||||
|
|
||||||
const usedType = type === 'inFront' ? 'cone' : type === 'emanation' ? 'circle' : type;
|
const usedType = type === 'inFront' ? 'cone' : type;
|
||||||
const usedAngle =
|
const usedAngle =
|
||||||
type === CONST.MEASURED_TEMPLATE_TYPES.CONE
|
type === CONE ? (angle ?? CONFIG.MeasuredTemplate.defaults.angle) : type === INFRONT ? '180' : undefined;
|
||||||
? (angle ?? CONFIG.MeasuredTemplate.defaults.angle)
|
|
||||||
: type === CONFIG.DH.GENERAL.templateTypes.INFRONT
|
|
||||||
? '180'
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
let baseDistance = range;
|
let baseDistance = range;
|
||||||
if (Number.isNaN(Number(range))) {
|
if (Number.isNaN(Number(range))) {
|
||||||
|
|
@ -71,18 +69,49 @@ export const renderMeasuredTemplate = async event => {
|
||||||
range
|
range
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
const distance = type === CONFIG.DH.GENERAL.templateTypes.EMANATION ? baseDistance + 2.5 : baseDistance;
|
|
||||||
|
const dimensionConstant = game.scenes.active.grid.size / game.scenes.active.grid.distance;
|
||||||
|
|
||||||
|
baseDistance *= dimensionConstant;
|
||||||
|
|
||||||
|
const length = baseDistance;
|
||||||
|
const radius = length;
|
||||||
|
|
||||||
|
const shapeWidth = type === LINE ? 5 * dimensionConstant : type === RECTANGLE ? length : undefined;
|
||||||
|
|
||||||
const { width, height } = game.canvas.scene.dimensions;
|
const { width, height } = game.canvas.scene.dimensions;
|
||||||
const data = {
|
const shapeData = {
|
||||||
x: width / 2,
|
x: width / 2,
|
||||||
y: height / 2,
|
y: height / 2,
|
||||||
|
base: {
|
||||||
|
type: 'token',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
shape: game.canvas.grid.isHexagonal ? CONST.TOKEN_SHAPES.ELLIPSE_1 : CONST.TOKEN_SHAPES.RECTANGLE_1
|
||||||
|
},
|
||||||
t: usedType,
|
t: usedType,
|
||||||
distance: distance,
|
length: length,
|
||||||
width: type === CONST.MEASURED_TEMPLATE_TYPES.RAY ? 5 : undefined,
|
width: shapeWidth,
|
||||||
|
height: length,
|
||||||
angle: usedAngle,
|
angle: usedAngle,
|
||||||
direction: direction
|
radius: radius,
|
||||||
|
direction: direction,
|
||||||
|
type: usedType
|
||||||
};
|
};
|
||||||
|
|
||||||
CONFIG.ux.TemplateManager.createPreview(data);
|
await canvas.regions.placeRegion(
|
||||||
|
{
|
||||||
|
name: usedType.capitalize(),
|
||||||
|
shapes: [shapeData],
|
||||||
|
restriction: { enabled: false, type: 'move', priority: 0 },
|
||||||
|
behaviors: [],
|
||||||
|
displayMeasurements: true,
|
||||||
|
locked: false,
|
||||||
|
ownership: { default: CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE },
|
||||||
|
visibility: CONST.REGION_VISIBILITY.ALWAYS
|
||||||
|
},
|
||||||
|
{ create: true }
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -49,9 +49,7 @@ export default class RegisterHandlebarsHelpers {
|
||||||
}
|
}
|
||||||
|
|
||||||
static damageSymbols(damageParts) {
|
static damageSymbols(damageParts) {
|
||||||
const symbols = [...new Set(damageParts.reduce((a, c) => a.concat([...c.type]), []))].map(
|
const symbols = [...new Set(damageParts.map(x => x.type))].map(p => CONFIG.DH.GENERAL.damageTypes[p].icon);
|
||||||
p => CONFIG.DH.GENERAL.damageTypes[p].icon
|
|
||||||
);
|
|
||||||
return new Handlebars.SafeString(Array.from(symbols).map(symbol => `<i class="fa-solid ${symbol}"></i>`));
|
return new Handlebars.SafeString(Array.from(symbols).map(symbol => `<i class="fa-solid ${symbol}"></i>`));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,13 @@ export const getCommandTarget = (options = {}) => {
|
||||||
|
|
||||||
export const setDiceSoNiceForDualityRoll = async (rollResult, advantageState, hopeFaces, fearFaces, advantageFaces) => {
|
export const setDiceSoNiceForDualityRoll = async (rollResult, advantageState, hopeFaces, fearFaces, advantageFaces) => {
|
||||||
if (!game.modules.get('dice-so-nice')?.active) return;
|
if (!game.modules.get('dice-so-nice')?.active) return;
|
||||||
const diceSoNicePresets = await getDiceSoNicePresets(hopeFaces, fearFaces, advantageFaces, advantageFaces);
|
const diceSoNicePresets = await getDiceSoNicePresets(
|
||||||
|
rollResult,
|
||||||
|
hopeFaces,
|
||||||
|
fearFaces,
|
||||||
|
advantageFaces,
|
||||||
|
advantageFaces
|
||||||
|
);
|
||||||
rollResult.dice[0].options = diceSoNicePresets.hope;
|
rollResult.dice[0].options = diceSoNicePresets.hope;
|
||||||
rollResult.dice[1].options = diceSoNicePresets.fear;
|
rollResult.dice[1].options = diceSoNicePresets.fear;
|
||||||
if (rollResult.dice[2] && advantageState) {
|
if (rollResult.dice[2] && advantageState) {
|
||||||
|
|
@ -171,10 +177,10 @@ export const getDeleteKeys = (property, innerProperty, innerPropertyDefaultValue
|
||||||
[innerProperty]: innerPropertyDefaultValue
|
[innerProperty]: innerPropertyDefaultValue
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
acc[`${key}.-=${innerProperty}`] = null;
|
acc[`${key}.${innerProperty}`] = _del;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
acc[`-=${key}`] = null;
|
acc[`${key}`] = _del;
|
||||||
}
|
}
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
|
|
@ -416,7 +422,12 @@ export async function createEmbeddedItemWithEffects(actor, baseData, update) {
|
||||||
...baseData,
|
...baseData,
|
||||||
id: data.id,
|
id: data.id,
|
||||||
uuid: data.uuid,
|
uuid: data.uuid,
|
||||||
effects: data.effects?.map(effect => effect.toObject())
|
_uuid: data.uuid,
|
||||||
|
effects: data.effects?.map(effect => effect.toObject()),
|
||||||
|
_stats: {
|
||||||
|
...data._stats,
|
||||||
|
compendiumSource: data.pack ? `Compendium.${data.pack}.Item.${data.id}` : null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -469,6 +480,8 @@ export async function waitForDiceSoNice(message) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function refreshIsAllowed(allowedTypes, typeToCheck) {
|
export function refreshIsAllowed(allowedTypes, typeToCheck) {
|
||||||
|
if (!allowedTypes) return true;
|
||||||
|
|
||||||
switch (typeToCheck) {
|
switch (typeToCheck) {
|
||||||
case CONFIG.DH.GENERAL.refreshTypes.scene.id:
|
case CONFIG.DH.GENERAL.refreshTypes.scene.id:
|
||||||
case CONFIG.DH.GENERAL.refreshTypes.session.id:
|
case CONFIG.DH.GENERAL.refreshTypes.session.id:
|
||||||
|
|
@ -485,6 +498,34 @@ export function refreshIsAllowed(allowedTypes, typeToCheck) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function expireActiveEffectIsAllowed(allowedTypes, typeToCheck) {
|
||||||
|
if (typeToCheck === CONFIG.DH.GENERAL.activeEffectDurations.act.id) return true;
|
||||||
|
|
||||||
|
return refreshIsAllowed(allowedTypes, typeToCheck);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function expireActiveEffects(actor, allowedTypes = null) {
|
||||||
|
const shouldExpireEffects = game.settings.get(
|
||||||
|
CONFIG.DH.id,
|
||||||
|
CONFIG.DH.SETTINGS.gameSettings.Automation
|
||||||
|
).autoExpireActiveEffects;
|
||||||
|
if (!shouldExpireEffects) return;
|
||||||
|
|
||||||
|
const effectsToExpire = actor
|
||||||
|
.getActiveEffects()
|
||||||
|
.filter(effect => {
|
||||||
|
if (!effect.system?.duration.type) return false;
|
||||||
|
|
||||||
|
const { temporary, custom } = CONFIG.DH.GENERAL.activeEffectDurations;
|
||||||
|
if ([temporary.id, custom.id].includes(effect.system.duration.type)) return false;
|
||||||
|
|
||||||
|
return expireActiveEffectIsAllowed(allowedTypes, effect.system.duration.type);
|
||||||
|
})
|
||||||
|
.map(x => x.id);
|
||||||
|
|
||||||
|
actor.deleteEmbeddedDocuments('ActiveEffect', effectsToExpire);
|
||||||
|
}
|
||||||
|
|
||||||
export async function getCritDamageBonus(formula) {
|
export async function getCritDamageBonus(formula) {
|
||||||
const critRoll = new Roll(formula);
|
const critRoll = new Roll(formula);
|
||||||
return critRoll.dice.reduce((acc, dice) => acc + dice.faces * dice.number, 0);
|
return critRoll.dice.reduce((acc, dice) => acc + dice.faces * dice.number, 0);
|
||||||
|
|
@ -497,6 +538,16 @@ export function htmlToText(html) {
|
||||||
return tempDivElement.textContent || tempDivElement.innerText || '';
|
return tempDivElement.textContent || tempDivElement.innerText || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getIconVisibleActiveEffects(effects) {
|
||||||
|
return effects.filter(effect => {
|
||||||
|
if (!(effect instanceof game.system.api.documents.DhActiveEffect)) return true;
|
||||||
|
|
||||||
|
const alwaysShown = effect.showIcon === CONST.ACTIVE_EFFECT_SHOW_ICON.ALWAYS;
|
||||||
|
const conditionalShown = effect.showIcon === CONST.ACTIVE_EFFECT_SHOW_ICON.CONDITIONAL && !effect.transfer; // TODO: system specific logic
|
||||||
|
|
||||||
|
return !effect.disabled && (alwaysShown || conditionalShown);
|
||||||
|
});
|
||||||
|
}
|
||||||
export async function getFeaturesHTMLData(features) {
|
export async function getFeaturesHTMLData(features) {
|
||||||
const result = [];
|
const result = [];
|
||||||
for (const feature of features) {
|
for (const feature of features) {
|
||||||
|
|
@ -582,6 +633,8 @@ export async function RefreshFeatures(
|
||||||
const refreshedActors = {};
|
const refreshedActors = {};
|
||||||
for (let actor of game.actors) {
|
for (let actor of game.actors) {
|
||||||
if (actorTypes.includes(actor.type) && actor.prototypeToken.actorLink) {
|
if (actorTypes.includes(actor.type) && actor.prototypeToken.actorLink) {
|
||||||
|
expireActiveEffects(actor, refreshTypes);
|
||||||
|
|
||||||
const updates = {};
|
const updates = {};
|
||||||
for (let item of actor.items) {
|
for (let item of actor.items) {
|
||||||
if (
|
if (
|
||||||
|
|
@ -676,3 +729,16 @@ export async function RefreshFeatures(
|
||||||
|
|
||||||
return refreshedActors;
|
return refreshedActors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getUnusedDamageTypes(parts) {
|
||||||
|
const usedKeys = Object.keys(parts);
|
||||||
|
return Object.keys(CONFIG.DH.GENERAL.healingTypes).reduce((acc, key) => {
|
||||||
|
if (!usedKeys.includes(key))
|
||||||
|
acc.push({
|
||||||
|
value: key,
|
||||||
|
label: game.i18n.localize(CONFIG.DH.GENERAL.healingTypes[key].label)
|
||||||
|
});
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ export const preloadHandlebarsTemplates = async function () {
|
||||||
'systems/daggerheart/templates/ui/chat/parts/target-part.hbs',
|
'systems/daggerheart/templates/ui/chat/parts/target-part.hbs',
|
||||||
'systems/daggerheart/templates/ui/chat/parts/button-part.hbs',
|
'systems/daggerheart/templates/ui/chat/parts/button-part.hbs',
|
||||||
'systems/daggerheart/templates/ui/itemBrowser/itemContainer.hbs',
|
'systems/daggerheart/templates/ui/itemBrowser/itemContainer.hbs',
|
||||||
'systems/daggerheart/templates/scene/dh-config.hbs'
|
'systems/daggerheart/templates/scene/dh-config.hbs',
|
||||||
|
'systems/daggerheart/templates/settings/appearance-settings/diceSoNiceTab.hbs'
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -197,7 +197,7 @@ export async function runMigrations() {
|
||||||
const initatorMissing = tagTeam.initiator && !game.actors.some(actor => actor.id === tagTeam.initiator);
|
const initatorMissing = tagTeam.initiator && !game.actors.some(actor => actor.id === tagTeam.initiator);
|
||||||
const missingMembers = Object.keys(tagTeam.members).reduce((acc, id) => {
|
const missingMembers = Object.keys(tagTeam.members).reduce((acc, id) => {
|
||||||
if (!game.actors.some(actor => actor.id === id)) {
|
if (!game.actors.some(actor => actor.id === id)) {
|
||||||
acc[`-=${id}`] = null;
|
acc[id] = _del;
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,13 @@
|
||||||
import { defaultLevelTiers, DhLevelTiers } from '../data/levelTier.mjs';
|
import { defaultLevelTiers, DhLevelTiers } from '../data/levelTier.mjs';
|
||||||
import DhCountdowns from '../data/countdowns.mjs';
|
import DhCountdowns from '../data/countdowns.mjs';
|
||||||
import { DhAppearance, DhAutomation, DhHomebrew, DhMetagaming, DhVariantRules } from '../data/settings/_module.mjs';
|
import {
|
||||||
|
DhAppearance,
|
||||||
|
DhAutomation,
|
||||||
|
DhGlobalOverrides,
|
||||||
|
DhHomebrew,
|
||||||
|
DhMetagaming,
|
||||||
|
DhVariantRules
|
||||||
|
} from '../data/settings/_module.mjs';
|
||||||
import {
|
import {
|
||||||
DhAppearanceSettings,
|
DhAppearanceSettings,
|
||||||
DhAutomationSettings,
|
DhAutomationSettings,
|
||||||
|
|
@ -54,17 +61,18 @@ const registerMenuSettings = () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.GlobalOverrides, {
|
||||||
|
scope: 'world',
|
||||||
|
config: false,
|
||||||
|
type: DhGlobalOverrides
|
||||||
|
});
|
||||||
|
|
||||||
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance, {
|
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance, {
|
||||||
scope: 'client',
|
scope: 'client',
|
||||||
config: false,
|
config: false,
|
||||||
type: DhAppearance,
|
type: DhAppearance,
|
||||||
onChange: value => {
|
onChange: value => {
|
||||||
if (value.displayFear) {
|
value.handleChange();
|
||||||
if (ui.resources) {
|
|
||||||
if (value.displayFear === 'hide') ui.resources.close({ allowed: true });
|
|
||||||
else ui.resources.render({ force: true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -91,8 +91,8 @@
|
||||||
"useDefault": false
|
"useDefault": false
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -118,7 +118,7 @@
|
||||||
},
|
},
|
||||||
"base": false
|
"base": false
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"_id": "TCKVaVweyJzhEArX",
|
"_id": "TCKVaVweyJzhEArX",
|
||||||
|
|
@ -343,7 +343,7 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [],
|
"parts": {},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -400,18 +400,18 @@
|
||||||
"type": "withinRange",
|
"type": "withinRange",
|
||||||
"target": "hostile",
|
"target": "hostile",
|
||||||
"range": "melee"
|
"range": "melee"
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
|
"duration": {
|
||||||
|
"type": "act"
|
||||||
|
}
|
||||||
|
},
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"value": null,
|
||||||
"combat": null,
|
"units": "seconds",
|
||||||
"seconds": null,
|
"expiry": null,
|
||||||
"rounds": null,
|
"expired": false
|
||||||
"turns": null,
|
|
||||||
"startRound": null,
|
|
||||||
"startTurn": null
|
|
||||||
},
|
},
|
||||||
"description": "",
|
"description": "",
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
|
|
@ -423,6 +423,16 @@
|
||||||
"_stats": {
|
"_stats": {
|
||||||
"compendiumSource": null
|
"compendiumSource": null
|
||||||
},
|
},
|
||||||
|
"start": {
|
||||||
|
"time": 0,
|
||||||
|
"combat": null,
|
||||||
|
"combatant": null,
|
||||||
|
"initiative": null,
|
||||||
|
"round": null,
|
||||||
|
"turn": null
|
||||||
|
},
|
||||||
|
"showIcon": 1,
|
||||||
|
"folder": null,
|
||||||
"_key": "!actors.items.effects!89yAh30vaNQOALlz.ctXYwil2D1zfsekT.9PsnogEPsp1OOK64"
|
"_key": "!actors.items.effects!89yAh30vaNQOALlz.ctXYwil2D1zfsekT.9PsnogEPsp1OOK64"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
@ -461,8 +471,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
|
@ -489,7 +499,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
"armor": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
|
@ -514,7 +524,7 @@
|
||||||
},
|
},
|
||||||
"type": []
|
"type": []
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -588,8 +598,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
|
@ -616,7 +626,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -642,8 +652,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
|
@ -670,7 +680,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
|
||||||
|
|
@ -75,8 +75,8 @@
|
||||||
},
|
},
|
||||||
"range": "veryClose",
|
"range": "veryClose",
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -102,7 +102,7 @@
|
||||||
},
|
},
|
||||||
"base": false
|
"base": false
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
},
|
},
|
||||||
"type": "attack",
|
"type": "attack",
|
||||||
"chatDisplay": false
|
"chatDisplay": false
|
||||||
|
|
@ -400,8 +400,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -427,7 +427,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false,
|
"includeBase": false,
|
||||||
"direct": true
|
"direct": true
|
||||||
},
|
},
|
||||||
|
|
@ -508,7 +508,7 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [],
|
"parts": {},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -581,8 +581,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -608,7 +608,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
"hope": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
|
@ -633,7 +633,7 @@
|
||||||
},
|
},
|
||||||
"type": []
|
"type": []
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
|
||||||
|
|
@ -72,8 +72,8 @@
|
||||||
"type": "attack"
|
"type": "attack"
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
|
@ -100,7 +100,7 @@
|
||||||
},
|
},
|
||||||
"base": false
|
"base": false
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
},
|
},
|
||||||
"img": "icons/weapons/daggers/dagger-bone-black.webp",
|
"img": "icons/weapons/daggers/dagger-bone-black.webp",
|
||||||
"type": "attack",
|
"type": "attack",
|
||||||
|
|
|
||||||
|
|
@ -85,8 +85,8 @@
|
||||||
},
|
},
|
||||||
"range": "far",
|
"range": "far",
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -112,7 +112,7 @@
|
||||||
},
|
},
|
||||||
"base": false
|
"base": false
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
},
|
},
|
||||||
"img": "icons/magic/unholy/beam-ringed-impact-purple.webp",
|
"img": "icons/magic/unholy/beam-ringed-impact-purple.webp",
|
||||||
"type": "attack",
|
"type": "attack",
|
||||||
|
|
@ -256,7 +256,7 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [],
|
"parts": {},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -336,8 +336,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -363,7 +363,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -414,8 +414,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"fear": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
|
@ -440,7 +440,7 @@
|
||||||
},
|
},
|
||||||
"type": []
|
"type": []
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -488,18 +488,19 @@
|
||||||
"type": "withinRange",
|
"type": "withinRange",
|
||||||
"target": "hostile",
|
"target": "hostile",
|
||||||
"range": "melee"
|
"range": "melee"
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
|
"duration": {
|
||||||
|
"type": "temporary",
|
||||||
|
"description": "<p>Until you roll with Hope.</p>"
|
||||||
|
}
|
||||||
|
},
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"value": null,
|
||||||
"combat": null,
|
"units": "seconds",
|
||||||
"seconds": null,
|
"expiry": null,
|
||||||
"rounds": null,
|
"expired": false
|
||||||
"turns": null,
|
|
||||||
"startRound": null,
|
|
||||||
"startTurn": null
|
|
||||||
},
|
},
|
||||||
"description": "<p><em>Vulnerable</em> until you roll with Hope.</p>",
|
"description": "<p><em>Vulnerable</em> until you roll with Hope.</p>",
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
|
|
@ -511,6 +512,16 @@
|
||||||
"_stats": {
|
"_stats": {
|
||||||
"compendiumSource": null
|
"compendiumSource": null
|
||||||
},
|
},
|
||||||
|
"start": {
|
||||||
|
"time": 0,
|
||||||
|
"combat": null,
|
||||||
|
"combatant": null,
|
||||||
|
"initiative": null,
|
||||||
|
"round": null,
|
||||||
|
"turn": null
|
||||||
|
},
|
||||||
|
"showIcon": 1,
|
||||||
|
"folder": null,
|
||||||
"_key": "!actors.items.effects!WPEOIGfclNJxWb87.4EECsXzHFG0RoIg0.KGdf2eqcXkdigg0u"
|
"_key": "!actors.items.effects!WPEOIGfclNJxWb87.4EECsXzHFG0RoIg0.KGdf2eqcXkdigg0u"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
@ -608,7 +619,7 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [],
|
"parts": {},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -681,8 +692,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -708,7 +719,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
|
||||||
|
|
@ -80,8 +80,8 @@
|
||||||
"type": "attack"
|
"type": "attack"
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -107,7 +107,7 @@
|
||||||
},
|
},
|
||||||
"base": false
|
"base": false
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
},
|
},
|
||||||
"img": "icons/weapons/bows/longbow-recurve-leather-brown.webp",
|
"img": "icons/weapons/bows/longbow-recurve-leather-brown.webp",
|
||||||
"type": "attack",
|
"type": "attack",
|
||||||
|
|
@ -246,8 +246,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -273,7 +273,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
|
||||||
|
|
@ -68,8 +68,8 @@
|
||||||
"description": "<p>A group of trained archers bearing massive bows.</p>",
|
"description": "<p>A group of trained archers bearing massive bows.</p>",
|
||||||
"attack": {
|
"attack": {
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -95,7 +95,7 @@
|
||||||
"resultBased": false,
|
"resultBased": false,
|
||||||
"base": false
|
"base": false
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
},
|
},
|
||||||
"name": "Longbow",
|
"name": "Longbow",
|
||||||
"img": "icons/weapons/bows/longbow-recurve-leather-brown.webp",
|
"img": "icons/weapons/bows/longbow-recurve-leather-brown.webp",
|
||||||
|
|
@ -270,8 +270,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -295,7 +295,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -368,8 +368,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -393,7 +393,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
|
||||||
|
|
@ -81,8 +81,8 @@
|
||||||
},
|
},
|
||||||
"range": "close",
|
"range": "close",
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -108,7 +108,7 @@
|
||||||
},
|
},
|
||||||
"base": false
|
"base": false
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
},
|
},
|
||||||
"type": "attack",
|
"type": "attack",
|
||||||
"chatDisplay": false
|
"chatDisplay": false
|
||||||
|
|
@ -277,20 +277,21 @@
|
||||||
"type": "withinRange",
|
"type": "withinRange",
|
||||||
"target": "hostile",
|
"target": "hostile",
|
||||||
"range": "melee"
|
"range": "melee"
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
|
"duration": {
|
||||||
|
"type": "temporary",
|
||||||
|
"description": "<p>Until you clear a HP.</p>"
|
||||||
|
}
|
||||||
|
},
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"value": null,
|
||||||
"combat": null,
|
"units": "seconds",
|
||||||
"seconds": null,
|
"expiry": null,
|
||||||
"rounds": null,
|
"expired": false
|
||||||
"turns": null,
|
|
||||||
"startRound": null,
|
|
||||||
"startTurn": null
|
|
||||||
},
|
},
|
||||||
"description": "<p> <em>Vulnerable</em> until you clear a HP.</p>",
|
"description": "<p><em>Vulnerable</em> until you clear a HP.</p>",
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
"statuses": [
|
"statuses": [
|
||||||
"vulnerable"
|
"vulnerable"
|
||||||
|
|
@ -300,6 +301,16 @@
|
||||||
"_stats": {
|
"_stats": {
|
||||||
"compendiumSource": null
|
"compendiumSource": null
|
||||||
},
|
},
|
||||||
|
"start": {
|
||||||
|
"time": 0,
|
||||||
|
"combat": null,
|
||||||
|
"combatant": null,
|
||||||
|
"initiative": null,
|
||||||
|
"round": null,
|
||||||
|
"turn": null
|
||||||
|
},
|
||||||
|
"showIcon": 1,
|
||||||
|
"folder": null,
|
||||||
"_key": "!actors.items.effects!h5RuhzGL17dW5FBT.Fz2lnUEeBxsDpx0G.2iBVUGHtGW3I9VIj"
|
"_key": "!actors.items.effects!h5RuhzGL17dW5FBT.Fz2lnUEeBxsDpx0G.2iBVUGHtGW3I9VIj"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -80,8 +80,8 @@
|
||||||
"type": "attack"
|
"type": "attack"
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -107,7 +107,7 @@
|
||||||
},
|
},
|
||||||
"base": false
|
"base": false
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
},
|
},
|
||||||
"range": "melee",
|
"range": "melee",
|
||||||
"type": "attack",
|
"type": "attack",
|
||||||
|
|
@ -309,7 +309,7 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [],
|
"parts": {},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -382,8 +382,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -409,7 +409,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -482,8 +482,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -509,7 +509,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -582,8 +582,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -609,7 +609,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -667,20 +667,21 @@
|
||||||
"type": "withinRange",
|
"type": "withinRange",
|
||||||
"target": "hostile",
|
"target": "hostile",
|
||||||
"range": "melee"
|
"range": "melee"
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
|
"duration": {
|
||||||
|
"type": "temporary",
|
||||||
|
"description": "<p><span style=\"color: rgb(239, 230, 216); font-family: Montserrat, sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgba(24, 22, 46, 0.565); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;\">Until their next roll with Hope.</span></p>"
|
||||||
|
}
|
||||||
|
},
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"value": null,
|
||||||
"combat": null,
|
"units": "seconds",
|
||||||
"seconds": null,
|
"expiry": null,
|
||||||
"rounds": null,
|
"expired": false
|
||||||
"turns": null,
|
|
||||||
"startRound": null,
|
|
||||||
"startTurn": null
|
|
||||||
},
|
},
|
||||||
"description": "<p><em>Vulnerable</em> until your next roll with Hope. </p>",
|
"description": "<p><em>Vulnerable</em> until your next roll with Hope.</p>",
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
"statuses": [
|
"statuses": [
|
||||||
"vulnerable"
|
"vulnerable"
|
||||||
|
|
@ -690,6 +691,16 @@
|
||||||
"_stats": {
|
"_stats": {
|
||||||
"compendiumSource": null
|
"compendiumSource": null
|
||||||
},
|
},
|
||||||
|
"start": {
|
||||||
|
"time": 0,
|
||||||
|
"combat": null,
|
||||||
|
"combatant": null,
|
||||||
|
"initiative": null,
|
||||||
|
"round": null,
|
||||||
|
"turn": null
|
||||||
|
},
|
||||||
|
"showIcon": 1,
|
||||||
|
"folder": null,
|
||||||
"_key": "!actors.items.effects!dgH3fW9FTYLaIDvS.XtnByqUr9AuYU9Ip.9NQcCXMhjyBReJRd"
|
"_key": "!actors.items.effects!dgH3fW9FTYLaIDvS.XtnByqUr9AuYU9Ip.9NQcCXMhjyBReJRd"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
@ -726,8 +737,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"stress": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
|
@ -752,7 +763,7 @@
|
||||||
},
|
},
|
||||||
"type": []
|
"type": []
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -825,7 +836,7 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [],
|
"parts": {},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -883,20 +894,21 @@
|
||||||
"type": "withinRange",
|
"type": "withinRange",
|
||||||
"target": "hostile",
|
"target": "hostile",
|
||||||
"range": "melee"
|
"range": "melee"
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
|
"duration": {
|
||||||
|
"type": "temporary",
|
||||||
|
"description": "<p><span style=\"color: rgb(239, 230, 216); font-family: Montserrat, sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgba(24, 22, 46, 0.565); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;\">Until the cube is defeated.</span></p>"
|
||||||
|
}
|
||||||
|
},
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"value": null,
|
||||||
"combat": null,
|
"units": "seconds",
|
||||||
"seconds": null,
|
"expiry": null,
|
||||||
"rounds": null,
|
"expired": false
|
||||||
"turns": null,
|
|
||||||
"startRound": null,
|
|
||||||
"startTurn": null
|
|
||||||
},
|
},
|
||||||
"description": "<p><em>Vulnerable</em> until the cube is defeated. </p>",
|
"description": "<p><em>Vulnerable</em> until the cube is defeated.</p>",
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
"statuses": [
|
"statuses": [
|
||||||
"vulnerable"
|
"vulnerable"
|
||||||
|
|
@ -906,6 +918,16 @@
|
||||||
"_stats": {
|
"_stats": {
|
||||||
"compendiumSource": null
|
"compendiumSource": null
|
||||||
},
|
},
|
||||||
|
"start": {
|
||||||
|
"time": 0,
|
||||||
|
"combat": null,
|
||||||
|
"combatant": null,
|
||||||
|
"initiative": null,
|
||||||
|
"round": null,
|
||||||
|
"turn": null
|
||||||
|
},
|
||||||
|
"showIcon": 1,
|
||||||
|
"folder": null,
|
||||||
"_key": "!actors.items.effects!dgH3fW9FTYLaIDvS.ijIaKjroxq3xZd9Z.S7kJlhnV8Nexzi8l"
|
"_key": "!actors.items.effects!dgH3fW9FTYLaIDvS.ijIaKjroxq3xZd9Z.S7kJlhnV8Nexzi8l"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
@ -942,8 +964,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -969,7 +991,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -1049,8 +1071,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"fear": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
|
@ -1075,7 +1097,7 @@
|
||||||
},
|
},
|
||||||
"type": []
|
"type": []
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -1143,8 +1165,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -1170,7 +1192,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
|
||||||
|
|
@ -84,8 +84,8 @@
|
||||||
"type": "attack"
|
"type": "attack"
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -111,7 +111,7 @@
|
||||||
},
|
},
|
||||||
"base": false
|
"base": false
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
},
|
},
|
||||||
"img": "icons/creatures/claws/claw-straight-brown.webp",
|
"img": "icons/creatures/claws/claw-straight-brown.webp",
|
||||||
"type": "attack",
|
"type": "attack",
|
||||||
|
|
@ -284,8 +284,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -311,7 +311,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -368,18 +368,19 @@
|
||||||
"type": "withinRange",
|
"type": "withinRange",
|
||||||
"target": "hostile",
|
"target": "hostile",
|
||||||
"range": "melee"
|
"range": "melee"
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
|
"duration": {
|
||||||
|
"type": "temporary",
|
||||||
|
"description": "<p>Until you break free with a successful Strength Roll.</p>"
|
||||||
|
}
|
||||||
|
},
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"value": null,
|
||||||
"combat": null,
|
"units": "seconds",
|
||||||
"seconds": null,
|
"expiry": null,
|
||||||
"rounds": null,
|
"expired": false
|
||||||
"turns": null,
|
|
||||||
"startRound": null,
|
|
||||||
"startTurn": null
|
|
||||||
},
|
},
|
||||||
"description": "<p>You are <em>Restrained</em> until you break free with a successful Strength Roll.</p>",
|
"description": "<p>You are <em>Restrained</em> until you break free with a successful Strength Roll.</p>",
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
|
|
@ -391,6 +392,16 @@
|
||||||
"_stats": {
|
"_stats": {
|
||||||
"compendiumSource": null
|
"compendiumSource": null
|
||||||
},
|
},
|
||||||
|
"start": {
|
||||||
|
"time": 0,
|
||||||
|
"combat": null,
|
||||||
|
"combatant": null,
|
||||||
|
"initiative": null,
|
||||||
|
"round": null,
|
||||||
|
"turn": null
|
||||||
|
},
|
||||||
|
"showIcon": 1,
|
||||||
|
"folder": null,
|
||||||
"_key": "!actors.items.effects!71qKDLKO3CsrNkdy.zgR0MEqyobKp2yXr.U50Ccm9emMqAxma6"
|
"_key": "!actors.items.effects!71qKDLKO3CsrNkdy.zgR0MEqyobKp2yXr.U50Ccm9emMqAxma6"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
@ -430,8 +441,8 @@
|
||||||
"consumeOnSuccess": false
|
"consumeOnSuccess": false
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"fear": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
|
@ -457,7 +468,7 @@
|
||||||
},
|
},
|
||||||
"type": []
|
"type": []
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
|
||||||
|
|
@ -80,8 +80,8 @@
|
||||||
"name": "Longsword",
|
"name": "Longsword",
|
||||||
"img": "icons/weapons/swords/sword-guard.webp",
|
"img": "icons/weapons/swords/sword-guard.webp",
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -107,7 +107,7 @@
|
||||||
},
|
},
|
||||||
"base": false
|
"base": false
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
},
|
},
|
||||||
"type": "attack",
|
"type": "attack",
|
||||||
"range": "melee",
|
"range": "melee",
|
||||||
|
|
@ -246,7 +246,7 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [],
|
"parts": {},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -319,7 +319,7 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [],
|
"parts": {},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -376,18 +376,19 @@
|
||||||
"type": "withinRange",
|
"type": "withinRange",
|
||||||
"target": "hostile",
|
"target": "hostile",
|
||||||
"range": "melee"
|
"range": "melee"
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
|
"duration": {
|
||||||
|
"type": "temporary",
|
||||||
|
"description": "<p><span style=\"color: rgb(239, 230, 216); font-family: Montserrat, sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgba(24, 22, 46, 0.565); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;\">Until you break free with a successful attack, Finesse Roll, or Strength Roll.</span></p>"
|
||||||
|
}
|
||||||
|
},
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"value": null,
|
||||||
"combat": null,
|
"units": "seconds",
|
||||||
"seconds": null,
|
"expiry": null,
|
||||||
"rounds": null,
|
"expired": false
|
||||||
"turns": null,
|
|
||||||
"startRound": null,
|
|
||||||
"startTurn": null
|
|
||||||
},
|
},
|
||||||
"description": "<p>You are <em>Restrained</em> until you break free with a successful attack, Finesse Roll, or Strength Roll.</p>",
|
"description": "<p>You are <em>Restrained</em> until you break free with a successful attack, Finesse Roll, or Strength Roll.</p>",
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
|
|
@ -399,6 +400,16 @@
|
||||||
"_stats": {
|
"_stats": {
|
||||||
"compendiumSource": null
|
"compendiumSource": null
|
||||||
},
|
},
|
||||||
|
"start": {
|
||||||
|
"time": 0,
|
||||||
|
"combat": null,
|
||||||
|
"combatant": null,
|
||||||
|
"initiative": null,
|
||||||
|
"round": null,
|
||||||
|
"turn": null
|
||||||
|
},
|
||||||
|
"showIcon": 1,
|
||||||
|
"folder": null,
|
||||||
"_key": "!actors.items.effects!B4LZcGuBAHzyVdzy.9gizFt9ovKL05DXu.LmzztuktRkwOCy1a"
|
"_key": "!actors.items.effects!B4LZcGuBAHzyVdzy.9gizFt9ovKL05DXu.LmzztuktRkwOCy1a"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -83,8 +83,8 @@
|
||||||
},
|
},
|
||||||
"range": "veryClose",
|
"range": "veryClose",
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -110,7 +110,7 @@
|
||||||
},
|
},
|
||||||
"base": false
|
"base": false
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
},
|
},
|
||||||
"img": "icons/skills/melee/unarmed-punch-fist-yellow-red.webp",
|
"img": "icons/skills/melee/unarmed-punch-fist-yellow-red.webp",
|
||||||
"type": "attack",
|
"type": "attack",
|
||||||
|
|
@ -280,8 +280,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -307,7 +307,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false,
|
"includeBase": false,
|
||||||
"direct": true
|
"direct": true
|
||||||
},
|
},
|
||||||
|
|
@ -389,8 +389,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"stress": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
|
@ -415,7 +415,7 @@
|
||||||
},
|
},
|
||||||
"type": []
|
"type": []
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -452,18 +452,18 @@
|
||||||
"type": "withinRange",
|
"type": "withinRange",
|
||||||
"target": "hostile",
|
"target": "hostile",
|
||||||
"range": "melee"
|
"range": "melee"
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
|
"duration": {
|
||||||
|
"type": "temporary"
|
||||||
|
}
|
||||||
|
},
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"value": null,
|
||||||
"combat": null,
|
"units": "seconds",
|
||||||
"seconds": null,
|
"expiry": null,
|
||||||
"rounds": null,
|
"expired": false
|
||||||
"turns": null,
|
|
||||||
"startRound": null,
|
|
||||||
"startTurn": null
|
|
||||||
},
|
},
|
||||||
"description": "",
|
"description": "",
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
|
|
@ -475,6 +475,16 @@
|
||||||
"_stats": {
|
"_stats": {
|
||||||
"compendiumSource": null
|
"compendiumSource": null
|
||||||
},
|
},
|
||||||
|
"start": {
|
||||||
|
"time": 0,
|
||||||
|
"combat": null,
|
||||||
|
"combatant": null,
|
||||||
|
"initiative": null,
|
||||||
|
"round": null,
|
||||||
|
"turn": null
|
||||||
|
},
|
||||||
|
"showIcon": 1,
|
||||||
|
"folder": null,
|
||||||
"_key": "!actors.items.effects!2UeZ0tEe7AzgSJNd.69reUZ5tv3splqyO.CjMrSdL6kgD8mKRQ"
|
"_key": "!actors.items.effects!2UeZ0tEe7AzgSJNd.69reUZ5tv3splqyO.CjMrSdL6kgD8mKRQ"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -79,8 +79,8 @@
|
||||||
},
|
},
|
||||||
"range": "veryClose",
|
"range": "veryClose",
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -106,7 +106,7 @@
|
||||||
},
|
},
|
||||||
"base": false
|
"base": false
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"direct": true
|
"direct": true
|
||||||
},
|
},
|
||||||
"name": "Club",
|
"name": "Club",
|
||||||
|
|
@ -336,8 +336,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
|
|
@ -365,7 +365,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false,
|
"includeBase": false,
|
||||||
"direct": true
|
"direct": true
|
||||||
},
|
},
|
||||||
|
|
@ -412,8 +412,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"fear": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
|
@ -438,7 +438,7 @@
|
||||||
},
|
},
|
||||||
"type": []
|
"type": []
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -507,8 +507,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -534,7 +534,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false,
|
"includeBase": false,
|
||||||
"direct": true
|
"direct": true
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -74,8 +74,8 @@
|
||||||
},
|
},
|
||||||
"range": "close",
|
"range": "close",
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -101,7 +101,7 @@
|
||||||
},
|
},
|
||||||
"base": false
|
"base": false
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
},
|
},
|
||||||
"img": "icons/magic/light/beam-rays-magenta.webp",
|
"img": "icons/magic/light/beam-rays-magenta.webp",
|
||||||
"type": "attack",
|
"type": "attack",
|
||||||
|
|
@ -383,8 +383,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -410,7 +410,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -483,8 +483,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"stress": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -508,7 +508,7 @@
|
||||||
},
|
},
|
||||||
"type": []
|
"type": []
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
|
||||||
|
|
@ -67,8 +67,8 @@
|
||||||
"type": "attack"
|
"type": "attack"
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
|
@ -95,7 +95,7 @@
|
||||||
},
|
},
|
||||||
"base": false
|
"base": false
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
},
|
},
|
||||||
"type": "attack",
|
"type": "attack",
|
||||||
"chatDisplay": false
|
"chatDisplay": false
|
||||||
|
|
|
||||||
|
|
@ -72,8 +72,8 @@
|
||||||
"type": "attack"
|
"type": "attack"
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -99,7 +99,7 @@
|
||||||
},
|
},
|
||||||
"base": false
|
"base": false
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
},
|
},
|
||||||
"name": "Fist Slam",
|
"name": "Fist Slam",
|
||||||
"img": "icons/skills/melee/unarmed-punch-fist-yellow-red.webp",
|
"img": "icons/skills/melee/unarmed-punch-fist-yellow-red.webp",
|
||||||
|
|
@ -332,8 +332,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -359,7 +359,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -534,8 +534,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -561,7 +561,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
|
||||||
|
|
@ -84,8 +84,8 @@
|
||||||
"type": "attack"
|
"type": "attack"
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -111,7 +111,7 @@
|
||||||
},
|
},
|
||||||
"base": false
|
"base": false
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
},
|
},
|
||||||
"img": "icons/weapons/daggers/dagger-straight-cracked.webp",
|
"img": "icons/weapons/daggers/dagger-straight-cracked.webp",
|
||||||
"type": "attack",
|
"type": "attack",
|
||||||
|
|
@ -256,8 +256,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"stress": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
|
@ -282,7 +282,7 @@
|
||||||
},
|
},
|
||||||
"type": []
|
"type": []
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -320,18 +320,19 @@
|
||||||
"type": "withinRange",
|
"type": "withinRange",
|
||||||
"target": "hostile",
|
"target": "hostile",
|
||||||
"range": "melee"
|
"range": "melee"
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
|
"duration": {
|
||||||
|
"type": "temporary",
|
||||||
|
"description": "<p><span style=\"color: rgb(239, 230, 216); font-family: Montserrat, sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgba(24, 22, 46, 0.565); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;\">Until the scene ends or they succeed on a social action against the </span><span style=\"box-sizing: border-box; scrollbar-width: thin; scrollbar-color: rgb(93, 20, 43) rgba(0, 0, 0, 0); font-family: Montserrat, sans-serif; color: rgb(239, 230, 216); font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgba(24, 22, 46, 0.565); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;\">Courtesan</span><span style=\"color: rgb(239, 230, 216); font-family: Montserrat, sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgba(24, 22, 46, 0.565); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;\">.</span></p>"
|
||||||
|
}
|
||||||
|
},
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"value": null,
|
||||||
"combat": null,
|
"units": "seconds",
|
||||||
"seconds": null,
|
"expiry": null,
|
||||||
"rounds": null,
|
"expired": false
|
||||||
"turns": null,
|
|
||||||
"startRound": null,
|
|
||||||
"startTurn": null
|
|
||||||
},
|
},
|
||||||
"description": "<p><em>Vulnerable</em> until the scene ends or they succeed on a social action against the Courtesan.</p>",
|
"description": "<p><em>Vulnerable</em> until the scene ends or they succeed on a social action against the Courtesan.</p>",
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
|
|
@ -343,6 +344,16 @@
|
||||||
"_stats": {
|
"_stats": {
|
||||||
"compendiumSource": null
|
"compendiumSource": null
|
||||||
},
|
},
|
||||||
|
"start": {
|
||||||
|
"time": 0,
|
||||||
|
"combat": null,
|
||||||
|
"combatant": null,
|
||||||
|
"initiative": null,
|
||||||
|
"round": null,
|
||||||
|
"turn": null
|
||||||
|
},
|
||||||
|
"showIcon": 1,
|
||||||
|
"folder": null,
|
||||||
"_key": "!actors.items.effects!ZxWaWPdzFIUPNC62.rSMUPC5GhR982ifg.blcRqns0PHqiuPac"
|
"_key": "!actors.items.effects!ZxWaWPdzFIUPNC62.rSMUPC5GhR982ifg.blcRqns0PHqiuPac"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -79,8 +79,8 @@
|
||||||
"type": "attack"
|
"type": "attack"
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -106,7 +106,7 @@
|
||||||
},
|
},
|
||||||
"base": false
|
"base": false
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
},
|
},
|
||||||
"img": "icons/weapons/daggers/dagger-twin-green.webp",
|
"img": "icons/weapons/daggers/dagger-twin-green.webp",
|
||||||
"type": "attack",
|
"type": "attack",
|
||||||
|
|
@ -253,8 +253,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"stress": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
|
@ -279,7 +279,7 @@
|
||||||
},
|
},
|
||||||
"type": []
|
"type": []
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -336,18 +336,18 @@
|
||||||
"type": "withinRange",
|
"type": "withinRange",
|
||||||
"target": "hostile",
|
"target": "hostile",
|
||||||
"range": "melee"
|
"range": "melee"
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
|
"duration": {
|
||||||
|
"type": "scene"
|
||||||
|
}
|
||||||
|
},
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"value": null,
|
||||||
"combat": null,
|
"units": "seconds",
|
||||||
"seconds": null,
|
"expiry": null,
|
||||||
"rounds": null,
|
"expired": false
|
||||||
"turns": null,
|
|
||||||
"startRound": null,
|
|
||||||
"startTurn": null
|
|
||||||
},
|
},
|
||||||
"description": "",
|
"description": "",
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
|
|
@ -359,6 +359,16 @@
|
||||||
"_stats": {
|
"_stats": {
|
||||||
"compendiumSource": null
|
"compendiumSource": null
|
||||||
},
|
},
|
||||||
|
"start": {
|
||||||
|
"time": 0,
|
||||||
|
"combat": null,
|
||||||
|
"combatant": null,
|
||||||
|
"initiative": null,
|
||||||
|
"round": null,
|
||||||
|
"turn": null
|
||||||
|
},
|
||||||
|
"showIcon": 1,
|
||||||
|
"folder": null,
|
||||||
"_key": "!actors.items.effects!CBBuEXAlLKFMJdjg.LYNaKEYcYMgvF4Rf.YNMhgBZW8ndrCjIp"
|
"_key": "!actors.items.effects!CBBuEXAlLKFMJdjg.LYNaKEYcYMgvF4Rf.YNMhgBZW8ndrCjIp"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -84,8 +84,8 @@
|
||||||
"type": "attack"
|
"type": "attack"
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -111,7 +111,7 @@
|
||||||
},
|
},
|
||||||
"base": false
|
"base": false
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
},
|
},
|
||||||
"range": "far",
|
"range": "far",
|
||||||
"img": "icons/weapons/staves/staff-ornate-purple.webp",
|
"img": "icons/weapons/staves/staff-ornate-purple.webp",
|
||||||
|
|
@ -256,8 +256,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -283,7 +283,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
"stress": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
|
@ -308,7 +308,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -422,31 +422,32 @@
|
||||||
"type": "withinRange",
|
"type": "withinRange",
|
||||||
"target": "hostile",
|
"target": "hostile",
|
||||||
"range": "melee"
|
"range": "melee"
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"changes": [
|
"changes": [
|
||||||
{
|
{
|
||||||
"key": "system.resistance.magical.resistance",
|
"key": "system.resistance.magical.resistance",
|
||||||
"mode": 5,
|
"value": 1,
|
||||||
"value": "1",
|
"priority": null,
|
||||||
"priority": null
|
"type": "override"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "system.resistance.physical.resistance",
|
"key": "system.resistance.physical.resistance",
|
||||||
"mode": 5,
|
"value": 1,
|
||||||
"value": "1",
|
"priority": null,
|
||||||
"priority": null
|
"type": "override"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"duration": {
|
||||||
|
"type": "temporary",
|
||||||
|
"description": "<p><span style=\"color: rgb(239, 230, 216); font-family: Montserrat, sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgba(24, 22, 46, 0.565); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;\">Until the </span><span style=\"box-sizing: border-box; scrollbar-width: thin; scrollbar-color: rgb(93, 20, 43) rgba(0, 0, 0, 0); font-family: Montserrat, sans-serif; color: rgb(239, 230, 216); font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgba(24, 22, 46, 0.565); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;\">Cult Adept</span><span style=\"color: rgb(239, 230, 216); font-family: Montserrat, sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgba(24, 22, 46, 0.565); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;\"> marks their last HP.</span></p>"
|
||||||
|
}
|
||||||
|
},
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"value": null,
|
||||||
"combat": null,
|
"units": "seconds",
|
||||||
"seconds": null,
|
"expiry": null,
|
||||||
"rounds": null,
|
"expired": false
|
||||||
"turns": null,
|
|
||||||
"startRound": null,
|
|
||||||
"startTurn": null
|
|
||||||
},
|
},
|
||||||
"description": "<p>Resistance to all damage until the Adept marks their last HP</p>",
|
"description": "<p>Resistance to all damage until the Adept marks their last HP</p>",
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
|
|
@ -456,6 +457,16 @@
|
||||||
"_stats": {
|
"_stats": {
|
||||||
"compendiumSource": null
|
"compendiumSource": null
|
||||||
},
|
},
|
||||||
|
"start": {
|
||||||
|
"time": 0,
|
||||||
|
"combat": null,
|
||||||
|
"combatant": null,
|
||||||
|
"initiative": null,
|
||||||
|
"round": null,
|
||||||
|
"turn": null
|
||||||
|
},
|
||||||
|
"showIcon": 1,
|
||||||
|
"folder": null,
|
||||||
"_key": "!actors.items.effects!0NxCSugvKQ4W8OYZ.IHWDn097sRgjlZXO.U9lWz1LgeAiK5L85"
|
"_key": "!actors.items.effects!0NxCSugvKQ4W8OYZ.IHWDn097sRgjlZXO.U9lWz1LgeAiK5L85"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
@ -533,18 +544,19 @@
|
||||||
"type": "withinRange",
|
"type": "withinRange",
|
||||||
"target": "hostile",
|
"target": "hostile",
|
||||||
"range": "melee"
|
"range": "melee"
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
|
"duration": {
|
||||||
|
"type": "temporary",
|
||||||
|
"description": "<p><span style=\"color: rgb(239, 230, 216); font-family: Montserrat, sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgba(24, 22, 46, 0.565); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;\">Until you break free with a successful Strength or Instinct Roll.</span></p>"
|
||||||
|
}
|
||||||
|
},
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"value": null,
|
||||||
"combat": null,
|
"units": "seconds",
|
||||||
"seconds": null,
|
"expiry": null,
|
||||||
"rounds": null,
|
"expired": false
|
||||||
"turns": null,
|
|
||||||
"startRound": null,
|
|
||||||
"startTurn": null
|
|
||||||
},
|
},
|
||||||
"description": "<p>You are<em> Restrained</em> in smoky chains until you break free with a successful Strength or Instinct Roll. A target Restrained by this feature must spend a Hope to make an action roll.</p>",
|
"description": "<p>You are<em> Restrained</em> in smoky chains until you break free with a successful Strength or Instinct Roll. A target Restrained by this feature must spend a Hope to make an action roll.</p>",
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
|
|
@ -556,6 +568,16 @@
|
||||||
"_stats": {
|
"_stats": {
|
||||||
"compendiumSource": null
|
"compendiumSource": null
|
||||||
},
|
},
|
||||||
|
"start": {
|
||||||
|
"time": 0,
|
||||||
|
"combat": null,
|
||||||
|
"combatant": null,
|
||||||
|
"initiative": null,
|
||||||
|
"round": null,
|
||||||
|
"turn": null
|
||||||
|
},
|
||||||
|
"showIcon": 1,
|
||||||
|
"folder": null,
|
||||||
"_key": "!actors.items.effects!0NxCSugvKQ4W8OYZ.JpSrduK3vjd9h098.lNH6srSPyEprXZ4o"
|
"_key": "!actors.items.effects!0NxCSugvKQ4W8OYZ.JpSrduK3vjd9h098.lNH6srSPyEprXZ4o"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
@ -592,8 +614,8 @@
|
||||||
"recovery": "scene"
|
"recovery": "scene"
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"stress": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
|
@ -618,7 +640,7 @@
|
||||||
},
|
},
|
||||||
"type": []
|
"type": []
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
|
||||||
|
|
@ -74,8 +74,8 @@
|
||||||
"type": "attack"
|
"type": "attack"
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -101,7 +101,7 @@
|
||||||
},
|
},
|
||||||
"base": false
|
"base": false
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
},
|
},
|
||||||
"range": "melee",
|
"range": "melee",
|
||||||
"type": "attack",
|
"type": "attack",
|
||||||
|
|
@ -300,8 +300,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"stress": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
|
@ -326,7 +326,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -384,18 +384,18 @@
|
||||||
"type": "withinRange",
|
"type": "withinRange",
|
||||||
"target": "hostile",
|
"target": "hostile",
|
||||||
"range": "melee"
|
"range": "melee"
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
|
"duration": {
|
||||||
|
"type": "temporary"
|
||||||
|
}
|
||||||
|
},
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"value": null,
|
||||||
"combat": null,
|
"units": "seconds",
|
||||||
"seconds": null,
|
"expiry": null,
|
||||||
"rounds": null,
|
"expired": false
|
||||||
"turns": null,
|
|
||||||
"startRound": null,
|
|
||||||
"startTurn": null
|
|
||||||
},
|
},
|
||||||
"description": "",
|
"description": "",
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
|
|
@ -407,6 +407,16 @@
|
||||||
"_stats": {
|
"_stats": {
|
||||||
"compendiumSource": null
|
"compendiumSource": null
|
||||||
},
|
},
|
||||||
|
"start": {
|
||||||
|
"time": 0,
|
||||||
|
"combat": null,
|
||||||
|
"combatant": null,
|
||||||
|
"initiative": null,
|
||||||
|
"round": null,
|
||||||
|
"turn": null
|
||||||
|
},
|
||||||
|
"showIcon": 1,
|
||||||
|
"folder": null,
|
||||||
"_key": "!actors.items.effects!tyBOpLfigAhI9bU3.ohASSruBxcvuItIK.LwWxRz7FTMA80VdA"
|
"_key": "!actors.items.effects!tyBOpLfigAhI9bU3.ohASSruBxcvuItIK.LwWxRz7FTMA80VdA"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -66,8 +66,8 @@
|
||||||
"type": "attack"
|
"type": "attack"
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
|
@ -94,7 +94,7 @@
|
||||||
},
|
},
|
||||||
"base": false
|
"base": false
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
},
|
},
|
||||||
"type": "attack",
|
"type": "attack",
|
||||||
"range": "melee",
|
"range": "melee",
|
||||||
|
|
|
||||||
|
|
@ -79,8 +79,8 @@
|
||||||
"type": "attack"
|
"type": "attack"
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -106,7 +106,7 @@
|
||||||
},
|
},
|
||||||
"base": false
|
"base": false
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
},
|
},
|
||||||
"img": "icons/magic/nature/root-vines-grow-brown.webp",
|
"img": "icons/magic/nature/root-vines-grow-brown.webp",
|
||||||
"type": "attack",
|
"type": "attack",
|
||||||
|
|
@ -245,8 +245,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"stress": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
|
@ -271,7 +271,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -325,8 +325,8 @@
|
||||||
"recovery": null
|
"recovery": null
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -350,7 +350,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"includeBase": false
|
"includeBase": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
|
|
@ -407,18 +407,19 @@
|
||||||
"type": "withinRange",
|
"type": "withinRange",
|
||||||
"target": "hostile",
|
"target": "hostile",
|
||||||
"range": "melee"
|
"range": "melee"
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"changes": [],
|
"changes": [],
|
||||||
|
"duration": {
|
||||||
|
"type": "temporary",
|
||||||
|
"description": "<p><span style=\"color: rgb(239, 230, 216); font-family: Montserrat, sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgba(24, 22, 46, 0.565); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;\">Until the </span><span style=\"box-sizing: border-box; scrollbar-width: thin; scrollbar-color: rgb(93, 20, 43) rgba(0, 0, 0, 0); font-family: Montserrat, sans-serif; color: rgb(239, 230, 216); font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgba(24, 22, 46, 0.565); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;\">Deeproot Defender</span><span style=\"color: rgb(239, 230, 216); font-family: Montserrat, sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgba(24, 22, 46, 0.565); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;\"> takes Severe damage.</span></p>"
|
||||||
|
}
|
||||||
|
},
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"value": null,
|
||||||
"combat": null,
|
"units": "seconds",
|
||||||
"seconds": null,
|
"expiry": null,
|
||||||
"rounds": null,
|
"expired": false
|
||||||
"turns": null,
|
|
||||||
"startRound": null,
|
|
||||||
"startTurn": null
|
|
||||||
},
|
},
|
||||||
"description": "<p>You are <em>Restrained </em>until the Defender takes Severe damage.</p>",
|
"description": "<p>You are <em>Restrained </em>until the Defender takes Severe damage.</p>",
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
|
|
@ -430,6 +431,16 @@
|
||||||
"_stats": {
|
"_stats": {
|
||||||
"compendiumSource": null
|
"compendiumSource": null
|
||||||
},
|
},
|
||||||
|
"start": {
|
||||||
|
"time": 0,
|
||||||
|
"combat": null,
|
||||||
|
"combatant": null,
|
||||||
|
"initiative": null,
|
||||||
|
"round": null,
|
||||||
|
"turn": null
|
||||||
|
},
|
||||||
|
"showIcon": 1,
|
||||||
|
"folder": null,
|
||||||
"_key": "!actors.items.effects!9x2xY9zwc3xzbXo5.rreGFW5TbhUoZf2T.F3E7fiz01AbF2kr5"
|
"_key": "!actors.items.effects!9x2xY9zwc3xzbXo5.rreGFW5TbhUoZf2T.F3E7fiz01AbF2kr5"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -80,8 +80,8 @@
|
||||||
"type": "attack"
|
"type": "attack"
|
||||||
},
|
},
|
||||||
"damage": {
|
"damage": {
|
||||||
"parts": [
|
"parts": {
|
||||||
{
|
"hitPoints": {
|
||||||
"value": {
|
"value": {
|
||||||
"custom": {
|
"custom": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|
@ -107,7 +107,7 @@
|
||||||
},
|
},
|
||||||
"base": false
|
"base": false
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
},
|
},
|
||||||
"type": "attack",
|
"type": "attack",
|
||||||
"range": "melee",
|
"range": "melee",
|
||||||
|
|
|
||||||
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