mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-04-21 23:13:39 +02:00
Merge branch 'v14-Dev' into v14/effect-stacking
This commit is contained in:
commit
efc0c53dde
611 changed files with 4609 additions and 2941 deletions
|
|
@ -233,6 +233,11 @@ Hooks.once('init', () => {
|
|||
return handlebarsRegistration();
|
||||
});
|
||||
|
||||
Hooks.on('i18nInit', () => {
|
||||
// Setup homebrew resources
|
||||
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).refreshConfig();
|
||||
});
|
||||
|
||||
Hooks.on('setup', () => {
|
||||
CONFIG.statusEffects = [
|
||||
...CONFIG.statusEffects.filter(x => !['dead', 'unconscious'].includes(x.id)),
|
||||
|
|
|
|||
57
lang/en.json
57
lang/en.json
|
|
@ -76,6 +76,15 @@
|
|||
"invalidDrop": "You can only drop Actor entities to summon.",
|
||||
"chatMessageTitle": "Test2",
|
||||
"chatMessageHeaderTitle": "Summoning"
|
||||
},
|
||||
"transform": {
|
||||
"name": "Transform",
|
||||
"tooltip": "Transform one actor into another",
|
||||
"noTransformActor": "There is no assigned actor to transform into",
|
||||
"transformActorMissing": "The assigned actor to transform into does not exist. It was probably deleted or moved in/out of a compendium",
|
||||
"canvasError": "There is no active scene.",
|
||||
"prototypeError": "You can only use a transform action from a Token",
|
||||
"actorLinkError": "You cannot transform a token with Actor Link set to true"
|
||||
}
|
||||
},
|
||||
"Config": {
|
||||
|
|
@ -131,6 +140,12 @@
|
|||
},
|
||||
"summon": {
|
||||
"dropSummonsHere": "Drop Summons Here"
|
||||
},
|
||||
"transform": {
|
||||
"dropTransformHere": "Drop Transform Here",
|
||||
"actorIsMissing": "The linked actor is missing. You should delete this link.",
|
||||
"clearHitPoints": "Clear Hitpoints",
|
||||
"clearStress": "Clear Stress"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -1055,6 +1070,10 @@
|
|||
"fear": "Fear",
|
||||
"spotlight": "Spotlight"
|
||||
},
|
||||
"DaggerheartDiceAnimationEvents": {
|
||||
"critical": { "name": "Critical" },
|
||||
"higher": { "name": "Highest Roll" }
|
||||
},
|
||||
"DamageType": {
|
||||
"physical": {
|
||||
"name": "Physical",
|
||||
|
|
@ -2278,6 +2297,7 @@
|
|||
"identify": "Identity",
|
||||
"imagePath": "Image Path",
|
||||
"inactiveEffects": "Inactive Effects",
|
||||
"initial": "Initial",
|
||||
"inventory": "Inventory",
|
||||
"itemResource": "Item Resource",
|
||||
"itemQuantity": "Item Quantity",
|
||||
|
|
@ -2305,6 +2325,7 @@
|
|||
"plurial": "Players"
|
||||
},
|
||||
"portrait": "Portrait",
|
||||
"preview": "Preview",
|
||||
"proficiency": "Proficiency",
|
||||
"quantity": "Quantity",
|
||||
"range": "Range",
|
||||
|
|
@ -2645,6 +2666,14 @@
|
|||
"title": "Triggers"
|
||||
}
|
||||
},
|
||||
"Metagaming": {
|
||||
"FIELDS": {
|
||||
"hideObserverPermissionInChat": {
|
||||
"label": "Hide Chat Info From Players",
|
||||
"hint": "Information such as hit/miss on attack rolls against adversaries will be hidden"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Homebrew": {
|
||||
"newDowntimeMove": "Downtime Move",
|
||||
"downtimeMove": "Downtime Move",
|
||||
|
|
@ -2659,6 +2688,8 @@
|
|||
"resetMovesText": "Are you sure you want to reset?",
|
||||
"deleteItemTitle": "Delete Homebrew Item",
|
||||
"deleteItemText": "Are you sure you want to delete the item?",
|
||||
"deleteResourceTitle": "Delete Homebrew Resource",
|
||||
"deleteResourceText": "Are you sure you want to delete the resource?",
|
||||
"FIELDS": {
|
||||
"maxFear": { "label": "Max Fear" },
|
||||
"maxHope": { "label": "Max Hope" },
|
||||
|
|
@ -2667,6 +2698,13 @@
|
|||
"label": "Max Cards in Loadout",
|
||||
"hint": "Set to blank or 0 for unlimited maximum"
|
||||
},
|
||||
"resources": {
|
||||
"resources": {
|
||||
"value": { "label": "Icon" },
|
||||
"isIcon": { "label": "Font Awesome Icon" },
|
||||
"noColorFilter": { "label": "Disable Color Filter" }
|
||||
}
|
||||
},
|
||||
"maxDomains": { "label": "Max Class Domains", "hint": "Max domains you can set on a class" }
|
||||
},
|
||||
"currency": {
|
||||
|
|
@ -2695,6 +2733,13 @@
|
|||
"adversaryType": {
|
||||
"title": "Custom Adversary Types",
|
||||
"newType": "Adversary Type"
|
||||
},
|
||||
"resources": {
|
||||
"typeTitle": "{type} Resources",
|
||||
"filledIcon": "Filled Icon",
|
||||
"emptyIcon": "Empty Icon",
|
||||
"resourceIdentifier": "Resource Identifier",
|
||||
"setResourceIdentifier": "Set Resource Identifier"
|
||||
}
|
||||
},
|
||||
"Menu": {
|
||||
|
|
@ -2704,6 +2749,11 @@
|
|||
"label": "Configure Automation",
|
||||
"hint": "Various settings automating resource management and more"
|
||||
},
|
||||
"metagaming": {
|
||||
"name": "Metagaming Settings",
|
||||
"label": "Configure Metagaming",
|
||||
"hint": "Various settings controlling the flow of information to players"
|
||||
},
|
||||
"homebrew": {
|
||||
"name": "Homebrew Settings",
|
||||
"label": "Configure Homebrew",
|
||||
|
|
@ -2729,7 +2779,12 @@
|
|||
"colorset": "Theme",
|
||||
"material": "Material",
|
||||
"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": {
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ export default class DhCompanionLevelUp extends BaseLevelUp {
|
|||
break;
|
||||
case 'summary':
|
||||
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;
|
||||
|
||||
let achievementExperiences = [];
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
export { default as DhAppearanceSettings } from './appearanceSettings.mjs';
|
||||
export { default as DhAutomationSettings } from './automationSettings.mjs';
|
||||
export { default as DhHomebrewSettings } from './homebrewSettings.mjs';
|
||||
export { default as DhMetagamingSettings } from './metagamingSettings.mjs';
|
||||
export { default as DhVariantRuleSettings } from './variantRuleSettings.mjs';
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
|
|||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
id: 'daggerheart-appearance-settings',
|
||||
classes: ['daggerheart', 'dialog', 'dh-style', 'setting'],
|
||||
classes: ['daggerheart', 'dialog', 'dh-style', 'setting', 'appearance-settings'],
|
||||
position: { width: '600', height: 'auto' },
|
||||
window: {
|
||||
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 */
|
||||
_configureRenderParts(options) {
|
||||
const parts = super._configureRenderParts(options);
|
||||
|
|
@ -83,15 +91,20 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
|
|||
/**@inheritdoc */
|
||||
async _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.globalOverrides = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.GlobalOverrides);
|
||||
}
|
||||
|
||||
context.setting = this.setting;
|
||||
context.globalOverrides = this.globalOverrides;
|
||||
context.fields = this.setting.schema.fields;
|
||||
|
||||
context.tabs = this._prepareTabs('general');
|
||||
context.dsnTabs = this._prepareTabs('diceSoNice');
|
||||
|
||||
context.isGM = game.user.isGM;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
|
|
@ -120,6 +133,9 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
|
|||
* @protected
|
||||
*/
|
||||
async prepareDiceSoNiceContext(context) {
|
||||
context.animationEvents = CONFIG.DH.GENERAL.daggerheartDiceAnimationEvents;
|
||||
context.previewAnimation = this.previewAnimation;
|
||||
|
||||
context.diceSoNiceTextures = Object.entries(game.dice3d.exports.TEXTURELIST).reduce(
|
||||
(acc, [k, v]) => ({
|
||||
...acc,
|
||||
|
|
@ -146,6 +162,13 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
|
|||
);
|
||||
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(
|
||||
context.dsnTabs,
|
||||
['hope', 'fear', 'advantage', 'disadvantage'].reduce(
|
||||
|
|
@ -153,7 +176,8 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
|
|||
...acc,
|
||||
[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
|
||||
* @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));
|
||||
|
||||
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.
|
||||
* @this {DHAppearanceSettings}
|
||||
|
|
@ -183,13 +214,25 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
|
|||
*/
|
||||
static async #onPreview(_, target) {
|
||||
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 faces = ['advantage', 'disadvantage'].includes(key) ? 'd6' : 'd12';
|
||||
const preset = await getDiceSoNicePreset(diceSoNice[key], faces);
|
||||
const diceSoNiceRoll = await new foundry.dice.Roll(`1${faces}`).evaluate();
|
||||
diceSoNiceRoll.dice[0].options.appearance = preset.appearance;
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,8 +31,8 @@ export default class DhAutomationSettings extends HandlebarsApplicationMixin(App
|
|||
};
|
||||
|
||||
static PARTS = {
|
||||
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
||||
header: { template: 'systems/daggerheart/templates/settings/automation-settings/header.hbs' },
|
||||
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
||||
general: { template: 'systems/daggerheart/templates/settings/automation-settings/general.hbs' },
|
||||
rules: { template: 'systems/daggerheart/templates/settings/automation-settings/deathMoves.hbs' },
|
||||
roll: { template: 'systems/daggerheart/templates/settings/automation-settings/roll.hbs' },
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { DhHomebrew } from '../../data/settings/_module.mjs';
|
||||
import { Resource } from '../../data/settings/Homebrew.mjs';
|
||||
import { slugify } from '../../helpers/utils.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
|
@ -44,6 +45,9 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
|||
addAdversaryType: this.addAdversaryType,
|
||||
deleteAdversaryType: this.deleteAdversaryType,
|
||||
selectAdversaryType: this.selectAdversaryType,
|
||||
addResource: this.addResource,
|
||||
removeResource: this.removeResource,
|
||||
resetResourceImage: this.resetResourceImage,
|
||||
save: this.save,
|
||||
resetTokenSizes: this.resetTokenSizes,
|
||||
reset: this.reset
|
||||
|
|
@ -56,6 +60,10 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
|||
settings: { template: 'systems/daggerheart/templates/settings/homebrew-settings/settings.hbs' },
|
||||
domains: { template: 'systems/daggerheart/templates/settings/homebrew-settings/domains.hbs' },
|
||||
types: { template: 'systems/daggerheart/templates/settings/homebrew-settings/types.hbs' },
|
||||
resources: {
|
||||
template: 'systems/daggerheart/templates/settings/homebrew-settings/resources.hbs',
|
||||
scrollable: ['.resource-types-container']
|
||||
},
|
||||
itemTypes: { template: 'systems/daggerheart/templates/settings/homebrew-settings/itemFeatures.hbs' },
|
||||
downtime: { template: 'systems/daggerheart/templates/settings/homebrew-settings/downtime.hbs' },
|
||||
footer: { template: 'systems/daggerheart/templates/settings/homebrew-settings/footer.hbs' }
|
||||
|
|
@ -64,7 +72,14 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
|||
/** @inheritdoc */
|
||||
static TABS = {
|
||||
main: {
|
||||
tabs: [{ id: 'settings' }, { id: 'domains' }, { id: 'types' }, { id: 'itemFeatures' }, { id: 'downtime' }],
|
||||
tabs: [
|
||||
{ id: 'settings' },
|
||||
{ id: 'domains' },
|
||||
{ id: 'types' },
|
||||
{ id: 'resources' },
|
||||
{ id: 'itemFeatures' },
|
||||
{ id: 'downtime' }
|
||||
],
|
||||
initial: 'settings',
|
||||
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
|
||||
}
|
||||
|
|
@ -77,9 +92,17 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
|||
this.render();
|
||||
}
|
||||
|
||||
_attachPartListeners(partId, htmlElement, options) {
|
||||
super._attachPartListeners(partId, htmlElement, options);
|
||||
|
||||
for (const element of htmlElement.querySelectorAll('.path-field input'))
|
||||
element.addEventListener('change', this.toggleResourceIsIcon.bind(this));
|
||||
}
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.settingFields = this.settings;
|
||||
context.schemaFields = context.settingFields.schema.fields;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
|
@ -103,6 +126,8 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
|||
? { id: this.selected.adversaryType, ...this.settings.adversaryTypes[this.selected.adversaryType] }
|
||||
: null;
|
||||
break;
|
||||
case 'resources':
|
||||
break;
|
||||
case 'downtime':
|
||||
context.restOptions = {
|
||||
shortRest: CONFIG.DH.GENERAL.defaultRestOptions.shortRest(),
|
||||
|
|
@ -124,6 +149,33 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
|||
this.render();
|
||||
}
|
||||
|
||||
async toggleResourceIsIcon(event) {
|
||||
const element = event.target.closest('.resource-icon-container');
|
||||
const { actorType, resourceKey, imageKey } = element.dataset;
|
||||
|
||||
const current = this.settings.resources[actorType].resources[resourceKey].images[imageKey];
|
||||
await this.settings.updateSource({
|
||||
[`resources.${actorType}.resources.${resourceKey}.images.${imageKey}`]: {
|
||||
isIcon: !current.isIcon,
|
||||
value: ''
|
||||
}
|
||||
});
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async resetResourceImage(_event, button) {
|
||||
const element = button.closest('.resource-icon-container');
|
||||
const { actorType, resourceKey, imageKey } = element.dataset;
|
||||
|
||||
await this.settings.updateSource({
|
||||
[`resources.${actorType}.resources.${resourceKey}.images.${imageKey}`]:
|
||||
Resource.getDefaultImageData(imageKey)
|
||||
});
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async changeCurrencyIcon(_, target) {
|
||||
const type = target.dataset.currency;
|
||||
const currentIcon = this.settings.currency[type].icon;
|
||||
|
|
@ -466,6 +518,58 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
|||
this.render();
|
||||
}
|
||||
|
||||
static async addResource(_, target) {
|
||||
const { actorType } = target.dataset;
|
||||
const content = new foundry.data.fields.StringField({
|
||||
label: game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.resources.resourceIdentifier'),
|
||||
required: true
|
||||
}).toFormGroup({}, { name: 'identifier', localize: true }).outerHTML;
|
||||
|
||||
async function callback(_, button) {
|
||||
const identifier = button.form.elements.identifier.value;
|
||||
if (!identifier) return;
|
||||
|
||||
const sluggedIdentifier = slugify(identifier);
|
||||
|
||||
await this.settings.updateSource({
|
||||
[`resources.${actorType}.resources.${sluggedIdentifier}`]: Resource.getDefaultResourceData(identifier)
|
||||
});
|
||||
|
||||
game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
|
||||
this.render();
|
||||
}
|
||||
|
||||
await foundry.applications.api.DialogV2.prompt({
|
||||
content: content,
|
||||
rejectClose: false,
|
||||
modal: true,
|
||||
ok: { callback: callback.bind(this) },
|
||||
window: {
|
||||
title: game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.resources.setResourceIdentifier')
|
||||
},
|
||||
position: { width: 400 }
|
||||
});
|
||||
}
|
||||
|
||||
static async removeResource(_, target) {
|
||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||
window: {
|
||||
title: game.i18n.localize(`DAGGERHEART.SETTINGS.Homebrew.deleteResourceTitle`)
|
||||
},
|
||||
content: game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.deleteResourceText')
|
||||
});
|
||||
|
||||
if (!confirmed) return;
|
||||
|
||||
const { actorType, resourceKey } = target.dataset;
|
||||
await this.settings.updateSource({
|
||||
[`resources.${actorType}.resources.${resourceKey}`]: _del
|
||||
});
|
||||
|
||||
game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async save() {
|
||||
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
|
||||
this.close();
|
||||
|
|
|
|||
62
module/applications/settings/metagamingSettings.mjs
Normal file
62
module/applications/settings/metagamingSettings.mjs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import { DhMetagaming } from '../../data/settings/_module.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
export default class DhMetagamingSettings extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor() {
|
||||
super({});
|
||||
|
||||
this.settings = new DhMetagaming(
|
||||
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Metagaming).toObject()
|
||||
);
|
||||
}
|
||||
|
||||
get title() {
|
||||
return game.i18n.localize('DAGGERHEART.SETTINGS.Menu.title');
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
id: 'daggerheart-metagaming-settings',
|
||||
classes: ['daggerheart', 'dh-style', 'dialog', 'setting'],
|
||||
position: { width: '600', height: 'auto' },
|
||||
window: {
|
||||
icon: 'fa-solid fa-eye-low-vision'
|
||||
},
|
||||
actions: {
|
||||
reset: this.reset,
|
||||
save: this.save
|
||||
},
|
||||
form: { handler: this.updateData, submitOnChange: true }
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
header: { template: 'systems/daggerheart/templates/settings/metagaming-settings/header.hbs' },
|
||||
general: { template: 'systems/daggerheart/templates/settings/metagaming-settings/general.hbs' },
|
||||
footer: { template: 'systems/daggerheart/templates/settings/metagaming-settings/footer.hbs' }
|
||||
};
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.settingFields = this.settings;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static async updateData(_event, _element, formData) {
|
||||
const updatedSettings = foundry.utils.expandObject(formData.object);
|
||||
|
||||
await this.settings.updateSource(updatedSettings);
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async reset() {
|
||||
this.settings = new DhMetagaming();
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async save() {
|
||||
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Metagaming, this.settings.toObject());
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import { getUnusedDamageTypes } from '../../helpers/utils.mjs';
|
||||
import DaggerheartSheet from '../sheets/daggerheart-sheet.mjs';
|
||||
|
||||
const { ApplicationV2 } = foundry.applications.api;
|
||||
|
|
@ -28,6 +29,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
|||
removeEffect: this.removeEffect,
|
||||
addElement: this.addElement,
|
||||
removeElement: this.removeElement,
|
||||
removeTransformActor: this.removeTransformActor,
|
||||
editEffect: this.editEffect,
|
||||
addDamage: this.addDamage,
|
||||
removeDamage: this.removeDamage,
|
||||
|
|
@ -41,7 +43,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
|||
submitOnChange: true,
|
||||
closeOnSubmit: false
|
||||
},
|
||||
dragDrop: [{ dragSelector: null, dropSelector: '#summon-drop-zone', handlers: ['_onDrop'] }]
|
||||
dragDrop: [{ dragSelector: null, dropSelector: '[data-is-drop-zone]', handlers: ['_onDrop'] }]
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
|
|
@ -103,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) {
|
||||
for (const v of Object.values(tabs)) {
|
||||
|
|
@ -120,6 +122,10 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
|||
htmlElement.querySelectorAll('.summon-count-wrapper input').forEach(element => {
|
||||
element.addEventListener('change', this.updateSummonCount.bind(this));
|
||||
});
|
||||
|
||||
htmlElement.querySelectorAll('.transform-resource input').forEach(element => {
|
||||
element.addEventListener('change', this.updateTransformResource.bind(this));
|
||||
});
|
||||
}
|
||||
|
||||
async _prepareContext(_options) {
|
||||
|
|
@ -133,6 +139,18 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
|||
context.summons.push({ actor, count: summon.count });
|
||||
}
|
||||
|
||||
if (context.source.transform) {
|
||||
const actor = await foundry.utils.fromUuid(context.source.transform.actorUUID);
|
||||
context.transform = {
|
||||
...context.source.transform,
|
||||
actor:
|
||||
actor ??
|
||||
(context.source.transform.actorUUID && !actor
|
||||
? { error: game.i18n.localize('DAGGERHEART.ACTIONS.Settings.transform.actorIsMissing') }
|
||||
: null)
|
||||
};
|
||||
}
|
||||
|
||||
context.openSection = this.openSection;
|
||||
context.tabs = this._getTabs(this.constructor.TABS);
|
||||
context.config = CONFIG.DH;
|
||||
|
|
@ -155,6 +173,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
|||
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;
|
||||
context.tierOptions = [
|
||||
|
|
@ -266,20 +285,69 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
|||
if (doc) return doc.sheet.render({ force: true });
|
||||
}
|
||||
|
||||
static async removeTransformActor() {
|
||||
const data = this.action.toObject();
|
||||
data.transform.actorUUID = null;
|
||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||
}
|
||||
|
||||
static addDamage(_event) {
|
||||
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' };
|
||||
data.damage.parts.push(part);
|
||||
data.damage.parts[type] = part;
|
||||
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) {
|
||||
if (!this.action.damage.parts) return;
|
||||
const data = this.action.toObject(),
|
||||
index = button.dataset.index;
|
||||
data.damage.parts.splice(index, 1);
|
||||
const data = this.action.toObject();
|
||||
const key = button.dataset.key;
|
||||
delete data.damage.parts[key];
|
||||
data.damage.parts[`${key}`] = _del;
|
||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||
}
|
||||
|
||||
|
|
@ -346,6 +414,14 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
|||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||
}
|
||||
|
||||
updateTransformResource(event) {
|
||||
event.stopPropagation();
|
||||
|
||||
const data = this.action.toObject();
|
||||
data.transform.resourceRefresh[event.target.dataset.resource] = event.target.checked;
|
||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||
}
|
||||
|
||||
/** Specific implementation in extending classes **/
|
||||
static async addEffect(_event) {}
|
||||
static removeEffect(_event, _button) {}
|
||||
|
|
@ -364,6 +440,18 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
|||
return;
|
||||
}
|
||||
|
||||
const dropZone = event.target.closest('[data-is-drop-zone]');
|
||||
if (!dropZone) return;
|
||||
|
||||
switch (dropZone.id) {
|
||||
case 'summon-drop-zone':
|
||||
return this.onSummonDrop(data);
|
||||
case 'transform-drop-zone':
|
||||
return this.onTransformDrop(data);
|
||||
}
|
||||
}
|
||||
|
||||
async onSummonDrop(data) {
|
||||
const actionData = this.action.toObject();
|
||||
let countvalue = 1;
|
||||
for (const entry of actionData.summon) {
|
||||
|
|
@ -380,4 +468,10 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
|||
actionData.summon.push({ actorUUID: data.uuid, count: countvalue });
|
||||
await this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(actionData) });
|
||||
}
|
||||
|
||||
async onTransformDrop(data) {
|
||||
const actionData = this.action.toObject();
|
||||
actionData.transform.actorUUID = data.uuid;
|
||||
await this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(actionData) });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
handleResourceDice: CharacterSheet.#handleResourceDice,
|
||||
advanceResourceDie: CharacterSheet.#advanceResourceDie,
|
||||
cancelBeastform: CharacterSheet.#cancelBeastform,
|
||||
toggleResourceManagement: CharacterSheet.#toggleResourceManagement,
|
||||
useDowntime: this.useDowntime,
|
||||
viewParty: CharacterSheet.#viewParty
|
||||
},
|
||||
|
|
@ -226,6 +227,9 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
async _preparePartContext(partId, context, options) {
|
||||
context = await super._preparePartContext(partId, context, options);
|
||||
switch (partId) {
|
||||
case 'header':
|
||||
await this._prepareHeaderContext(context, options);
|
||||
break;
|
||||
case 'loadout':
|
||||
await this._prepareLoadoutContext(context, options);
|
||||
break;
|
||||
|
|
@ -240,6 +244,12 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
return context;
|
||||
}
|
||||
|
||||
async _prepareHeaderContext(context, _options) {
|
||||
context.hasExtraResources = Object.keys(CONFIG.DH.RESOURCE.character.all).some(
|
||||
key => !CONFIG.DH.RESOURCE.character.base[key]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare render context for the Loadout part.
|
||||
* @param {ApplicationRenderContext} context
|
||||
|
|
@ -942,6 +952,78 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
});
|
||||
}
|
||||
|
||||
static async #toggleResourceManagement(event, button) {
|
||||
event.stopPropagation();
|
||||
const existingTooltip = document.body.querySelector('.locked-tooltip .resource-management-container');
|
||||
if (existingTooltip) {
|
||||
game.tooltip.dismissLockedTooltips();
|
||||
return;
|
||||
}
|
||||
|
||||
const extraResources = Object.values(CONFIG.DH.RESOURCE.character.all).reduce((acc, resource) => {
|
||||
if (CONFIG.DH.RESOURCE.character.base[resource.id]) return acc;
|
||||
|
||||
const resourceData = this.document.system.resources[resource.id];
|
||||
acc[resource.id] = {
|
||||
id: resource.id,
|
||||
label: game.i18n.localize(resource.label),
|
||||
value: resourceData.value,
|
||||
max: resourceData.max,
|
||||
fullIcon: resource.images?.full ?? { value: 'fa-solid fa-circle', isIcon: true },
|
||||
emptyIcon: resource.images?.empty ?? { value: 'fa-regular fa-circle', isIcon: true }
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const html = document.createElement('div');
|
||||
html.innerHTML = await foundry.applications.handlebars.renderTemplate(
|
||||
`systems/daggerheart/templates/ui/tooltip/resourceManagement.hbs`,
|
||||
{
|
||||
resources: extraResources
|
||||
}
|
||||
);
|
||||
|
||||
const target = button.closest('.resource-section');
|
||||
|
||||
game.tooltip.dismissLockedTooltips();
|
||||
game.tooltip.activate(target, {
|
||||
html,
|
||||
locked: true,
|
||||
cssClass: 'bordered-tooltip',
|
||||
direction: 'DOWN',
|
||||
noOffset: true
|
||||
});
|
||||
|
||||
const resourceManager = target.querySelector('.resource-manager');
|
||||
resourceManager.classList.toggle('inverted');
|
||||
|
||||
Hooks.once(CONFIG.DH.HOOKS.hooksConfig.lockedTooltipDismissed, () => {
|
||||
resourceManager.classList.toggle('inverted');
|
||||
});
|
||||
|
||||
for (const element of html.querySelectorAll('.resource-value'))
|
||||
element.addEventListener('click', this.onUpdateResource.bind(this));
|
||||
}
|
||||
|
||||
async onUpdateResource(event) {
|
||||
const target = event.target.closest('.resource-value');
|
||||
const { resource, value: textValue } = target.dataset;
|
||||
|
||||
const inputValue = Number.parseInt(textValue);
|
||||
const decreasing = inputValue <= this.document.system.resources[resource].value;
|
||||
const value = decreasing ? inputValue - 1 : inputValue;
|
||||
await this.document.update({ [`system.resources.${resource}.value`]: value }, { render: false });
|
||||
|
||||
/* Update resource symbols */
|
||||
const section = target.closest('.resource-section');
|
||||
for (const element of section.querySelectorAll('.resource-value')) {
|
||||
const showFull = Number.parseInt(element.dataset.value) <= value;
|
||||
element.querySelector('.full').classList.toggle('hidden', !showFull);
|
||||
element.querySelector('.empty').classList.toggle('hidden', showFull);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the downtime application.
|
||||
* @type {ApplicationClickAction}
|
||||
|
|
|
|||
|
|
@ -44,8 +44,32 @@ export default class DHBaseActorSettings extends DHApplicationMixin(DocumentShee
|
|||
const context = await super._prepareContext(options);
|
||||
context.isNPC = this.actor.isNPC;
|
||||
|
||||
if (context.systemFields.attack)
|
||||
if (context.systemFields.attack) {
|
||||
context.systemFields.attack.fields = this.actor.system.attack.schema.fields;
|
||||
}
|
||||
|
||||
// Create fake fields for actor configurable max resource value.
|
||||
const resourceConfig = CONFIG.DH.RESOURCE[this.actor.type]?.all;
|
||||
if (resourceConfig) {
|
||||
const relevant = ['hitPoints', 'stress'].filter(r => r in resourceConfig);
|
||||
context.resources = relevant.map(key => {
|
||||
const data = this.actor._source.system.resources[key];
|
||||
const config = resourceConfig[key];
|
||||
return {
|
||||
label: config.label,
|
||||
name: `system.resources.${key}.max`,
|
||||
value: data.max ?? config.max,
|
||||
tooltip: key === 'hitPoints' ? game.i18n.localize('DAGGERHEART.UI.Tooltip.maxHPClassBound') : null,
|
||||
field: new foundry.data.fields.NumberField({
|
||||
initial: config.max,
|
||||
integer: true,
|
||||
label: game.i18n.format('DAGGERHEART.GENERAL.maxWithThing', {
|
||||
thing: game.i18n.localize(config.label)
|
||||
})
|
||||
})
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,6 +74,15 @@ export default function DHApplicationMixin(Base) {
|
|||
class DHSheetV2 extends HandlebarsApplicationMixin(Base) {
|
||||
#nonHeaderAttribution = ['environment', 'ancestry', 'community', 'domainCard'];
|
||||
|
||||
/**
|
||||
* @param {DHSheetV2Configuration} [options={}]
|
||||
*/
|
||||
constructor(options = {}) {
|
||||
super(options);
|
||||
|
||||
this._setupDragDrop();
|
||||
}
|
||||
|
||||
/**
|
||||
* The default options for the sheet.
|
||||
* @type {DHSheetV2Configuration}
|
||||
|
|
@ -165,7 +174,9 @@ export default function DHApplicationMixin(Base) {
|
|||
/**@inheritdoc */
|
||||
_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
|
||||
for (const deltaInput of htmlElement.querySelectorAll('input[data-allow-delta]')) {
|
||||
|
|
@ -338,6 +349,26 @@ export default function DHApplicationMixin(Base) {
|
|||
/* Drag and Drop */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Creates drag-drop handlers from the configured options.
|
||||
* @returns {foundry.applications.ux.DragDrop[]}
|
||||
* @private
|
||||
*/
|
||||
_setupDragDrop() {
|
||||
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 = {
|
||||
dragstart: this._onDragStart.bind(this),
|
||||
drop: this._onDrop.bind(this)
|
||||
};
|
||||
return new foundry.applications.ux.DragDrop.implementation(d);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle dragStart event.
|
||||
* @param {DragEvent} event
|
||||
|
|
@ -472,7 +503,10 @@ export default function DHApplicationMixin(Base) {
|
|||
icon: 'fa-solid fa-explosion',
|
||||
condition: 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) => {
|
||||
const doc = await getDocFromElement(target),
|
||||
|
|
@ -664,6 +698,9 @@ export default function DHApplicationMixin(Base) {
|
|||
case 'weapon':
|
||||
presets.folder = 'equipments.folders.weapons';
|
||||
break;
|
||||
case 'feature':
|
||||
presets.folder = 'features';
|
||||
break;
|
||||
case 'domainCard':
|
||||
presets.folder = 'domains';
|
||||
presets.filter = {
|
||||
|
|
|
|||
|
|
@ -251,6 +251,12 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
|
||||
/* If any noticeable slowdown occurs, consider replacing with enriching description on clicking to expand descriptions */
|
||||
for (const item of this.items) {
|
||||
if (['weapon', 'armor'].includes(item.type)) {
|
||||
item.system.enrichedTags = await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/sheets/global/partials/item-tags.hbs',
|
||||
item.system
|
||||
);
|
||||
}
|
||||
item.system.enrichedDescription =
|
||||
(await item.system.getEnrichedDescription?.()) ??
|
||||
(await foundry.applications.ux.TextEditor.implementation.enrichHTML(item.description));
|
||||
|
|
|
|||
|
|
@ -43,6 +43,53 @@ export default class DhRegionLayer extends foundry.canvas.layers.RegionLayer {
|
|||
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 };
|
||||
|
||||
return super._createDragShapeData(event);
|
||||
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('REGION_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();
|
||||
}
|
||||
}
|
||||
|
|
@ -11,3 +11,4 @@ export * as settingsConfig from './settingsConfig.mjs';
|
|||
export * as systemConfig from './system.mjs';
|
||||
export * as itemBrowserConfig from './itemBrowserConfig.mjs';
|
||||
export * as triggerConfig from './triggerConfig.mjs';
|
||||
export * as resourceConfig from './resourceConfig.mjs';
|
||||
|
|
|
|||
|
|
@ -35,6 +35,12 @@ export const actionTypes = {
|
|||
icon: 'fa-ghost',
|
||||
tooltip: 'DAGGERHEART.ACTIONS.TYPES.summon.tooltip'
|
||||
},
|
||||
transform: {
|
||||
id: 'transform',
|
||||
name: 'DAGGERHEART.ACTIONS.TYPES.transform.name',
|
||||
icon: 'fa-dragon',
|
||||
tooltip: 'DAGGERHEART.ACTIONS.TYPES.transform.tooltip'
|
||||
},
|
||||
effect: {
|
||||
id: 'effect',
|
||||
name: 'DAGGERHEART.ACTIONS.TYPES.effect.name',
|
||||
|
|
|
|||
|
|
@ -55,24 +55,6 @@ export const abilities = {
|
|||
}
|
||||
};
|
||||
|
||||
export const scrollingTextResource = {
|
||||
hitPoints: {
|
||||
label: 'DAGGERHEART.GENERAL.HitPoints.plural',
|
||||
reversed: true
|
||||
},
|
||||
stress: {
|
||||
label: 'DAGGERHEART.GENERAL.stress',
|
||||
reversed: true
|
||||
},
|
||||
hope: {
|
||||
label: 'DAGGERHEART.GENERAL.hope'
|
||||
},
|
||||
armor: {
|
||||
label: 'DAGGERHEART.GENERAL.armor',
|
||||
reversed: true
|
||||
}
|
||||
};
|
||||
|
||||
export const featureProperties = {
|
||||
agility: {
|
||||
name: 'DAGGERHEART.CONFIG.Traits.agility.name',
|
||||
|
|
|
|||
|
|
@ -245,8 +245,8 @@ export const defaultRestOptions = {
|
|||
type: 'friendly'
|
||||
},
|
||||
damage: {
|
||||
parts: [
|
||||
{
|
||||
parts: {
|
||||
hitPoints: {
|
||||
applyTo: healingTypes.hitPoints.id,
|
||||
value: {
|
||||
custom: {
|
||||
|
|
@ -255,7 +255,7 @@ export const defaultRestOptions = {
|
|||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -279,8 +279,8 @@ export const defaultRestOptions = {
|
|||
type: 'self'
|
||||
},
|
||||
damage: {
|
||||
parts: [
|
||||
{
|
||||
parts: {
|
||||
stress: {
|
||||
applyTo: healingTypes.stress.id,
|
||||
value: {
|
||||
custom: {
|
||||
|
|
@ -289,7 +289,7 @@ export const defaultRestOptions = {
|
|||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -314,8 +314,8 @@ export const defaultRestOptions = {
|
|||
type: 'friendly'
|
||||
},
|
||||
damage: {
|
||||
parts: [
|
||||
{
|
||||
parts: {
|
||||
armor: {
|
||||
applyTo: healingTypes.armor.id,
|
||||
value: {
|
||||
custom: {
|
||||
|
|
@ -324,7 +324,7 @@ export const defaultRestOptions = {
|
|||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -348,8 +348,8 @@ export const defaultRestOptions = {
|
|||
type: 'self'
|
||||
},
|
||||
damage: {
|
||||
parts: [
|
||||
{
|
||||
parts: {
|
||||
hope: {
|
||||
applyTo: healingTypes.hope.id,
|
||||
value: {
|
||||
custom: {
|
||||
|
|
@ -358,7 +358,7 @@ export const defaultRestOptions = {
|
|||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
prepareWithFriends: {
|
||||
|
|
@ -372,8 +372,8 @@ export const defaultRestOptions = {
|
|||
type: 'self'
|
||||
},
|
||||
damage: {
|
||||
parts: [
|
||||
{
|
||||
parts: {
|
||||
hope: {
|
||||
applyTo: healingTypes.hope.id,
|
||||
value: {
|
||||
custom: {
|
||||
|
|
@ -382,7 +382,7 @@ export const defaultRestOptions = {
|
|||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -409,8 +409,8 @@ export const defaultRestOptions = {
|
|||
type: 'friendly'
|
||||
},
|
||||
damage: {
|
||||
parts: [
|
||||
{
|
||||
parts: {
|
||||
hitPoints: {
|
||||
applyTo: healingTypes.hitPoints.id,
|
||||
value: {
|
||||
custom: {
|
||||
|
|
@ -419,7 +419,7 @@ export const defaultRestOptions = {
|
|||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -443,8 +443,8 @@ export const defaultRestOptions = {
|
|||
type: 'self'
|
||||
},
|
||||
damage: {
|
||||
parts: [
|
||||
{
|
||||
parts: {
|
||||
stress: {
|
||||
applyTo: healingTypes.stress.id,
|
||||
value: {
|
||||
custom: {
|
||||
|
|
@ -453,7 +453,7 @@ export const defaultRestOptions = {
|
|||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -478,8 +478,8 @@ export const defaultRestOptions = {
|
|||
type: 'friendly'
|
||||
},
|
||||
damage: {
|
||||
parts: [
|
||||
{
|
||||
parts: {
|
||||
armor: {
|
||||
applyTo: healingTypes.armor.id,
|
||||
value: {
|
||||
custom: {
|
||||
|
|
@ -488,7 +488,7 @@ export const defaultRestOptions = {
|
|||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -512,8 +512,8 @@ export const defaultRestOptions = {
|
|||
type: 'self'
|
||||
},
|
||||
damage: {
|
||||
parts: [
|
||||
{
|
||||
parts: {
|
||||
hope: {
|
||||
applyTo: healingTypes.hope.id,
|
||||
value: {
|
||||
custom: {
|
||||
|
|
@ -522,7 +522,7 @@ export const defaultRestOptions = {
|
|||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
prepareWithFriends: {
|
||||
|
|
@ -536,8 +536,8 @@ export const defaultRestOptions = {
|
|||
type: 'self'
|
||||
},
|
||||
damage: {
|
||||
parts: [
|
||||
{
|
||||
parts: {
|
||||
hope: {
|
||||
applyTo: healingTypes.hope.id,
|
||||
value: {
|
||||
custom: {
|
||||
|
|
@ -546,7 +546,7 @@ export const defaultRestOptions = {
|
|||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -634,7 +634,95 @@ export const diceSetNumbers = {
|
|||
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);
|
||||
if (!system) {
|
||||
ui.notifications.error(
|
||||
|
|
@ -657,16 +745,33 @@ export const getDiceSoNicePreset = async (type, faces) => {
|
|||
appearance: {
|
||||
...system.appearance,
|
||||
...type
|
||||
}
|
||||
},
|
||||
sfx: getDiceSoNiceSFX(sfxOptions)
|
||||
};
|
||||
};
|
||||
|
||||
export const getDiceSoNicePresets = async (hopeFaces, fearFaces, advantageFaces = 'd6', disadvantageFaces = 'd6') => {
|
||||
const { diceSoNice } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance);
|
||||
export const getDiceSoNicePresets = async (
|
||||
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 {
|
||||
hope: await getDiceSoNicePreset(diceSoNice.hope, hopeFaces),
|
||||
fear: await getDiceSoNicePreset(diceSoNice.fear, fearFaces),
|
||||
hope: await getDiceSoNicePreset(diceSoNice.hope, hopeFaces, {
|
||||
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),
|
||||
disadvantage: await getDiceSoNicePreset(diceSoNice.disadvantage, disadvantageFaces)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
export const hooksConfig = {
|
||||
effectDisplayToggle: 'DHEffectDisplayToggle'
|
||||
effectDisplayToggle: 'DHEffectDisplayToggle',
|
||||
lockedTooltipDismissed: 'DHLockedTooltipDismissed'
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ export const armorFeatures = {
|
|||
type: 'hostile'
|
||||
},
|
||||
damage: {
|
||||
parts: [
|
||||
{
|
||||
parts: {
|
||||
stress: {
|
||||
applyTo: 'stress',
|
||||
value: {
|
||||
custom: {
|
||||
|
|
@ -24,7 +24,7 @@ export const armorFeatures = {
|
|||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -732,8 +732,8 @@ export const weaponFeatures = {
|
|||
type: 'hostile'
|
||||
},
|
||||
damage: {
|
||||
parts: [
|
||||
{
|
||||
parts: {
|
||||
stress: {
|
||||
applyTo: 'stress',
|
||||
value: {
|
||||
custom: {
|
||||
|
|
@ -742,7 +742,7 @@ export const weaponFeatures = {
|
|||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
@ -914,8 +914,8 @@ export const weaponFeatures = {
|
|||
type: 'self'
|
||||
},
|
||||
damage: {
|
||||
parts: [
|
||||
{
|
||||
parts: {
|
||||
hitPoints: {
|
||||
applyTo: 'hitPoints',
|
||||
value: {
|
||||
custom: {
|
||||
|
|
@ -924,7 +924,7 @@ export const weaponFeatures = {
|
|||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
88
module/config/resourceConfig.mjs
Normal file
88
module/config/resourceConfig.mjs
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/**
|
||||
* Full custom typing:
|
||||
* id
|
||||
* initial
|
||||
* max
|
||||
* reverse
|
||||
* label
|
||||
* images {
|
||||
* full { value, isIcon, noColorFilter }
|
||||
* empty { value, isIcon noColorFilter }
|
||||
* }
|
||||
*/
|
||||
|
||||
const characterBaseResources = Object.freeze({
|
||||
hitPoints: {
|
||||
id: 'hitPoints',
|
||||
initial: 0,
|
||||
max: 0,
|
||||
reverse: true,
|
||||
label: 'DAGGERHEART.GENERAL.HitPoints.plural',
|
||||
maxLabel: 'DAGGERHEART.ACTORS.Character.maxHPBonus'
|
||||
},
|
||||
stress: {
|
||||
id: 'stress',
|
||||
initial: 0,
|
||||
max: 6,
|
||||
reverse: true,
|
||||
label: 'DAGGERHEART.GENERAL.stress'
|
||||
},
|
||||
hope: {
|
||||
id: 'hope',
|
||||
initial: 2,
|
||||
reverse: false,
|
||||
label: 'DAGGERHEART.GENERAL.hope'
|
||||
}
|
||||
});
|
||||
|
||||
const adversaryBaseResources = Object.freeze({
|
||||
hitPoints: {
|
||||
id: 'hitPoints',
|
||||
initial: 0,
|
||||
max: 0,
|
||||
reverse: true,
|
||||
label: 'DAGGERHEART.GENERAL.HitPoints.plural',
|
||||
maxLabel: 'DAGGERHEART.ACTORS.Character.maxHPBonus'
|
||||
},
|
||||
stress: {
|
||||
id: 'stress',
|
||||
initial: 0,
|
||||
max: 0,
|
||||
reverse: true,
|
||||
label: 'DAGGERHEART.GENERAL.stress'
|
||||
}
|
||||
});
|
||||
|
||||
const companionBaseResources = Object.freeze({
|
||||
stress: {
|
||||
id: 'stress',
|
||||
initial: 0,
|
||||
max: 0,
|
||||
reverse: true,
|
||||
label: 'DAGGERHEART.GENERAL.stress'
|
||||
},
|
||||
hope: {
|
||||
id: 'hope',
|
||||
initial: 0,
|
||||
reverse: false,
|
||||
label: 'DAGGERHEART.GENERAL.hope'
|
||||
}
|
||||
});
|
||||
|
||||
export const character = {
|
||||
base: characterBaseResources,
|
||||
custom: {}, // module stuff goes here
|
||||
all: { ...characterBaseResources }
|
||||
};
|
||||
|
||||
export const adversary = {
|
||||
base: adversaryBaseResources,
|
||||
custom: {}, // module stuff goes here
|
||||
all: { ...adversaryBaseResources }
|
||||
};
|
||||
|
||||
export const companion = {
|
||||
base: companionBaseResources,
|
||||
custom: {}, // module stuff goes here
|
||||
all: { ...companionBaseResources }
|
||||
};
|
||||
|
|
@ -3,6 +3,10 @@ export const menu = {
|
|||
Name: 'GameSettingsAutomation',
|
||||
Icon: 'fa-solid fa-robot'
|
||||
},
|
||||
Metagaming: {
|
||||
Name: 'GameSettingsMetagaming',
|
||||
Icon: 'fa-solid fa-eye-low-vision'
|
||||
},
|
||||
Homebrew: {
|
||||
Name: 'GameSettingsHomebrew',
|
||||
Icon: 'fa-solid fa-flask-vial'
|
||||
|
|
@ -19,8 +23,10 @@ export const menu = {
|
|||
|
||||
export const gameSettings = {
|
||||
Automation: 'Automation',
|
||||
Metagaming: 'Metagaming',
|
||||
Homebrew: 'Homebrew',
|
||||
appearance: 'Appearance',
|
||||
GlobalOverrides: 'GlobalOverrides',
|
||||
variantRules: 'VariantRules',
|
||||
Resources: {
|
||||
Fear: 'ResourcesFear'
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import * as GENERAL from './generalConfig.mjs';
|
|||
import * as DOMAIN from './domainConfig.mjs';
|
||||
import * as ENCOUNTER from './encounterConfig.mjs';
|
||||
import * as ACTOR from './actorConfig.mjs';
|
||||
import * as RESOURCE from './resourceConfig.mjs';
|
||||
import * as ITEM from './itemConfig.mjs';
|
||||
import * as SETTINGS from './settingsConfig.mjs';
|
||||
import * as EFFECTS from './effectConfig.mjs';
|
||||
|
|
@ -19,6 +20,7 @@ export const SYSTEM = {
|
|||
GENERAL,
|
||||
DOMAIN,
|
||||
ACTOR,
|
||||
RESOURCE,
|
||||
ITEM,
|
||||
SETTINGS,
|
||||
EFFECTS,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import EffectAction from './effectAction.mjs';
|
|||
import HealingAction from './healingAction.mjs';
|
||||
import MacroAction from './macroAction.mjs';
|
||||
import SummonAction from './summonAction.mjs';
|
||||
import TransformAction from './transformAction.mjs';
|
||||
|
||||
export const actionsTypes = {
|
||||
base: BaseAction,
|
||||
|
|
@ -17,5 +18,6 @@ export const actionsTypes = {
|
|||
summon: SummonAction,
|
||||
effect: EffectAction,
|
||||
macro: MacroAction,
|
||||
beastform: BeastformAction
|
||||
beastform: BeastformAction,
|
||||
transform: TransformAction
|
||||
};
|
||||
|
|
|
|||
|
|
@ -26,23 +26,23 @@ export default class DHAttackAction extends DHDamageAction {
|
|||
return {
|
||||
value: {
|
||||
multiplier: 'prof',
|
||||
dice: this.item?.system?.attack.damage.parts[0].value.dice,
|
||||
bonus: this.item?.system?.attack.damage.parts[0].value.bonus ?? 0
|
||||
dice: this.item?.system?.attack.damage.parts.hitPoints.value.dice,
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
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';
|
||||
|
||||
return hitPointsPart.value.getFormula();
|
||||
}
|
||||
|
||||
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';
|
||||
|
||||
return hitPointsPart.valueAlt.getFormula();
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
async executeWorkflow(config) {
|
||||
for (const [key, part] of this.workflow) {
|
||||
if (Hooks.call(`${CONFIG.DH.id}.pre${key.capitalize()}Action`, this, config) === false) return;
|
||||
if ((await part.execute(config)) === false) return;
|
||||
if ((await part.execute(config)) === false) return false;
|
||||
if (Hooks.call(`${CONFIG.DH.id}.post${key.capitalize()}Action`, this, config) === false) return;
|
||||
}
|
||||
}
|
||||
|
|
@ -224,7 +224,9 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
}
|
||||
|
||||
// Execute the Action Worflow in order based of schema fields
|
||||
await this.executeWorkflow(config);
|
||||
const result = await this.executeWorkflow(config);
|
||||
if (result === false) return;
|
||||
|
||||
await config.resourceUpdates.updateResources();
|
||||
|
||||
if (Hooks.call(`${CONFIG.DH.id}.postUseAction`, this, config) === false) return;
|
||||
|
|
@ -352,11 +354,11 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
}
|
||||
|
||||
get hasDamage() {
|
||||
return this.damage?.parts?.length && this.type !== 'healing';
|
||||
return !foundry.utils.isEmpty(this.damage?.parts) && this.type !== 'healing';
|
||||
}
|
||||
|
||||
get hasHealing() {
|
||||
return this.damage?.parts?.length && this.type === 'healing';
|
||||
return !foundry.utils.isEmpty(this.damage?.parts) && this.type === 'healing';
|
||||
}
|
||||
|
||||
get hasSave() {
|
||||
|
|
@ -376,6 +378,15 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
|
||||
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 {
|
||||
|
|
|
|||
5
module/data/action/transformAction.mjs
Normal file
5
module/data/action/transformAction.mjs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import DHBaseAction from './baseAction.mjs';
|
||||
|
||||
export default class DHTransformAction extends DHBaseAction {
|
||||
static extraSchemas = [...super.extraSchemas, 'transform'];
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ import DHAdversarySettings from '../../applications/sheets-configs/adversary-set
|
|||
import { ActionField } from '../fields/actionField.mjs';
|
||||
import { commonActorRules } from './base.mjs';
|
||||
import DhCreature from './creature.mjs';
|
||||
import { resourceField, bonusField } from '../fields/actorField.mjs';
|
||||
import { bonusField } from '../fields/actorField.mjs';
|
||||
import { calculateExpectedValue, parseTermsFromSimpleFormula } from '../../helpers/utils.mjs';
|
||||
import { adversaryExpectedDamage, adversaryScalingData } from '../../config/actorConfig.mjs';
|
||||
|
||||
|
|
@ -65,10 +65,6 @@ export default class DhpAdversary extends DhCreature {
|
|||
label: 'DAGGERHEART.GENERAL.DamageThresholds.severeThreshold'
|
||||
})
|
||||
}),
|
||||
resources: new fields.SchemaField({
|
||||
hitPoints: resourceField(0, 0, 'DAGGERHEART.GENERAL.HitPoints.plural', true),
|
||||
stress: resourceField(0, 0, 'DAGGERHEART.GENERAL.stress', true)
|
||||
}),
|
||||
rules: new fields.SchemaField({
|
||||
...commonActorRules()
|
||||
}),
|
||||
|
|
@ -89,14 +85,14 @@ export default class DhpAdversary extends DhCreature {
|
|||
type: 'attack'
|
||||
},
|
||||
damage: {
|
||||
parts: [
|
||||
{
|
||||
parts: {
|
||||
hitPoints: {
|
||||
type: ['physical'],
|
||||
value: {
|
||||
multiplier: 'flat'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
|
@ -191,6 +187,7 @@ export default class DhpAdversary extends DhCreature {
|
|||
}
|
||||
|
||||
prepareDerivedData() {
|
||||
super.prepareDerivedData();
|
||||
this.attack.roll.isStandardAttack = true;
|
||||
}
|
||||
|
||||
|
|
@ -268,12 +265,12 @@ export default class DhpAdversary extends DhCreature {
|
|||
}
|
||||
|
||||
// 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
|
||||
for (const action of Object.values(item.system.actions)) {
|
||||
try {
|
||||
const result = this.#adjustActionDamage(action, { ...damageMeta, type: 'action' });
|
||||
if (!result) continue;
|
||||
|
||||
for (const { previousFormula, formula } of Object.values(result)) {
|
||||
const oldFormulaRegexp = new RegExp(
|
||||
previousFormula.replace(' ', '').replace('+', '(?:\\s)?\\+(?:\\s)?')
|
||||
|
|
@ -375,16 +372,14 @@ export default class DhpAdversary extends DhCreature {
|
|||
/**
|
||||
* Updates damage to reflect a specific value.
|
||||
* @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) {
|
||||
// The current algorithm only returns a value if there is a single damage part
|
||||
const hpDamageParts = action.damage.parts.filter(d => d.applyTo === 'hitPoints');
|
||||
if (hpDamageParts.length !== 1) throw new Error('incorrect number of hp parts');
|
||||
if (!action.damage?.parts.hitPoints) return null;
|
||||
|
||||
const result = {};
|
||||
for (const property of ['value', 'valueAlt']) {
|
||||
const data = hpDamageParts[0][property];
|
||||
const data = action.damage.parts.hitPoints[property];
|
||||
const previousFormula = data.custom.enabled
|
||||
? data.custom.formula
|
||||
: [data.flatMultiplier ? `${data.flatMultiplier}${data.dice}` : 0, data.bonus ?? 0]
|
||||
|
|
|
|||
|
|
@ -211,7 +211,7 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
|
|||
const textData = Object.keys(changes.system.resources).reduce((acc, key) => {
|
||||
const resource = changes.system.resources[key];
|
||||
if (resource.value !== undefined && resource.value !== this.resources[key].value) {
|
||||
acc.push(getScrollTextData(this.resources, resource, key));
|
||||
acc.push(getScrollTextData(this.parent, resource, key));
|
||||
}
|
||||
|
||||
return acc;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
|||
import DhLevelData from '../levelData.mjs';
|
||||
import { commonActorRules } from './base.mjs';
|
||||
import DhCreature from './creature.mjs';
|
||||
import { attributeField, resourceField, stressDamageReductionRule, bonusField } from '../fields/actorField.mjs';
|
||||
import { attributeField, stressDamageReductionRule, bonusField } from '../fields/actorField.mjs';
|
||||
import { ActionField } from '../fields/actionField.mjs';
|
||||
import DHCharacterSettings from '../../applications/sheets-configs/character-settings.mjs';
|
||||
|
||||
|
|
@ -27,28 +27,6 @@ export default class DhCharacter extends DhCreature {
|
|||
|
||||
return {
|
||||
...super.defineSchema(),
|
||||
resources: new fields.SchemaField({
|
||||
hitPoints: resourceField(
|
||||
0,
|
||||
0,
|
||||
'DAGGERHEART.GENERAL.HitPoints.plural',
|
||||
true,
|
||||
'DAGGERHEART.ACTORS.Character.maxHPBonus'
|
||||
),
|
||||
stress: resourceField(6, 0, 'DAGGERHEART.GENERAL.stress', true),
|
||||
hope: new fields.SchemaField(
|
||||
{
|
||||
value: new fields.NumberField({
|
||||
initial: 2,
|
||||
min: 0,
|
||||
integer: true,
|
||||
label: 'DAGGERHEART.GENERAL.hope'
|
||||
}),
|
||||
isReversed: new fields.BooleanField({ initial: false })
|
||||
},
|
||||
{ label: 'DAGGERHEART.GENERAL.hope' }
|
||||
)
|
||||
}),
|
||||
traits: new fields.SchemaField({
|
||||
agility: attributeField('DAGGERHEART.CONFIG.Traits.agility.name'),
|
||||
strength: attributeField('DAGGERHEART.CONFIG.Traits.strength.name'),
|
||||
|
|
@ -118,8 +96,8 @@ export default class DhCharacter extends DhCreature {
|
|||
trait: 'strength'
|
||||
},
|
||||
damage: {
|
||||
parts: [
|
||||
{
|
||||
parts: {
|
||||
hitPoints: {
|
||||
type: ['physical'],
|
||||
value: {
|
||||
custom: {
|
||||
|
|
@ -128,7 +106,7 @@ export default class DhCharacter extends DhCreature {
|
|||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
|
@ -609,6 +587,7 @@ export default class DhCharacter extends DhCreature {
|
|||
}
|
||||
|
||||
prepareBaseData() {
|
||||
super.prepareBaseData();
|
||||
this.evasion += this.class.value?.system?.evasion ?? 0;
|
||||
|
||||
const currentLevel = this.levelData.level.current;
|
||||
|
|
@ -680,6 +659,7 @@ export default class DhCharacter extends DhCreature {
|
|||
}
|
||||
|
||||
prepareDerivedData() {
|
||||
super.prepareDerivedData();
|
||||
let baseHope = this.resources.hope.value;
|
||||
if (this.companion) {
|
||||
for (let levelKey in this.companion.system.levelData.levelups) {
|
||||
|
|
@ -699,12 +679,13 @@ export default class DhCharacter extends DhCreature {
|
|||
this.attack.roll.trait = this.rules.attack.roll.trait ?? this.attack.roll.trait;
|
||||
|
||||
this.resources.armor = {
|
||||
label: 'DAGGERHEART.GENERAL.armor',
|
||||
value: this.armor?.system?.marks?.value ?? 0,
|
||||
max: this.armorScore,
|
||||
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() {
|
||||
|
|
@ -740,7 +721,8 @@ export default class DhCharacter extends DhCreature {
|
|||
const newHopeMax = this.system.resources.hope.max + diff;
|
||||
const newHopeValue = Math.min(newHopeMax, 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,
|
||||
value: changes.system.resources.hope.value + newHopeValue
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
|||
import { ActionField } from '../fields/actionField.mjs';
|
||||
import { adjustDice, adjustRange } from '../../helpers/utils.mjs';
|
||||
import DHCompanionSettings from '../../applications/sheets-configs/companion-settings.mjs';
|
||||
import { resourceField, bonusField } from '../fields/actorField.mjs';
|
||||
import { bonusField } from '../fields/actorField.mjs';
|
||||
|
||||
export default class DhCompanion extends DhCreature {
|
||||
static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Companion'];
|
||||
|
|
@ -26,10 +26,6 @@ export default class DhCompanion extends DhCreature {
|
|||
return {
|
||||
...super.defineSchema(),
|
||||
partner: new ForeignDocumentUUIDField({ type: 'Actor' }),
|
||||
resources: new fields.SchemaField({
|
||||
stress: resourceField(3, 0, 'DAGGERHEART.GENERAL.stress', true),
|
||||
hope: new fields.NumberField({ initial: 0, integer: true, label: 'DAGGERHEART.GENERAL.hope' })
|
||||
}),
|
||||
evasion: new fields.NumberField({
|
||||
required: true,
|
||||
min: 1,
|
||||
|
|
@ -85,15 +81,15 @@ export default class DhCompanion extends DhCreature {
|
|||
bonus: 0
|
||||
},
|
||||
damage: {
|
||||
parts: [
|
||||
{
|
||||
parts: {
|
||||
hitPoints: {
|
||||
type: ['physical'],
|
||||
value: {
|
||||
dice: 'd6',
|
||||
multiplier: 'prof'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
|
@ -127,6 +123,7 @@ export default class DhCompanion extends DhCreature {
|
|||
}
|
||||
|
||||
prepareBaseData() {
|
||||
super.prepareBaseData();
|
||||
this.attack.roll.bonus = this.partner?.system?.spellcastModifier ?? 0;
|
||||
|
||||
for (let levelKey in this.levelData.levelups) {
|
||||
|
|
@ -138,7 +135,9 @@ export default class DhCompanion extends DhCreature {
|
|||
break;
|
||||
case 'vicious':
|
||||
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 {
|
||||
this.attack.range = adjustRange(this.attack.range).id;
|
||||
}
|
||||
|
|
@ -161,6 +160,7 @@ export default class DhCompanion extends DhCreature {
|
|||
}
|
||||
|
||||
prepareDerivedData() {
|
||||
super.prepareDerivedData();
|
||||
/* Partner Related Setup */
|
||||
if (this.partner) {
|
||||
this.levelData.level.changed = this.partner.system.levelData.level.current;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { ResourcesField } from '../fields/actorField.mjs';
|
||||
import BaseDataActor from './base.mjs';
|
||||
|
||||
export default class DhCreature extends BaseDataActor {
|
||||
|
|
@ -7,6 +8,7 @@ export default class DhCreature extends BaseDataActor {
|
|||
|
||||
return {
|
||||
...super.defineSchema(),
|
||||
resources: new ResourcesField(this.metadata.type),
|
||||
advantageSources: new fields.ArrayField(new fields.StringField(), {
|
||||
label: 'DAGGERHEART.ACTORS.Character.advantageSources.label',
|
||||
hint: 'DAGGERHEART.ACTORS.Character.advantageSources.hint'
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ export default class DhEnvironment extends BaseDataActor {
|
|||
potentialAdversaries: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
label: new fields.StringField(),
|
||||
adversaries: new ForeignDocumentUUIDArrayField({ type: 'Actor' }, { required: false, initial: [] })
|
||||
adversaries: new ForeignDocumentUUIDArrayField({ type: 'Actor' })
|
||||
})
|
||||
),
|
||||
notes: new fields.HTMLField()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
export { ActionCollection } from './actionField.mjs';
|
||||
export { default as IterableTypedObjectField } from './iterableTypedObjectField.mjs';
|
||||
export { default as FormulaField } from './formulaField.mjs';
|
||||
export { default as ForeignDocumentUUIDField } from './foreignDocumentUUIDField.mjs';
|
||||
export { default as ForeignDocumentUUIDArrayField } from './foreignDocumentUUIDArrayField.mjs';
|
||||
|
|
|
|||
|
|
@ -10,3 +10,4 @@ export { default as DamageField } from './damageField.mjs';
|
|||
export { default as RollField } from './rollField.mjs';
|
||||
export { default as MacroField } from './macroField.mjs';
|
||||
export { default as SummonField } from './summonField.mjs';
|
||||
export { default as TransformField } from './transformField.mjs';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import FormulaField from '../formulaField.mjs';
|
||||
import { setsEqual } from '../../../helpers/utils.mjs';
|
||||
import IterableTypedObjectField from '../iterableTypedObjectField.mjs';
|
||||
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
|
|
@ -12,7 +13,7 @@ export default class DamageField extends fields.SchemaField {
|
|||
/** @inheritDoc */
|
||||
constructor(options, context = {}) {
|
||||
const damageFields = {
|
||||
parts: new fields.ArrayField(new fields.EmbeddedDataField(DHDamageData)),
|
||||
parts: new IterableTypedObjectField(DHDamageData),
|
||||
includeBase: new fields.BooleanField({
|
||||
initial: false,
|
||||
label: 'DAGGERHEART.ACTIONS.Settings.includeBase.label'
|
||||
|
|
|
|||
103
module/data/fields/action/transformField.mjs
Normal file
103
module/data/fields/action/transformField.mjs
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
const fields = foundry.data.fields;
|
||||
|
||||
export default class DHSummonField extends fields.SchemaField {
|
||||
/**
|
||||
* Action Workflow order
|
||||
*/
|
||||
static order = 130;
|
||||
|
||||
constructor(options = {}, context = {}) {
|
||||
const transformFields = {
|
||||
actorUUID: new fields.DocumentUUIDField({
|
||||
type: 'Actor',
|
||||
required: true
|
||||
}),
|
||||
resourceRefresh: new fields.SchemaField({
|
||||
hitPoints: new fields.BooleanField({ initial: true }),
|
||||
stress: new fields.BooleanField({ initial: true })
|
||||
})
|
||||
};
|
||||
super(transformFields, options, context);
|
||||
}
|
||||
|
||||
static async execute() {
|
||||
if (!this.transform.actorUUID) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.transform.noTransformActor'));
|
||||
return false;
|
||||
}
|
||||
|
||||
const baseActor = await foundry.utils.fromUuid(this.transform.actorUUID);
|
||||
if (!baseActor) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.transform.transformActorMissing'));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!canvas.scene) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.transform.canvasError'));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.actor.prototypeToken.actorLink) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.transform.actorLinkError'));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.actor.token) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.transform.prototypeError'));
|
||||
return false;
|
||||
}
|
||||
|
||||
const actor = await DHSummonField.getWorldActor(baseActor);
|
||||
const tokenSizes = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes;
|
||||
const tokenSize = actor?.system.metadata.usesSize ? tokenSizes[actor.system.size] : actor.prototypeToken.width;
|
||||
|
||||
await this.actor.token.update(
|
||||
{ ...actor.prototypeToken.toJSON(), actorId: actor.id, width: tokenSize, height: tokenSize },
|
||||
{ diff: false, recursive: false, noHook: true }
|
||||
);
|
||||
|
||||
if (this.actor.token.combatant) {
|
||||
this.actor.token.combatant.update({ actorId: actor.id, img: actor.prototypeToken.texture.src });
|
||||
}
|
||||
|
||||
const marks = { hitPoints: 0, stress: 0 };
|
||||
if (!this.transform.resourceRefresh.hitPoints) {
|
||||
marks.hitPoints = Math.min(
|
||||
this.actor.system.resources.hitPoints.value,
|
||||
this.actor.token.actor.system.resources.hitPoints.max - 1
|
||||
);
|
||||
}
|
||||
if (!this.transform.resourceRefresh.stress) {
|
||||
marks.stress = Math.min(
|
||||
this.actor.system.resources.stress.value,
|
||||
this.actor.token.actor.system.resources.stress.max - 1
|
||||
);
|
||||
}
|
||||
if (marks.hitPoints || marks.stress) {
|
||||
this.actor.token.actor.update({
|
||||
'system.resources': {
|
||||
hitPoints: { value: marks.hitPoints },
|
||||
stress: { value: marks.stress }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const prevPosition = { ...this.actor.sheet.position };
|
||||
this.actor.sheet.close();
|
||||
this.actor.token.actor.sheet.render({ force: true, position: prevPosition });
|
||||
}
|
||||
|
||||
/* Check for any available instances of the actor present in the world, or create a world actor based on compendium */
|
||||
static async getWorldActor(baseActor) {
|
||||
if (!baseActor.inCompendium) return baseActor;
|
||||
|
||||
const dataType = game.system.api.data.actors[`Dh${baseActor.type.capitalize()}`];
|
||||
if (dataType && baseActor.img === dataType.DEFAULT_ICON) {
|
||||
const worldActorCopy = game.actors.find(x => x.name === baseActor.name);
|
||||
if (worldActorCopy) return worldActorCopy;
|
||||
}
|
||||
|
||||
const worldActor = await game.system.api.documents.DhpActor.create(baseActor.toObject());
|
||||
return worldActor;
|
||||
}
|
||||
}
|
||||
|
|
@ -87,10 +87,10 @@ export class ActionField extends foundry.data.fields.ObjectField {
|
|||
/* -------------------------------------------- */
|
||||
|
||||
/** @override */
|
||||
_cleanType(value, options) {
|
||||
_cleanType(value, options, _state) {
|
||||
if (!(typeof value === 'object')) value = {};
|
||||
const cls = this.getModel(value);
|
||||
if (cls) return cls.cleanData(value, options);
|
||||
if (cls) return cls.cleanData(value, options, _state);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,22 +6,6 @@ const attributeField = label =>
|
|||
tierMarked: new fields.BooleanField({ initial: false })
|
||||
});
|
||||
|
||||
const resourceField = (max = 0, initial = 0, label, reverse = false, maxLabel) =>
|
||||
new fields.SchemaField(
|
||||
{
|
||||
value: new fields.NumberField({ initial: initial, min: 0, integer: true, label }),
|
||||
max: new fields.NumberField({
|
||||
initial: max,
|
||||
integer: true,
|
||||
label:
|
||||
maxLabel ??
|
||||
game.i18n.format('DAGGERHEART.GENERAL.maxWithThing', { thing: game.i18n.localize(label) })
|
||||
}),
|
||||
isReversed: new fields.BooleanField({ initial: reverse })
|
||||
},
|
||||
{ label }
|
||||
);
|
||||
|
||||
const stressDamageReductionRule = localizationPath =>
|
||||
new fields.SchemaField({
|
||||
cost: new fields.NumberField({
|
||||
|
|
@ -37,4 +21,67 @@ const bonusField = label =>
|
|||
dice: new fields.ArrayField(new fields.StringField(), { label: `${game.i18n.localize(label)} Dice` })
|
||||
});
|
||||
|
||||
export { attributeField, resourceField, stressDamageReductionRule, bonusField };
|
||||
/**
|
||||
* Field used for actor resources. It is a resource that validates dynamically based on the config.
|
||||
* Because "max" may be defined during runtime, we don't attempt to clamp the maximum value.
|
||||
*/
|
||||
class ResourcesField extends fields.TypedObjectField {
|
||||
constructor(actorType) {
|
||||
super(
|
||||
new fields.SchemaField({
|
||||
value: new fields.NumberField({ min: 0, initial: 0, integer: true }),
|
||||
// Some resources allow changing max. A null max means its the default
|
||||
max: new fields.NumberField({ initial: null, integer: true, nullable: true })
|
||||
})
|
||||
);
|
||||
this.actorType = actorType;
|
||||
}
|
||||
|
||||
getInitialValue() {
|
||||
const resources = CONFIG.DH.RESOURCE[this.actorType].all;
|
||||
return Object.values(resources).reduce((result, resource) => {
|
||||
result[resource.id] = {
|
||||
value: resource.initial,
|
||||
max: null
|
||||
};
|
||||
return result;
|
||||
}, {});
|
||||
}
|
||||
|
||||
_validateKey(key) {
|
||||
return key in CONFIG.DH.RESOURCE[this.actorType].all;
|
||||
}
|
||||
|
||||
_cleanType(value, options, _state) {
|
||||
value = super._cleanType(value, options, _state);
|
||||
|
||||
// If not partial, ensure all data exists
|
||||
if (!options.partial) {
|
||||
value = foundry.utils.mergeObject(this.getInitialValue(), value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/** Initializes the original source data, returning prepared data */
|
||||
initialize(...args) {
|
||||
const data = super.initialize(...args);
|
||||
const resources = CONFIG.DH.RESOURCE[this.actorType].all;
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
// TypedObjectField only calls _validateKey when persisting, so we also call it here
|
||||
if (!this._validateKey(key)) {
|
||||
delete value[key];
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add basic prepared data.
|
||||
const resource = resources[key];
|
||||
value.label = resource.label;
|
||||
value.isReversed = resources[key].reverse;
|
||||
value.max = typeof resource.max === 'number' ? (value.max ?? resource.max) : null;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
export { attributeField, ResourcesField, stressDamageReductionRule, bonusField };
|
||||
|
|
|
|||
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);
|
||||
}
|
||||
};
|
||||
|
|
@ -224,7 +224,7 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
|||
const armorChanged =
|
||||
changed.system?.marks?.value !== undefined && changed.system.marks.value !== this.marks.value;
|
||||
if (armorChanged && autoSettings.resourceScrollTexts && this.parent.parent?.type === 'character') {
|
||||
const armorData = getScrollTextData(this.parent.parent.system.resources, changed.system.marks, 'armor');
|
||||
const armorData = getScrollTextData(this.parent.parent, changed.system.marks, 'armor');
|
||||
options.scrollingTextData = [armorData];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -63,15 +63,15 @@ export default class DHWeapon extends AttachableItem {
|
|||
type: 'attack'
|
||||
},
|
||||
damage: {
|
||||
parts: [
|
||||
{
|
||||
parts: {
|
||||
hitPoints: {
|
||||
type: ['physical'],
|
||||
value: {
|
||||
multiplier: 'prof',
|
||||
dice: 'd8'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
|
@ -112,24 +112,14 @@ export default class DHWeapon extends AttachableItem {
|
|||
async getDescriptionData() {
|
||||
const baseDescription = this.description;
|
||||
|
||||
const tier = game.i18n.localize(`DAGGERHEART.GENERAL.Tiers.${this.tier}`);
|
||||
const trait = game.i18n.localize(CONFIG.DH.ACTOR.abilities[this.attack.roll.trait].label);
|
||||
const range = game.i18n.localize(`DAGGERHEART.CONFIG.Range.${this.attack.range}.name`);
|
||||
const damage = Roll.replaceFormulaData(this.attack.damageFormula, this.parent.parent ?? this.parent);
|
||||
const burden = game.i18n.localize(CONFIG.DH.GENERAL.burden[this.burden].label);
|
||||
|
||||
const allFeatures = CONFIG.DH.ITEM.allWeaponFeatures();
|
||||
const features = this.weaponFeatures.map(x => allFeatures[x.value]).filter(x => x);
|
||||
|
||||
const prefix = await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/sheets/items/weapon/description.hbs',
|
||||
{
|
||||
features,
|
||||
tier,
|
||||
trait,
|
||||
range,
|
||||
damage,
|
||||
burden
|
||||
item: this,
|
||||
features
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,16 @@
|
|||
export default class DhAppearance extends foundry.abstract.DataModel {
|
||||
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() {
|
||||
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 }),
|
||||
material: new StringField({ initial: 'metal', 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 {
|
||||
|
|
@ -30,7 +43,10 @@ export default class DhAppearance extends foundry.abstract.DataModel {
|
|||
hope: diceStyle({ fg: '#ffffff', bg: '#ffe760', outline: '#000000', edge: '#ffffff' }),
|
||||
fear: diceStyle({ fg: '#000000', bg: '#0032b1', outline: '#ffffff', edge: '#000000' }),
|
||||
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(),
|
||||
extendAdversaryDescriptions: new BooleanField(),
|
||||
|
|
@ -65,4 +81,48 @@ export default class DhAppearance extends foundry.abstract.DataModel {
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -145,6 +145,16 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
|
|||
description: new fields.StringField()
|
||||
})
|
||||
),
|
||||
resources: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
resources: new fields.TypedObjectField(new fields.EmbeddedDataField(Resource))
|
||||
}),
|
||||
{
|
||||
initial: {
|
||||
character: { resources: {} }
|
||||
}
|
||||
}
|
||||
),
|
||||
itemFeatures: new fields.SchemaField({
|
||||
weaponFeatures: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
|
|
@ -185,4 +195,117 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
|
|||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
/** Invoked by the setting when data changes */
|
||||
handleChange() {
|
||||
if (this.maxFear) {
|
||||
if (ui.resources) ui.resources.render({ force: true });
|
||||
}
|
||||
|
||||
this.refreshConfig();
|
||||
this.#resetActors();
|
||||
}
|
||||
|
||||
/** Update config values based on homebrew data. Make sure the references don't change */
|
||||
refreshConfig() {
|
||||
for (const [actorType, actorData] of Object.entries(this.resources)) {
|
||||
const config = CONFIG.DH.RESOURCE[actorType];
|
||||
for (const key of Object.keys(config.all)) {
|
||||
delete config.all[key];
|
||||
}
|
||||
Object.assign(config.all, {
|
||||
...Object.entries(actorData.resources).reduce((result, [key, value]) => {
|
||||
result[key] = value.toObject();
|
||||
result[key].id = key;
|
||||
return result;
|
||||
}, {}),
|
||||
...config.custom,
|
||||
...config.base
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers a reset and non-forced re-render on all given actors (if given)
|
||||
* or all world actors and actors in all scenes to show immediate results for a changed setting.
|
||||
*/
|
||||
#resetActors() {
|
||||
const actors = new Set(
|
||||
[
|
||||
game.actors.contents,
|
||||
game.scenes.contents.flatMap(s => s.tokens.contents).flatMap(t => t.actor ?? [])
|
||||
].flat()
|
||||
);
|
||||
for (const actor of actors) {
|
||||
for (const app of Object.values(actor.apps)) {
|
||||
for (const element of app.element?.querySelectorAll('prose-mirror.active')) {
|
||||
element.open = false; // This triggers a save
|
||||
}
|
||||
}
|
||||
|
||||
actor.reset();
|
||||
actor.render();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class Resource extends foundry.abstract.DataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
initial: new fields.NumberField({
|
||||
required: true,
|
||||
integer: true,
|
||||
initial: 0,
|
||||
min: 0,
|
||||
label: 'DAGGERHEART.GENERAL.initial'
|
||||
}),
|
||||
max: new fields.NumberField({
|
||||
nullable: true,
|
||||
initial: null,
|
||||
min: 0,
|
||||
label: 'DAGGERHEART.GENERAL.max'
|
||||
}),
|
||||
label: new fields.StringField({ label: 'DAGGERHEART.GENERAL.label' }),
|
||||
images: new fields.SchemaField({
|
||||
full: imageIconField('fa solid fa-circle'),
|
||||
empty: imageIconField('fa-regular fa-circle')
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
static getDefaultResourceData = label => {
|
||||
const images = Resource.schema.fields.images.getInitialValue();
|
||||
return {
|
||||
initial: 0,
|
||||
max: 0,
|
||||
label: label ?? '',
|
||||
images
|
||||
};
|
||||
};
|
||||
|
||||
static getDefaultImageData = imageKey => {
|
||||
return Resource.schema.fields.images.fields[imageKey].getInitialValue();
|
||||
};
|
||||
}
|
||||
|
||||
const imageIconField = defaultValue =>
|
||||
new foundry.data.fields.SchemaField(
|
||||
{
|
||||
value: new foundry.data.fields.StringField({
|
||||
initial: defaultValue,
|
||||
label: 'DAGGERHEART.SETTINGS.Homebrew.FIELDS.resources.resources.value.label'
|
||||
}),
|
||||
isIcon: new foundry.data.fields.BooleanField({
|
||||
required: true,
|
||||
initial: true,
|
||||
label: 'DAGGERHEART.SETTINGS.Homebrew.FIELDS.resources.resources.isIcon.label'
|
||||
}),
|
||||
noColorFilter: new foundry.data.fields.BooleanField({
|
||||
required: true,
|
||||
initial: false,
|
||||
label: 'DAGGERHEART.SETTINGS.Homebrew.FIELDS.resources.resources.noColorFilter.label'
|
||||
})
|
||||
},
|
||||
{ required: true }
|
||||
);
|
||||
|
|
|
|||
12
module/data/settings/Metagaming.mjs
Normal file
12
module/data/settings/Metagaming.mjs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
export default class DhMetagaming extends foundry.abstract.DataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
hideObserverPermissionInChat: new fields.BooleanField({
|
||||
initial: false,
|
||||
label: 'DAGGERHEART.SETTINGS.Metagaming.FIELDS.hideObserverPermissionInChat.label',
|
||||
hint: 'DAGGERHEART.SETTINGS.Metagaming.FIELDS.hideObserverPermissionInChat.hint'
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
export { default as DhAppearance } from './Appearance.mjs';
|
||||
export { default as DhAutomation } from './Automation.mjs';
|
||||
export { default as DhHomebrew } from './Homebrew.mjs';
|
||||
export { default as DhMetagaming } from './Metagaming.mjs';
|
||||
export { default as DhVariantRules } from './VariantRules.mjs';
|
||||
export { default as DhGlobalOverrides } from './GlobalOverrides.mjs';
|
||||
|
|
|
|||
|
|
@ -140,8 +140,14 @@ export default class DHRoll extends Roll {
|
|||
/** @inheritDoc */
|
||||
async render({ flavor, template = this.constructor.CHAT_TEMPLATE, isPrivate = false, ...options } = {}) {
|
||||
if (!this._evaluated) return;
|
||||
|
||||
const metagamingSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Metagaming);
|
||||
const chatData = await this._prepareChatRenderContext({ flavor, isPrivate, ...options });
|
||||
return foundry.applications.handlebars.renderTemplate(template, chatData);
|
||||
return foundry.applications.handlebars.renderTemplate(template, {
|
||||
...chatData,
|
||||
parent: chatData.parent,
|
||||
metagamingSettings
|
||||
});
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
|
|
|
|||
|
|
@ -378,6 +378,8 @@ export default class DualityRoll extends D20Roll {
|
|||
let parsedRoll = game.system.api.dice.DualityRoll.fromData({ ...rollString, evaluated: false });
|
||||
const term = parsedRoll.terms[target.dataset.dieIndex];
|
||||
await term.reroll(`/r1=${term.total}`);
|
||||
const result = await parsedRoll.evaluate();
|
||||
|
||||
if (game.modules.get('dice-so-nice')?.active) {
|
||||
const diceSoNiceRoll = {
|
||||
_evaluated: true,
|
||||
|
|
@ -391,7 +393,7 @@ export default class DualityRoll extends D20Roll {
|
|||
options: { appearance: {} }
|
||||
};
|
||||
|
||||
const diceSoNicePresets = await getDiceSoNicePresets(`d${term._faces}`, `d${term._faces}`);
|
||||
const diceSoNicePresets = await getDiceSoNicePresets(result, `d${term._faces}`, `d${term._faces}`);
|
||||
const type = target.dataset.type;
|
||||
if (diceSoNicePresets[type]) {
|
||||
diceSoNiceRoll.dice[0].options = diceSoNicePresets[type];
|
||||
|
|
@ -400,8 +402,6 @@ export default class DualityRoll extends D20Roll {
|
|||
await game.dice3d.showForRoll(diceSoNiceRoll, game.user, true);
|
||||
}
|
||||
|
||||
await parsedRoll.evaluate();
|
||||
|
||||
const newRoll = game.system.api.dice.DualityRoll.postEvaluate(parsedRoll, {
|
||||
targets: message.system.targets,
|
||||
roll: {
|
||||
|
|
|
|||
|
|
@ -68,8 +68,11 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
|||
document = fromUuidSync(uuid);
|
||||
if (!document) return;
|
||||
|
||||
e.setAttribute('data-view-perm', document.testUserPermission(game.user, 'OBSERVER'));
|
||||
e.setAttribute('data-use-perm', document.testUserPermission(game.user, 'OWNER'));
|
||||
|
||||
const settings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Metagaming);
|
||||
if (settings.hideObserverPermissionInChat)
|
||||
e.setAttribute('data-view-perm', document.testUserPermission(game.user, 'OBSERVER'));
|
||||
});
|
||||
|
||||
if (this.isContentVisible) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { AdversaryBPPerEncounter, BaseBPPerEncounter } from '../config/encounter
|
|||
export default class DhTooltipManager extends foundry.helpers.interaction.TooltipManager {
|
||||
#wide = false;
|
||||
#bordered = false;
|
||||
#active = false;
|
||||
|
||||
async activate(element, options = {}) {
|
||||
const { TextEditor } = foundry.applications.ux;
|
||||
|
|
@ -168,7 +169,100 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
|
|||
}
|
||||
}
|
||||
|
||||
super.activate(element, { ...options, html: html });
|
||||
this.baseActivate(element, { ...options, html: html });
|
||||
}
|
||||
|
||||
/* Need to pass more options to _setAnchor, so have to copy whole foundry method >_< */
|
||||
async baseActivate(element, options) {
|
||||
let { text, direction, cssClass, locked = false, html, content } = options;
|
||||
if (content && !html) {
|
||||
foundry.utils.logCompatibilityWarning(
|
||||
'The content option has been deprecated in favor of the html option',
|
||||
{ since: 13, until: 15, once: true }
|
||||
);
|
||||
html = content;
|
||||
}
|
||||
if (text && html) throw new Error('Cannot provide both text and html options to TooltipManager#activate.');
|
||||
// Deactivate currently active element
|
||||
this.deactivate();
|
||||
// Check if the element still exists in the DOM.
|
||||
if (!document.body.contains(element)) return;
|
||||
// Mark the new element as active
|
||||
this.#active = true;
|
||||
this.element = element;
|
||||
element.setAttribute('aria-describedby', 'tooltip');
|
||||
html ||= element.dataset.tooltipHtml;
|
||||
if (html) {
|
||||
if (typeof html === 'string') this.tooltip.innerHTML = foundry.utils.cleanHTML(html);
|
||||
else {
|
||||
this.tooltip.innerHTML = ''; // Clear existing HTML
|
||||
this.tooltip.appendChild(html);
|
||||
}
|
||||
} else {
|
||||
text ||= element.dataset.tooltipText;
|
||||
if (text) this.tooltip.textContent = text;
|
||||
else {
|
||||
text = element.dataset.tooltip;
|
||||
// Localized message should be safe
|
||||
if (game.i18n.has(text)) this.tooltip.innerHTML = game.i18n.localize(text);
|
||||
else this.tooltip.innerHTML = foundry.utils.cleanHTML(text);
|
||||
}
|
||||
}
|
||||
|
||||
// Activate display of the tooltip
|
||||
this.tooltip.removeAttribute('class');
|
||||
this.tooltip.classList.add('active', 'themed', 'theme-dark');
|
||||
this.tooltip.showPopover();
|
||||
cssClass ??= element.closest('[data-tooltip-class]')?.dataset.tooltipClass;
|
||||
if (cssClass) this.tooltip.classList.add(...cssClass.split(' '));
|
||||
|
||||
// Set tooltip position
|
||||
direction ??= element.closest('[data-tooltip-direction]')?.dataset.tooltipDirection;
|
||||
if (!direction) direction = this._determineDirection();
|
||||
this._setAnchor(direction, options);
|
||||
|
||||
if (locked || element.dataset.hasOwnProperty('locked')) this.lockTooltip();
|
||||
}
|
||||
|
||||
_setAnchor(direction, options) {
|
||||
const directions = this.constructor.TOOLTIP_DIRECTIONS;
|
||||
const pad = this.constructor.TOOLTIP_MARGIN_PX;
|
||||
const pos = this.element.getBoundingClientRect();
|
||||
|
||||
const { innerHeight, innerWidth } = this.tooltip.ownerDocument.defaultView;
|
||||
const tooltipPadding = 16;
|
||||
const horizontalOffset = options.noOffset ? tooltipPadding : this.tooltip.offsetWidth / 2 - pos.width / 2;
|
||||
const verticalOffset = options.noOffset ? tooltipPadding : this.tooltip.offsetHeight / 2 - pos.height / 2;
|
||||
|
||||
const style = {};
|
||||
switch (direction) {
|
||||
case directions.DOWN:
|
||||
style.textAlign = 'center';
|
||||
style.left = pos.left - horizontalOffset;
|
||||
style.top = pos.bottom + pad;
|
||||
break;
|
||||
case directions.LEFT:
|
||||
style.textAlign = 'left';
|
||||
style.right = innerWidth - pos.left + pad;
|
||||
style.top = pos.top - verticalOffset;
|
||||
break;
|
||||
case directions.RIGHT:
|
||||
style.textAlign = 'right';
|
||||
style.left = pos.right + pad;
|
||||
style.top = pos.top - verticalOffset;
|
||||
break;
|
||||
case directions.UP:
|
||||
style.textAlign = 'center';
|
||||
style.left = pos.left - horizontalOffset;
|
||||
style.bottom = innerHeight - pos.top + pad;
|
||||
break;
|
||||
case directions.CENTER:
|
||||
style.textAlign = 'center';
|
||||
style.left = pos.left - horizontalOffset;
|
||||
style.top = pos.top - verticalOffset;
|
||||
break;
|
||||
}
|
||||
return this._setStyle(style);
|
||||
}
|
||||
|
||||
_determineItemTooltipDirection(element, prefered = this.constructor.TOOLTIP_DIRECTIONS.LEFT) {
|
||||
|
|
@ -270,6 +364,12 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
|
|||
return clone;
|
||||
}
|
||||
|
||||
/**@inheritdoc */
|
||||
dismissLockedTooltips() {
|
||||
super.dismissLockedTooltips();
|
||||
Hooks.callAll(CONFIG.DH.HOOKS.hooksConfig.lockedTooltipDismissed);
|
||||
}
|
||||
|
||||
/** Get HTML for Battlepoints tooltip */
|
||||
async getBattlepointHTML(combatId) {
|
||||
const combat = game.combats.get(combatId);
|
||||
|
|
|
|||
|
|
@ -49,9 +49,7 @@ export default class RegisterHandlebarsHelpers {
|
|||
}
|
||||
|
||||
static damageSymbols(damageParts) {
|
||||
const symbols = [...new Set(damageParts.reduce((a, c) => a.concat([...c.type]), []))].map(
|
||||
p => CONFIG.DH.GENERAL.damageTypes[p].icon
|
||||
);
|
||||
const symbols = [...new Set(damageParts.map(x => x.type))].map(p => CONFIG.DH.GENERAL.damageTypes[p].icon);
|
||||
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) => {
|
||||
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[1].options = diceSoNicePresets.fear;
|
||||
if (rollResult.dice[2] && advantageState) {
|
||||
|
|
@ -378,17 +384,18 @@ export const arraysEqual = (a, b) =>
|
|||
|
||||
export const setsEqual = (a, b) => a.size === b.size && [...a].every(value => b.has(value));
|
||||
|
||||
export function getScrollTextData(resources, resource, key) {
|
||||
const { reversed, label } = CONFIG.DH.ACTOR.scrollingTextResource[key];
|
||||
export function getScrollTextData(actor, resource, key) {
|
||||
const { BOTTOM, TOP } = CONST.TEXT_ANCHOR_POINTS;
|
||||
|
||||
const resources = actor.system.resources;
|
||||
const increased = resources[key].value < resource.value;
|
||||
const value = -1 * (resources[key].value - resource.value);
|
||||
const { label, isReversed } = resources[key];
|
||||
|
||||
const text = `${game.i18n.localize(label)} ${value.signedString()}`;
|
||||
|
||||
const stroke = increased ? (reversed ? 0xffffff : 0x000000) : reversed ? 0x000000 : 0xffffff;
|
||||
const fill = increased ? (reversed ? 0x0032b1 : 0xffe760) : reversed ? 0xffe760 : 0x0032b1;
|
||||
const direction = increased ? (reversed ? BOTTOM : TOP) : reversed ? TOP : BOTTOM;
|
||||
const stroke = increased ? (isReversed ? 0xffffff : 0x000000) : isReversed ? 0x000000 : 0xffffff;
|
||||
const fill = increased ? (isReversed ? 0x0032b1 : 0xffe760) : isReversed ? 0xffe760 : 0x0032b1;
|
||||
const direction = increased ? (isReversed ? BOTTOM : TOP) : isReversed ? TOP : BOTTOM;
|
||||
|
||||
return { text, stroke, fill, direction };
|
||||
}
|
||||
|
|
@ -722,3 +729,16 @@ export async function RefreshFeatures(
|
|||
|
||||
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;
|
||||
}, []);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,9 +17,10 @@ export const preloadHandlebarsTemplates = async function () {
|
|||
'systems/daggerheart/templates/sheets/global/partials/resource-section/dice-value.hbs',
|
||||
'systems/daggerheart/templates/sheets/global/partials/resource-section/die.hbs',
|
||||
'systems/daggerheart/templates/sheets/global/partials/resource-bar.hbs',
|
||||
'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs',
|
||||
'systems/daggerheart/templates/sheets/global/partials/item-tags.hbs',
|
||||
'systems/daggerheart/templates/components/card-preview.hbs',
|
||||
'systems/daggerheart/templates/levelup/parts/selectable-card-preview.hbs',
|
||||
'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs',
|
||||
'systems/daggerheart/templates/ui/combatTracker/combatTrackerSection.hbs',
|
||||
'systems/daggerheart/templates/actionTypes/damage.hbs',
|
||||
'systems/daggerheart/templates/actionTypes/resource.hbs',
|
||||
|
|
@ -33,6 +34,7 @@ export const preloadHandlebarsTemplates = async function () {
|
|||
'systems/daggerheart/templates/actionTypes/beastform.hbs',
|
||||
'systems/daggerheart/templates/actionTypes/countdown.hbs',
|
||||
'systems/daggerheart/templates/actionTypes/summon.hbs',
|
||||
'systems/daggerheart/templates/actionTypes/transform.hbs',
|
||||
'systems/daggerheart/templates/settings/components/settings-item-line.hbs',
|
||||
'systems/daggerheart/templates/ui/tooltip/parts/tooltipChips.hbs',
|
||||
'systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs',
|
||||
|
|
@ -44,6 +46,7 @@ export const preloadHandlebarsTemplates = async function () {
|
|||
'systems/daggerheart/templates/ui/chat/parts/target-part.hbs',
|
||||
'systems/daggerheart/templates/ui/chat/parts/button-part.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'
|
||||
]);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,10 +1,18 @@
|
|||
import { defaultLevelTiers, DhLevelTiers } from '../data/levelTier.mjs';
|
||||
import DhCountdowns from '../data/countdowns.mjs';
|
||||
import { DhAppearance, DhAutomation, DhHomebrew, DhVariantRules } from '../data/settings/_module.mjs';
|
||||
import {
|
||||
DhAppearance,
|
||||
DhAutomation,
|
||||
DhGlobalOverrides,
|
||||
DhHomebrew,
|
||||
DhMetagaming,
|
||||
DhVariantRules
|
||||
} from '../data/settings/_module.mjs';
|
||||
import {
|
||||
DhAppearanceSettings,
|
||||
DhAutomationSettings,
|
||||
DhHomebrewSettings,
|
||||
DhMetagamingSettings,
|
||||
DhVariantRuleSettings
|
||||
} from '../applications/settings/_module.mjs';
|
||||
import { CompendiumBrowserSettings, DhTagTeamRoll } from '../data/_module.mjs';
|
||||
|
|
@ -38,18 +46,25 @@ const registerMenuSettings = () => {
|
|||
type: DhAutomation
|
||||
});
|
||||
|
||||
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Metagaming, {
|
||||
scope: 'world',
|
||||
config: false,
|
||||
type: DhMetagaming
|
||||
});
|
||||
|
||||
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, {
|
||||
scope: 'world',
|
||||
config: false,
|
||||
type: DhHomebrew,
|
||||
onChange: value => {
|
||||
if (value.maxFear) {
|
||||
if (ui.resources) ui.resources.render({ force: true });
|
||||
value.handleChange();
|
||||
}
|
||||
});
|
||||
|
||||
// Some homebrew settings may change sheets in various ways, so trigger a re-render
|
||||
resetActors();
|
||||
}
|
||||
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, {
|
||||
|
|
@ -57,12 +72,7 @@ const registerMenuSettings = () => {
|
|||
config: false,
|
||||
type: DhAppearance,
|
||||
onChange: value => {
|
||||
if (value.displayFear) {
|
||||
if (ui.resources) {
|
||||
if (value.displayFear === 'hide') ui.resources.close({ allowed: true });
|
||||
else ui.resources.render({ force: true });
|
||||
}
|
||||
}
|
||||
value.handleChange();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
@ -76,6 +86,16 @@ const registerMenus = () => {
|
|||
type: DhAutomationSettings,
|
||||
restricted: true
|
||||
});
|
||||
|
||||
game.settings.registerMenu(CONFIG.DH.id, CONFIG.DH.SETTINGS.menu.Metagaming.Name, {
|
||||
name: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.metagaming.name'),
|
||||
label: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.metagaming.label'),
|
||||
hint: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.metagaming.hint'),
|
||||
icon: CONFIG.DH.SETTINGS.menu.Metagaming.Icon,
|
||||
type: DhMetagamingSettings,
|
||||
restricted: true
|
||||
});
|
||||
|
||||
game.settings.registerMenu(CONFIG.DH.id, CONFIG.DH.SETTINGS.menu.Homebrew.Name, {
|
||||
name: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.homebrew.name'),
|
||||
label: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.homebrew.label'),
|
||||
|
|
@ -144,30 +164,8 @@ const registerNonConfigSettings = () => {
|
|||
});
|
||||
|
||||
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.CompendiumBrowserSettings, {
|
||||
scope: 'client',
|
||||
scope: 'world',
|
||||
config: false,
|
||||
type: CompendiumBrowserSettings
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Triggers a reset and non-forced re-render on all given actors (if given)
|
||||
* or all world actors and actors in all scenes to show immediate results for a changed setting.
|
||||
*/
|
||||
function resetActors(actors) {
|
||||
actors ??= [
|
||||
game.actors.contents,
|
||||
game.scenes.contents.flatMap(s => s.tokens.contents).flatMap(t => t.actor ?? [])
|
||||
].flat();
|
||||
actors = new Set(actors);
|
||||
for (const actor of actors) {
|
||||
for (const app of Object.values(actor.apps)) {
|
||||
for (const element of app.element?.querySelectorAll('prose-mirror.active')) {
|
||||
element.open = false; // This triggers a save
|
||||
}
|
||||
}
|
||||
|
||||
actor.reset();
|
||||
actor.render();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,8 +91,8 @@
|
|||
"useDefault": false
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -118,7 +118,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"_id": "TCKVaVweyJzhEArX",
|
||||
|
|
@ -343,7 +343,7 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [],
|
||||
"parts": {},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -471,8 +471,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -499,7 +499,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"armor": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -524,7 +524,7 @@
|
|||
},
|
||||
"type": []
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -598,8 +598,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -626,7 +626,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -652,8 +652,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -680,7 +680,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -75,8 +75,8 @@
|
|||
},
|
||||
"range": "veryClose",
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -102,7 +102,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "attack",
|
||||
"chatDisplay": false
|
||||
|
|
@ -400,8 +400,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -427,7 +427,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false,
|
||||
"direct": true
|
||||
},
|
||||
|
|
@ -508,7 +508,7 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [],
|
||||
"parts": {},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -581,8 +581,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -608,7 +608,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"hope": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -633,7 +633,7 @@
|
|||
},
|
||||
"type": []
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -72,8 +72,8 @@
|
|||
"type": "attack"
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -100,7 +100,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"img": "icons/weapons/daggers/dagger-bone-black.webp",
|
||||
"type": "attack",
|
||||
|
|
|
|||
|
|
@ -85,8 +85,8 @@
|
|||
},
|
||||
"range": "far",
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -112,7 +112,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"img": "icons/magic/unholy/beam-ringed-impact-purple.webp",
|
||||
"type": "attack",
|
||||
|
|
@ -256,7 +256,7 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [],
|
||||
"parts": {},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -336,8 +336,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -363,7 +363,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -414,8 +414,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"fear": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -440,7 +440,7 @@
|
|||
},
|
||||
"type": []
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -619,7 +619,7 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [],
|
||||
"parts": {},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -692,8 +692,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -719,7 +719,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -80,8 +80,8 @@
|
|||
"type": "attack"
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -107,7 +107,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"img": "icons/weapons/bows/longbow-recurve-leather-brown.webp",
|
||||
"type": "attack",
|
||||
|
|
@ -246,8 +246,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -273,7 +273,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -68,8 +68,8 @@
|
|||
"description": "<p>A group of trained archers bearing massive bows.</p>",
|
||||
"attack": {
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -95,7 +95,7 @@
|
|||
"resultBased": false,
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"name": "Longbow",
|
||||
"img": "icons/weapons/bows/longbow-recurve-leather-brown.webp",
|
||||
|
|
@ -270,8 +270,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -295,7 +295,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -368,8 +368,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -393,7 +393,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -81,8 +81,8 @@
|
|||
},
|
||||
"range": "close",
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -108,7 +108,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "attack",
|
||||
"chatDisplay": false
|
||||
|
|
|
|||
|
|
@ -80,8 +80,8 @@
|
|||
"type": "attack"
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -107,7 +107,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"range": "melee",
|
||||
"type": "attack",
|
||||
|
|
@ -309,7 +309,7 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [],
|
||||
"parts": {},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -382,8 +382,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -409,7 +409,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -482,8 +482,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -509,7 +509,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -582,8 +582,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -609,7 +609,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -737,8 +737,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"stress": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -763,7 +763,7 @@
|
|||
},
|
||||
"type": []
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -836,7 +836,7 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [],
|
||||
"parts": {},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -964,8 +964,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -991,7 +991,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -1071,8 +1071,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"fear": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -1097,7 +1097,7 @@
|
|||
},
|
||||
"type": []
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -1165,8 +1165,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -1192,7 +1192,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -84,8 +84,8 @@
|
|||
"type": "attack"
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -111,7 +111,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"img": "icons/creatures/claws/claw-straight-brown.webp",
|
||||
"type": "attack",
|
||||
|
|
@ -284,8 +284,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -311,7 +311,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -441,8 +441,8 @@
|
|||
"consumeOnSuccess": false
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"fear": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -468,7 +468,7 @@
|
|||
},
|
||||
"type": []
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -80,8 +80,8 @@
|
|||
"name": "Longsword",
|
||||
"img": "icons/weapons/swords/sword-guard.webp",
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -107,7 +107,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "attack",
|
||||
"range": "melee",
|
||||
|
|
@ -246,7 +246,7 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [],
|
||||
"parts": {},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -319,7 +319,7 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [],
|
||||
"parts": {},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -83,8 +83,8 @@
|
|||
},
|
||||
"range": "veryClose",
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -110,7 +110,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"img": "icons/skills/melee/unarmed-punch-fist-yellow-red.webp",
|
||||
"type": "attack",
|
||||
|
|
@ -280,8 +280,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -307,7 +307,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false,
|
||||
"direct": true
|
||||
},
|
||||
|
|
@ -389,8 +389,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"stress": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -415,7 +415,7 @@
|
|||
},
|
||||
"type": []
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -79,8 +79,8 @@
|
|||
},
|
||||
"range": "veryClose",
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -106,7 +106,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
],
|
||||
},
|
||||
"direct": true
|
||||
},
|
||||
"name": "Club",
|
||||
|
|
@ -336,8 +336,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false,
|
||||
|
|
@ -365,7 +365,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false,
|
||||
"direct": true
|
||||
},
|
||||
|
|
@ -412,8 +412,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"fear": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -438,7 +438,7 @@
|
|||
},
|
||||
"type": []
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -507,8 +507,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -534,7 +534,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false,
|
||||
"direct": true
|
||||
},
|
||||
|
|
|
|||
|
|
@ -74,8 +74,8 @@
|
|||
},
|
||||
"range": "close",
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -101,7 +101,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"img": "icons/magic/light/beam-rays-magenta.webp",
|
||||
"type": "attack",
|
||||
|
|
@ -383,8 +383,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -410,7 +410,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -483,8 +483,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"stress": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -508,7 +508,7 @@
|
|||
},
|
||||
"type": []
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -67,8 +67,8 @@
|
|||
"type": "attack"
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -95,7 +95,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "attack",
|
||||
"chatDisplay": false
|
||||
|
|
|
|||
|
|
@ -72,8 +72,8 @@
|
|||
"type": "attack"
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -99,7 +99,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"name": "Fist Slam",
|
||||
"img": "icons/skills/melee/unarmed-punch-fist-yellow-red.webp",
|
||||
|
|
@ -332,8 +332,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -359,7 +359,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -534,8 +534,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -561,7 +561,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -84,8 +84,8 @@
|
|||
"type": "attack"
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -111,7 +111,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"img": "icons/weapons/daggers/dagger-straight-cracked.webp",
|
||||
"type": "attack",
|
||||
|
|
@ -256,8 +256,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"stress": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -282,7 +282,7 @@
|
|||
},
|
||||
"type": []
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -79,8 +79,8 @@
|
|||
"type": "attack"
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -106,7 +106,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"img": "icons/weapons/daggers/dagger-twin-green.webp",
|
||||
"type": "attack",
|
||||
|
|
@ -253,8 +253,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"stress": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -279,7 +279,7 @@
|
|||
},
|
||||
"type": []
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -84,8 +84,8 @@
|
|||
"type": "attack"
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -111,7 +111,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"range": "far",
|
||||
"img": "icons/weapons/staves/staff-ornate-purple.webp",
|
||||
|
|
@ -256,8 +256,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -283,7 +283,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"stress": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -308,7 +308,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -614,8 +614,8 @@
|
|||
"recovery": "scene"
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"stress": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -640,7 +640,7 @@
|
|||
},
|
||||
"type": []
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -74,8 +74,8 @@
|
|||
"type": "attack"
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -101,7 +101,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"range": "melee",
|
||||
"type": "attack",
|
||||
|
|
@ -300,8 +300,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"stress": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -326,7 +326,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -66,8 +66,8 @@
|
|||
"type": "attack"
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -94,7 +94,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "attack",
|
||||
"range": "melee",
|
||||
|
|
|
|||
|
|
@ -79,8 +79,8 @@
|
|||
"type": "attack"
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -106,7 +106,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"img": "icons/magic/nature/root-vines-grow-brown.webp",
|
||||
"type": "attack",
|
||||
|
|
@ -245,8 +245,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"stress": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -271,7 +271,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -325,8 +325,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -350,7 +350,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -80,8 +80,8 @@
|
|||
"type": "attack"
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -107,7 +107,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "attack",
|
||||
"range": "melee",
|
||||
|
|
|
|||
|
|
@ -80,8 +80,8 @@
|
|||
"type": "attack"
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -107,7 +107,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "attack",
|
||||
"range": "far",
|
||||
|
|
@ -435,8 +435,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hope": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -461,7 +461,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -515,8 +515,8 @@
|
|||
"consumeOnSuccess": false
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"fear": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -542,7 +542,7 @@
|
|||
},
|
||||
"type": []
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -81,8 +81,8 @@
|
|||
"type": "attack"
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -108,7 +108,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "attack",
|
||||
"chatDisplay": false
|
||||
|
|
@ -251,8 +251,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hope": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -277,7 +277,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -329,8 +329,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"stress": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -355,7 +355,7 @@
|
|||
},
|
||||
"type": []
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -414,8 +414,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -441,7 +441,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -550,8 +550,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"stress": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -576,7 +576,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -80,8 +80,8 @@
|
|||
},
|
||||
"range": "far",
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -107,7 +107,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
],
|
||||
},
|
||||
"direct": true
|
||||
},
|
||||
"img": "icons/magic/symbols/rune-sigil-rough-white-teal.webp",
|
||||
|
|
@ -352,7 +352,7 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [],
|
||||
"parts": {},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -81,8 +81,8 @@
|
|||
},
|
||||
"img": "icons/skills/melee/unarmed-punch-fist-yellow-red.webp",
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -108,7 +108,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
],
|
||||
},
|
||||
"direct": true
|
||||
},
|
||||
"type": "attack",
|
||||
|
|
@ -398,8 +398,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -425,7 +425,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false,
|
||||
"direct": true
|
||||
},
|
||||
|
|
|
|||
|
|
@ -74,8 +74,8 @@
|
|||
"motivesAndTactics": "Cause fear, consume fl esh, please masters",
|
||||
"attack": {
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -101,7 +101,7 @@
|
|||
"resultBased": false,
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"name": "Claws and Fangs",
|
||||
"img": "icons/creatures/abilities/mouth-teeth-rows-red.webp",
|
||||
|
|
@ -269,8 +269,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hope": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -295,7 +295,7 @@
|
|||
},
|
||||
"type": []
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -349,8 +349,8 @@
|
|||
"consumeOnSuccess": false
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"fear": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -376,7 +376,7 @@
|
|||
},
|
||||
"type": []
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -78,8 +78,8 @@
|
|||
"type": "attack"
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -105,7 +105,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"img": "icons/creatures/claws/claw-hooked-curved.webp",
|
||||
"type": "attack",
|
||||
|
|
@ -312,8 +312,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"stress": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -337,7 +337,7 @@
|
|||
},
|
||||
"type": []
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -397,8 +397,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -424,7 +424,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -79,8 +79,8 @@
|
|||
"type": "attack"
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -106,7 +106,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"img": "icons/creatures/claws/claw-straight-brown.webp",
|
||||
"type": "attack",
|
||||
|
|
@ -247,8 +247,8 @@
|
|||
"consumeOnSuccess": false
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -272,7 +272,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -352,8 +352,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -377,7 +377,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false,
|
||||
"direct": true
|
||||
},
|
||||
|
|
|
|||
|
|
@ -81,8 +81,8 @@
|
|||
},
|
||||
"range": "far",
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -108,7 +108,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "attack",
|
||||
"chatDisplay": false
|
||||
|
|
@ -251,7 +251,7 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [],
|
||||
"parts": {},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -297,8 +297,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -324,7 +324,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -438,8 +438,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -464,7 +464,7 @@
|
|||
},
|
||||
"type": []
|
||||
},
|
||||
{
|
||||
"stress": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -489,7 +489,7 @@
|
|||
},
|
||||
"type": []
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -68,8 +68,8 @@
|
|||
"motivesAndTactics": "Avoid larger predators, shock prey, tear apart",
|
||||
"attack": {
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -95,7 +95,7 @@
|
|||
"resultBased": false,
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"name": "Shocking Bite",
|
||||
"img": "icons/creatures/abilities/mouth-teeth-sharp.webp",
|
||||
|
|
@ -270,8 +270,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -297,7 +297,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -67,8 +67,8 @@
|
|||
},
|
||||
"range": "close",
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -95,7 +95,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "attack",
|
||||
"chatDisplay": false
|
||||
|
|
|
|||
|
|
@ -98,8 +98,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -125,7 +125,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -276,8 +276,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -303,7 +303,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -79,8 +79,8 @@
|
|||
"type": "attack"
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -106,7 +106,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"img": "icons/creatures/claws/claw-hooked-barbed.webp",
|
||||
"range": "melee",
|
||||
|
|
|
|||
|
|
@ -67,8 +67,8 @@
|
|||
"img": "icons/weapons/axes/axe-battle-skull-black.webp",
|
||||
"range": "veryClose",
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -95,7 +95,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "attack",
|
||||
"chatDisplay": false
|
||||
|
|
@ -256,8 +256,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hope": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -282,7 +282,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -80,8 +80,8 @@
|
|||
},
|
||||
"range": "far",
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -107,7 +107,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"img": "icons/weapons/staves/staff-animal-skull-bull.webp",
|
||||
"type": "attack",
|
||||
|
|
@ -251,8 +251,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -278,7 +278,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -482,8 +482,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"stress": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -508,7 +508,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -67,8 +67,8 @@
|
|||
"useDefault": false
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -94,7 +94,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"description": "",
|
||||
|
|
@ -337,7 +337,7 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [],
|
||||
"parts": {},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -416,8 +416,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -443,7 +443,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -516,8 +516,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -543,7 +543,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -653,8 +653,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hope": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -679,7 +679,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -717,7 +717,35 @@
|
|||
"system": {
|
||||
"description": "<p>When the @Lookup[@name] marks their last HP, replace them with the @UUID[Compendium.daggerheart.adversaries.Actor.RXkZTwBRi4dJ3JE5]{Fallen Warlord: Undefeated Champion} and immediately spotlight them.</p>",
|
||||
"resource": null,
|
||||
"actions": {},
|
||||
"actions": {
|
||||
"gP426WmWbtrZEWCD": {
|
||||
"type": "transform",
|
||||
"_id": "gP426WmWbtrZEWCD",
|
||||
"systemPath": "actions",
|
||||
"baseAction": false,
|
||||
"description": "",
|
||||
"chatDisplay": true,
|
||||
"originItem": {
|
||||
"type": "itemCollection"
|
||||
},
|
||||
"actionType": "action",
|
||||
"triggers": [],
|
||||
"cost": [],
|
||||
"uses": {
|
||||
"value": null,
|
||||
"max": null,
|
||||
"recovery": null,
|
||||
"consumeOnSuccess": false
|
||||
},
|
||||
"transform": {
|
||||
"actorUUID": "Compendium.daggerheart.adversaries.Actor.RXkZTwBRi4dJ3JE5",
|
||||
"resourceRefresh": {
|
||||
"hitPoints": true,
|
||||
"stress": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"originItemType": null,
|
||||
"originId": null,
|
||||
"featureForm": "reaction"
|
||||
|
|
|
|||
|
|
@ -67,8 +67,8 @@
|
|||
"useDefault": false
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -94,7 +94,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"description": "",
|
||||
|
|
@ -338,7 +338,7 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [],
|
||||
"parts": {},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -410,8 +410,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -437,7 +437,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -697,8 +697,8 @@
|
|||
"consumeOnSuccess": false
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"fear": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -724,7 +724,7 @@
|
|||
},
|
||||
"type": []
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -792,8 +792,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hope": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -818,7 +818,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -81,8 +81,8 @@
|
|||
},
|
||||
"range": "far",
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -108,7 +108,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "attack",
|
||||
"chatDisplay": false
|
||||
|
|
@ -269,8 +269,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -294,7 +294,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -81,8 +81,8 @@
|
|||
},
|
||||
"range": "veryClose",
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -108,7 +108,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "attack",
|
||||
"chatDisplay": false
|
||||
|
|
@ -251,8 +251,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -278,7 +278,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -351,8 +351,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -378,7 +378,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -452,8 +452,8 @@
|
|||
"consumeOnSuccess": false
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"fear": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -479,7 +479,7 @@
|
|||
},
|
||||
"type": []
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -98,8 +98,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -125,7 +125,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -345,8 +345,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -372,7 +372,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -499,8 +499,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -526,7 +526,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
@ -643,8 +643,8 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -670,7 +670,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -74,8 +74,8 @@
|
|||
"motivesAndTactics": "Fly away, harass, steal blood",
|
||||
"attack": {
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": false
|
||||
|
|
@ -101,7 +101,7 @@
|
|||
"resultBased": false,
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"name": "Proboscis",
|
||||
"img": "icons/skills/wounds/blood-cells-vessel-red.webp",
|
||||
|
|
|
|||
|
|
@ -72,8 +72,8 @@
|
|||
"name": "Claws",
|
||||
"img": "icons/creatures/claws/claw-straight-brown.webp",
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -100,7 +100,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "attack",
|
||||
"range": "melee",
|
||||
|
|
@ -272,7 +272,7 @@
|
|||
"recovery": null
|
||||
},
|
||||
"damage": {
|
||||
"parts": [],
|
||||
"parts": {},
|
||||
"includeBase": false
|
||||
},
|
||||
"target": {
|
||||
|
|
|
|||
|
|
@ -62,8 +62,8 @@
|
|||
"name": "Warhammer",
|
||||
"img": "icons/weapons/hammers/hammer-double-stone-worn.webp",
|
||||
"damage": {
|
||||
"parts": [
|
||||
{
|
||||
"parts": {
|
||||
"hitPoints": {
|
||||
"value": {
|
||||
"custom": {
|
||||
"enabled": true,
|
||||
|
|
@ -90,7 +90,7 @@
|
|||
},
|
||||
"base": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"range": "veryClose",
|
||||
"roll": {
|
||||
|
|
|
|||
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