mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-15 13:11:08 +01:00
Merge branch 'development' into feature/313-preset-measured-templates
This commit is contained in:
commit
d5964a8d1e
94 changed files with 1472 additions and 550 deletions
|
|
@ -44,12 +44,14 @@ We encourage contributors to leave comments or open Discussions when proposing s
|
||||||
## 🧾 Issue & PR Best Practices
|
## 🧾 Issue & PR Best Practices
|
||||||
|
|
||||||
**For Issues:**
|
**For Issues:**
|
||||||
|
|
||||||
- Use clear, descriptive titles
|
- Use clear, descriptive titles
|
||||||
- Provide a concise explanation of the problem or idea
|
- Provide a concise explanation of the problem or idea
|
||||||
- Include reproduction steps or example scenarios if it's a bug
|
- Include reproduction steps or example scenarios if it's a bug
|
||||||
- Add screenshots or logs if helpful
|
- Add screenshots or logs if helpful
|
||||||
|
|
||||||
**For Pull Requests:**
|
**For Pull Requests:**
|
||||||
|
|
||||||
- Use a clear title summarizing the change
|
- Use a clear title summarizing the change
|
||||||
- Provide a brief description of what your code does and why
|
- Provide a brief description of what your code does and why
|
||||||
- Link to any related Issues
|
- Link to any related Issues
|
||||||
|
|
@ -71,6 +73,6 @@ Discussions are currently happening on GitHub — in Issues, PRs, and [GitHub Di
|
||||||
|
|
||||||
## 🤗 Thank You!
|
## 🤗 Thank You!
|
||||||
|
|
||||||
Whether you're fixing a typo or designing entire mechanics — every contribution matters. Thank you for helping bring *Daggerheart* to life in FoundryVTT through **Foundryborne**!
|
Whether you're fixing a typo or designing entire mechanics — every contribution matters. Thank you for helping bring _Daggerheart_ to life in FoundryVTT through **Foundryborne**!
|
||||||
|
|
||||||
🐸🛠️
|
🐸🛠️
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { SYSTEM } from './module/config/system.mjs';
|
import { SYSTEM } from './module/config/system.mjs';
|
||||||
import * as applications from './module/applications/_module.mjs';
|
import * as applications from './module/applications/_module.mjs';
|
||||||
|
import * as data from './module/data/_module.mjs';
|
||||||
import * as models from './module/data/_module.mjs';
|
import * as models from './module/data/_module.mjs';
|
||||||
import * as documents from './module/documents/_module.mjs';
|
import * as documents from './module/documents/_module.mjs';
|
||||||
import * as dice from './module/dice/_module.mjs';
|
import * as dice from './module/dice/_module.mjs';
|
||||||
|
|
@ -26,6 +27,7 @@ Hooks.once('init', () => {
|
||||||
CONFIG.DH = SYSTEM;
|
CONFIG.DH = SYSTEM;
|
||||||
game.system.api = {
|
game.system.api = {
|
||||||
applications,
|
applications,
|
||||||
|
data,
|
||||||
models,
|
models,
|
||||||
documents,
|
documents,
|
||||||
dice,
|
dice,
|
||||||
|
|
@ -133,6 +135,8 @@ Hooks.once('init', () => {
|
||||||
CONFIG.ui.combat = applications.ui.DhCombatTracker;
|
CONFIG.ui.combat = applications.ui.DhCombatTracker;
|
||||||
CONFIG.ui.chat = applications.ui.DhChatLog;
|
CONFIG.ui.chat = applications.ui.DhChatLog;
|
||||||
CONFIG.ui.hotbar = applications.ui.DhHotbar;
|
CONFIG.ui.hotbar = applications.ui.DhHotbar;
|
||||||
|
CONFIG.ui.sidebar = applications.sidebar.DhSidebar;
|
||||||
|
CONFIG.ui.daggerheartMenu = applications.sidebar.DaggerheartMenu;
|
||||||
CONFIG.Token.rulerClass = placeables.DhTokenRuler;
|
CONFIG.Token.rulerClass = placeables.DhTokenRuler;
|
||||||
|
|
||||||
CONFIG.ui.resources = applications.ui.DhFearTracker;
|
CONFIG.ui.resources = applications.ui.DhFearTracker;
|
||||||
|
|
@ -162,7 +166,7 @@ Hooks.on('ready', async () => {
|
||||||
if (game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance).displayFear !== 'hide')
|
if (game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance).displayFear !== 'hide')
|
||||||
ui.resources.render({ force: true });
|
ui.resources.render({ force: true });
|
||||||
|
|
||||||
if(!(ui.compendiumBrowser instanceof applications.ui.ItemBrowser))
|
if (!(ui.compendiumBrowser instanceof applications.ui.ItemBrowser))
|
||||||
ui.compendiumBrowser = new applications.ui.ItemBrowser();
|
ui.compendiumBrowser = new applications.ui.ItemBrowser();
|
||||||
|
|
||||||
ui.presetTemplates = new applications.ui.PresetTemplates();
|
ui.presetTemplates = new applications.ui.PresetTemplates();
|
||||||
|
|
@ -311,5 +315,5 @@ Hooks.on('moveToken', async (movedToken, data) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Hooks.on("renderCompendiumDirectory", (app, html) => applications.ui.ItemBrowser.injectSidebarButton(html));
|
Hooks.on('renderCompendiumDirectory', (app, html) => applications.ui.ItemBrowser.injectSidebarButton(html));
|
||||||
Hooks.on("renderDocumentDirectory", (app, html) => applications.ui.ItemBrowser.injectSidebarButton(html));
|
Hooks.on('renderDocumentDirectory', (app, html) => applications.ui.ItemBrowser.injectSidebarButton(html));
|
||||||
|
|
|
||||||
32
lang/en.json
32
lang/en.json
|
|
@ -502,18 +502,21 @@
|
||||||
},
|
},
|
||||||
"takeLevelUp": "Finish Level Up",
|
"takeLevelUp": "Finish Level Up",
|
||||||
"tier2": {
|
"tier2": {
|
||||||
|
"name": "Tier 2",
|
||||||
"label": "Levels 2-4",
|
"label": "Levels 2-4",
|
||||||
"infoLabel": "At Level 2, gain an additional Experience at +2 and gain a +1 bonus to your Proficiency.",
|
"infoLabel": "At Level 2, gain an additional Experience at +2 and gain a +1 bonus to your Proficiency.",
|
||||||
"pretext": "Choose two options from the list below",
|
"pretext": "Choose two options from the list below",
|
||||||
"posttext": "Take an additional domain card of your level or lower from a domain you have access to."
|
"posttext": "Take an additional domain card of your level or lower from a domain you have access to."
|
||||||
},
|
},
|
||||||
"tier3": {
|
"tier3": {
|
||||||
|
"name": "Tier 3",
|
||||||
"label": "Levels 5-7",
|
"label": "Levels 5-7",
|
||||||
"infoLabel": "At Level 5, take an additional Experience and clear all marks on Character Traits.",
|
"infoLabel": "At Level 5, take an additional Experience and clear all marks on Character Traits.",
|
||||||
"pretext": "When you level up, record it on your character sheet, then choose two from the list below or any unmarked from the previous tier.",
|
"pretext": "When you level up, record it on your character sheet, then choose two from the list below or any unmarked from the previous tier.",
|
||||||
"posttext": "Take an additional domain card of your level or lower from a domain you have access to."
|
"posttext": "Take an additional domain card of your level or lower from a domain you have access to."
|
||||||
},
|
},
|
||||||
"tier4": {
|
"tier4": {
|
||||||
|
"name": "Tier 4",
|
||||||
"label": "Levels 8-10",
|
"label": "Levels 8-10",
|
||||||
"infoLabel": "At Level 8, take an additional Experience and clear all marks on Character Traits.",
|
"infoLabel": "At Level 8, take an additional Experience and clear all marks on Character Traits.",
|
||||||
"pretext": "When you level up, record it on your character sheet, then choose two from the list below or any unmarked from the previous tier.",
|
"pretext": "When you level up, record it on your character sheet, then choose two from the list below or any unmarked from the previous tier.",
|
||||||
|
|
@ -1027,6 +1030,12 @@
|
||||||
"selectType": "Select Action Type",
|
"selectType": "Select Action Type",
|
||||||
"selectAction": "Action Selection"
|
"selectAction": "Action Selection"
|
||||||
},
|
},
|
||||||
|
"TargetTypes": {
|
||||||
|
"any": "Any",
|
||||||
|
"friendly": "Friendly",
|
||||||
|
"hostile": "Hostile",
|
||||||
|
"self": "Self"
|
||||||
|
},
|
||||||
"TemplateTypes": {
|
"TemplateTypes": {
|
||||||
"circle": "Circle",
|
"circle": "Circle",
|
||||||
"cone": "Cone",
|
"cone": "Cone",
|
||||||
|
|
@ -1918,7 +1927,10 @@
|
||||||
"roll": "Roll",
|
"roll": "Roll",
|
||||||
"rules": "Rules",
|
"rules": "Rules",
|
||||||
"types": "Types",
|
"types": "Types",
|
||||||
"questions": "Questions"
|
"itemFeatures": "Item Features",
|
||||||
|
"questions": "Questions",
|
||||||
|
"configuration": "Configuration",
|
||||||
|
"base": "Base"
|
||||||
},
|
},
|
||||||
"Tiers": {
|
"Tiers": {
|
||||||
"singular": "Tier",
|
"singular": "Tier",
|
||||||
|
|
@ -1935,6 +1947,7 @@
|
||||||
"amount": "Amount",
|
"amount": "Amount",
|
||||||
"any": "Any",
|
"any": "Any",
|
||||||
"armor": "Armor",
|
"armor": "Armor",
|
||||||
|
"armorFeatures": "Armor Features",
|
||||||
"armors": "Armors",
|
"armors": "Armors",
|
||||||
"armorScore": "Armor Score",
|
"armorScore": "Armor Score",
|
||||||
"activeEffects": "Active Effects",
|
"activeEffects": "Active Effects",
|
||||||
|
|
@ -2027,6 +2040,8 @@
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
"scalable": "Scalable",
|
"scalable": "Scalable",
|
||||||
"situationalBonus": "Situational Bonus",
|
"situationalBonus": "Situational Bonus",
|
||||||
|
"spent": "Spent",
|
||||||
|
"step": "Step",
|
||||||
"stress": "Stress",
|
"stress": "Stress",
|
||||||
"subclasses": "Subclasses",
|
"subclasses": "Subclasses",
|
||||||
"success": "Success",
|
"success": "Success",
|
||||||
|
|
@ -2047,6 +2062,7 @@
|
||||||
"used": "Used",
|
"used": "Used",
|
||||||
"uses": "Uses",
|
"uses": "Uses",
|
||||||
"value": "Value",
|
"value": "Value",
|
||||||
|
"weaponFeatures": "Weapon Features",
|
||||||
"weapons": "Weapons",
|
"weapons": "Weapons",
|
||||||
"withThing": "With {thing}"
|
"withThing": "With {thing}"
|
||||||
},
|
},
|
||||||
|
|
@ -2277,7 +2293,9 @@
|
||||||
},
|
},
|
||||||
"Homebrew": {
|
"Homebrew": {
|
||||||
"newDowntimeMove": "Downtime Move",
|
"newDowntimeMove": "Downtime Move",
|
||||||
|
"newFeature": "New ItemFeature",
|
||||||
"downtimeMoves": "Downtime Moves",
|
"downtimeMoves": "Downtime Moves",
|
||||||
|
"itemFeatures": "Item Features",
|
||||||
"nrChoices": "# Moves Per Rest",
|
"nrChoices": "# Moves Per Rest",
|
||||||
"resetMovesTitle": "Reset {type} Downtime Moves",
|
"resetMovesTitle": "Reset {type} Downtime Moves",
|
||||||
"resetMovesText": "Are you sure you want to reset?",
|
"resetMovesText": "Are you sure you want to reset?",
|
||||||
|
|
@ -2421,6 +2439,10 @@
|
||||||
"heal": "Heal",
|
"heal": "Heal",
|
||||||
"applyHealing": "Apply Healing"
|
"applyHealing": "Apply Healing"
|
||||||
},
|
},
|
||||||
|
"refreshMessage": {
|
||||||
|
"title": "Feature Refresh",
|
||||||
|
"header": "Refreshed"
|
||||||
|
},
|
||||||
"reroll": {
|
"reroll": {
|
||||||
"confirmTitle": "Reroll Dice",
|
"confirmTitle": "Reroll Dice",
|
||||||
"confirmText": "Are you sure you want to reroll?"
|
"confirmText": "Are you sure you want to reroll?"
|
||||||
|
|
@ -2535,8 +2557,16 @@
|
||||||
"multiclassAlreadyPresent": "You already have a class and multiclass",
|
"multiclassAlreadyPresent": "You already have a class and multiclass",
|
||||||
"subclassesAlreadyPresent": "You already have a class and multiclass subclass",
|
"subclassesAlreadyPresent": "You already have a class and multiclass subclass",
|
||||||
"noDiceSystem": "Your selected dice {system} does not have a {faces} dice",
|
"noDiceSystem": "Your selected dice {system} does not have a {faces} dice",
|
||||||
|
"gmMenuRefresh": "You refreshed all actions and resources {types}",
|
||||||
"subclassAlreadyLinked": "{name} is already a subclass in the class {class}. Remove it from there if you want it to be a subclass to this class."
|
"subclassAlreadyLinked": "{name} is already a subclass in the class {class}. Remove it from there if you want it to be a subclass to this class."
|
||||||
},
|
},
|
||||||
|
"Sidebar": {
|
||||||
|
"daggerheartMenu": {
|
||||||
|
"title": "Daggerheart Menu",
|
||||||
|
"startSession": "Start Session",
|
||||||
|
"startScene": "Start Scene"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Tooltip": {
|
"Tooltip": {
|
||||||
"disableEffect": "Disable Effect",
|
"disableEffect": "Disable Effect",
|
||||||
"enableEffect": "Enable Effect",
|
"enableEffect": "Enable Effect",
|
||||||
|
|
|
||||||
|
|
@ -6,5 +6,6 @@ export * as scene from './scene/_module.mjs';
|
||||||
export * as settings from './settings/_module.mjs';
|
export * as settings from './settings/_module.mjs';
|
||||||
export * as sheets from './sheets/_module.mjs';
|
export * as sheets from './sheets/_module.mjs';
|
||||||
export * as sheetConfigs from './sheets-configs/_module.mjs';
|
export * as sheetConfigs from './sheets-configs/_module.mjs';
|
||||||
|
export * as sidebar from './sidebar/_module.mjs';
|
||||||
export * as ui from './ui/_module.mjs';
|
export * as ui from './ui/_module.mjs';
|
||||||
export * as ux from './ux/_module.mjs';
|
export * as ux from './ux/_module.mjs';
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
export { default as DhSceneConfigSettings } from './sceneConfigSettings.mjs';
|
export { default as DhSceneConfigSettings } from './sceneConfigSettings.mjs';
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,24 @@
|
||||||
export default class DhSceneConfigSettings extends foundry.applications.sheets.SceneConfig {
|
export default class DhSceneConfigSettings extends foundry.applications.sheets.SceneConfig {
|
||||||
constructor(options, ...args) {
|
constructor(options, ...args) {
|
||||||
super(options, ...args);
|
super(options, ...args);
|
||||||
}
|
|
||||||
|
|
||||||
static buildParts() {
|
|
||||||
const { footer, ...parts } = super.PARTS;
|
|
||||||
const tmpParts = {
|
|
||||||
...parts,
|
|
||||||
dh: { template: "systems/daggerheart/templates/scene/dh-config.hbs" },
|
|
||||||
footer
|
|
||||||
}
|
}
|
||||||
return tmpParts;
|
|
||||||
}
|
|
||||||
|
|
||||||
static PARTS = DhSceneConfigSettings.buildParts();
|
static buildParts() {
|
||||||
|
const { footer, ...parts } = super.PARTS;
|
||||||
|
const tmpParts = {
|
||||||
|
...parts,
|
||||||
|
dh: { template: 'systems/daggerheart/templates/scene/dh-config.hbs' },
|
||||||
|
footer
|
||||||
|
};
|
||||||
|
return tmpParts;
|
||||||
|
}
|
||||||
|
|
||||||
static buildTabs() {
|
static PARTS = DhSceneConfigSettings.buildParts();
|
||||||
super.TABS.sheet.tabs.push({ id: "dh", icon: "fa-solid" });
|
|
||||||
return super.TABS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static TABS = DhSceneConfigSettings.buildTabs();
|
static buildTabs() {
|
||||||
|
super.TABS.sheet.tabs.push({ id: 'dh', icon: 'fa-solid' });
|
||||||
|
return super.TABS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static TABS = DhSceneConfigSettings.buildTabs();
|
||||||
}
|
}
|
||||||
|
|
@ -33,7 +33,7 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
|
||||||
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
||||||
main: { template: 'systems/daggerheart/templates/settings/appearance-settings/main.hbs' },
|
main: { template: 'systems/daggerheart/templates/settings/appearance-settings/main.hbs' },
|
||||||
diceSoNice: { template: 'systems/daggerheart/templates/settings/appearance-settings/diceSoNice.hbs' },
|
diceSoNice: { template: 'systems/daggerheart/templates/settings/appearance-settings/diceSoNice.hbs' },
|
||||||
footer: { template: "templates/generic/form-footer.hbs" }
|
footer: { template: 'templates/generic/form-footer.hbs' }
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
|
|
@ -41,7 +41,7 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
|
||||||
general: {
|
general: {
|
||||||
tabs: [
|
tabs: [
|
||||||
{ id: 'main', label: 'DAGGERHEART.GENERAL.Tabs.general' },
|
{ id: 'main', label: 'DAGGERHEART.GENERAL.Tabs.general' },
|
||||||
{ id: 'diceSoNice', label: 'DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.title' },
|
{ id: 'diceSoNice', label: 'DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.title' }
|
||||||
],
|
],
|
||||||
initial: 'main'
|
initial: 'main'
|
||||||
},
|
},
|
||||||
|
|
@ -73,7 +73,7 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
_configureRenderParts(options) {
|
_configureRenderParts(options) {
|
||||||
const parts = super._configureRenderParts(options);
|
const parts = super._configureRenderParts(options);
|
||||||
if (!game.modules.get('dice-so-nice')?.active){
|
if (!game.modules.get('dice-so-nice')?.active) {
|
||||||
delete parts.diceSoNice;
|
delete parts.diceSoNice;
|
||||||
delete parts.tabs;
|
delete parts.tabs;
|
||||||
}
|
}
|
||||||
|
|
@ -83,7 +83,8 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
|
||||||
/**@inheritdoc */
|
/**@inheritdoc */
|
||||||
async _prepareContext(options) {
|
async _prepareContext(options) {
|
||||||
const context = await super._prepareContext(options);
|
const context = await super._prepareContext(options);
|
||||||
if (options.isFirstRender) this.setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance);
|
if (options.isFirstRender)
|
||||||
|
this.setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance);
|
||||||
|
|
||||||
context.setting = this.setting;
|
context.setting = this.setting;
|
||||||
context.fields = this.setting.schema.fields;
|
context.fields = this.setting.schema.fields;
|
||||||
|
|
@ -99,18 +100,17 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
|
||||||
const partContext = await super._preparePartContext(partId, context, options);
|
const partContext = await super._preparePartContext(partId, context, options);
|
||||||
if (partId in context.tabs) partContext.tab = partContext.tabs[partId];
|
if (partId in context.tabs) partContext.tab = partContext.tabs[partId];
|
||||||
switch (partId) {
|
switch (partId) {
|
||||||
case "diceSoNice":
|
case 'diceSoNice':
|
||||||
await this.prepareDiceSoNiceContext(partContext);
|
await this.prepareDiceSoNiceContext(partContext);
|
||||||
break;
|
break;
|
||||||
case "footer":
|
case 'footer':
|
||||||
partContext.buttons = [
|
partContext.buttons = [
|
||||||
{ type: "button", action: "reset", icon: "fa-solid fa-arrow-rotate-left", label: "Reset" },
|
{ type: 'button', action: 'reset', icon: 'fa-solid fa-arrow-rotate-left', label: 'Reset' },
|
||||||
{ type: "submit", icon: "fa-solid fa-floppy-disk", label: "Save Changes" }
|
{ type: 'submit', icon: 'fa-solid fa-floppy-disk', label: 'Save Changes' }
|
||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return partContext;
|
return partContext;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -120,32 +120,44 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
async prepareDiceSoNiceContext(context) {
|
async prepareDiceSoNiceContext(context) {
|
||||||
context.diceSoNiceTextures = Object.entries(game.dice3d.exports.TEXTURELIST).reduce((acc, [k, v]) => ({
|
context.diceSoNiceTextures = Object.entries(game.dice3d.exports.TEXTURELIST).reduce(
|
||||||
...acc,
|
(acc, [k, v]) => ({
|
||||||
[k]: v.name
|
...acc,
|
||||||
}), {});
|
[k]: v.name
|
||||||
context.diceSoNiceColorsets = Object.values(game.dice3d.exports.COLORSETS).reduce((acc, v) => ({
|
}),
|
||||||
...acc,
|
{}
|
||||||
[v.id]: v.description
|
);
|
||||||
}), {});
|
context.diceSoNiceColorsets = Object.values(game.dice3d.exports.COLORSETS).reduce(
|
||||||
context.diceSoNiceMaterials = Object.keys(game.dice3d.DiceFactory.material_options).reduce((acc, key) => ({
|
(acc, v) => ({
|
||||||
...acc,
|
...acc,
|
||||||
[key]: `DICESONICE.Material${key.capitalize()}`
|
[v.id]: v.description
|
||||||
}), {});
|
}),
|
||||||
context.diceSoNiceSystems = Object.fromEntries([...game.dice3d.DiceFactory.systems].map(([k, v]) => [k, v.name]));
|
{}
|
||||||
|
);
|
||||||
|
context.diceSoNiceMaterials = Object.keys(game.dice3d.DiceFactory.material_options).reduce(
|
||||||
|
(acc, key) => ({
|
||||||
|
...acc,
|
||||||
|
[key]: `DICESONICE.Material${key.capitalize()}`
|
||||||
|
}),
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
context.diceSoNiceSystems = Object.fromEntries(
|
||||||
|
[...game.dice3d.DiceFactory.systems].map(([k, v]) => [k, v.name])
|
||||||
|
);
|
||||||
|
|
||||||
foundry.utils.mergeObject(context.dsnTabs, [
|
foundry.utils.mergeObject(
|
||||||
"hope",
|
context.dsnTabs,
|
||||||
"fear",
|
['hope', 'fear', 'advantage', 'disadvantage'].reduce(
|
||||||
"advantage",
|
(acc, key) => ({
|
||||||
"disadvantage",
|
...acc,
|
||||||
].reduce((acc, key) => ({
|
[key]: {
|
||||||
...acc,
|
values: this.setting.diceSoNice[key],
|
||||||
[key]: {
|
fields: this.setting.schema.getField(`diceSoNice.${key}`).fields
|
||||||
values: this.setting.diceSoNice[key],
|
}
|
||||||
fields: this.setting.schema.getField(`diceSoNice.${key}`).fields,
|
}),
|
||||||
}
|
{}
|
||||||
}), {}));
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -169,7 +181,7 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
|
||||||
* @type {ApplicationClickAction}
|
* @type {ApplicationClickAction}
|
||||||
*/
|
*/
|
||||||
static async #onPreview(_, target) {
|
static async #onPreview(_, target) {
|
||||||
const formData = new foundry.applications.ux.FormDataExtended(target.closest("form"));
|
const formData = new foundry.applications.ux.FormDataExtended(target.closest('form'));
|
||||||
const { diceSoNice } = foundry.utils.expandObject(formData.object);
|
const { diceSoNice } = foundry.utils.expandObject(formData.object);
|
||||||
const { key } = target.dataset;
|
const { key } = target.dataset;
|
||||||
const faces = ['advantage', 'disadvantage'].includes(key) ? 'd6' : 'd12';
|
const faces = ['advantage', 'disadvantage'].includes(key) ? 'd6' : 'd12';
|
||||||
|
|
@ -181,10 +193,10 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset the form back to default values.
|
* Reset the form back to default values.
|
||||||
* @this {DHAppearanceSettings}
|
* @this {DHAppearanceSettings}
|
||||||
* @type {ApplicationClickAction}
|
* @type {ApplicationClickAction}
|
||||||
*/
|
*/
|
||||||
static async #onReset() {
|
static async #onReset() {
|
||||||
this.setting = new this.setting.constructor();
|
this.setting = new this.setting.constructor();
|
||||||
this.render({ force: false });
|
this.render({ force: false });
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
settings: { template: 'systems/daggerheart/templates/settings/homebrew-settings/settings.hbs' },
|
settings: { template: 'systems/daggerheart/templates/settings/homebrew-settings/settings.hbs' },
|
||||||
domains: { template: 'systems/daggerheart/templates/settings/homebrew-settings/domains.hbs' },
|
domains: { template: 'systems/daggerheart/templates/settings/homebrew-settings/domains.hbs' },
|
||||||
types: { template: 'systems/daggerheart/templates/settings/homebrew-settings/types.hbs' },
|
types: { template: 'systems/daggerheart/templates/settings/homebrew-settings/types.hbs' },
|
||||||
|
itemTypes: { template: 'systems/daggerheart/templates/settings/homebrew-settings/itemFeatures.hbs' },
|
||||||
downtime: { template: 'systems/daggerheart/templates/settings/homebrew-settings/downtime.hbs' },
|
downtime: { template: 'systems/daggerheart/templates/settings/homebrew-settings/downtime.hbs' },
|
||||||
footer: { template: 'systems/daggerheart/templates/settings/homebrew-settings/footer.hbs' }
|
footer: { template: 'systems/daggerheart/templates/settings/homebrew-settings/footer.hbs' }
|
||||||
};
|
};
|
||||||
|
|
@ -60,7 +61,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
static TABS = {
|
static TABS = {
|
||||||
main: {
|
main: {
|
||||||
tabs: [{ id: 'settings' }, { id: 'domains' }, { id: 'types' }, { id: 'downtime' }],
|
tabs: [{ id: 'settings' }, { id: 'domains' }, { id: 'types' }, { id: 'itemFeatures' }, { id: 'downtime' }],
|
||||||
initial: 'settings',
|
initial: 'settings',
|
||||||
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
|
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
|
||||||
}
|
}
|
||||||
|
|
@ -115,33 +116,53 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
}
|
}
|
||||||
|
|
||||||
static async addItem(_, target) {
|
static async addItem(_, target) {
|
||||||
await this.settings.updateSource({
|
const { type } = target.dataset;
|
||||||
[`restMoves.${target.dataset.type}.moves.${foundry.utils.randomID()}`]: {
|
if (['shortRest', 'longRest'].includes(type)) {
|
||||||
name: game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.newDowntimeMove'),
|
await this.settings.updateSource({
|
||||||
img: 'icons/magic/life/cross-worn-green.webp',
|
[`restMoves.${type}.moves.${foundry.utils.randomID()}`]: {
|
||||||
description: '',
|
name: game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.newDowntimeMove'),
|
||||||
actions: []
|
img: 'icons/magic/life/cross-worn-green.webp',
|
||||||
}
|
description: '',
|
||||||
});
|
actions: []
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (['armorFeatures', 'weaponFeatures'].includes(type)) {
|
||||||
|
await this.settings.updateSource({
|
||||||
|
[`itemFeatures.${type}.${foundry.utils.randomID()}`]: {
|
||||||
|
name: game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.newFeature'),
|
||||||
|
img: 'icons/magic/life/cross-worn-green.webp',
|
||||||
|
description: '',
|
||||||
|
actions: [],
|
||||||
|
effects: []
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
static async editItem(_, target) {
|
static async editItem(_, target) {
|
||||||
const move = this.settings.restMoves[target.dataset.type].moves[target.dataset.id];
|
const { type, id } = target.dataset;
|
||||||
const path = `restMoves.${target.dataset.type}.moves.${target.dataset.id}`;
|
const isDowntime = ['shortRest', 'longRest'].includes(type);
|
||||||
const editedMove = await game.system.api.applications.sheetConfigs.DowntimeConfig.configure(
|
const path = isDowntime ? `restMoves.${type}.moves.${id}` : `itemFeatures.${type}.${id}`;
|
||||||
move,
|
const featureBase = isDowntime ? this.settings.restMoves[type].moves[id] : this.settings.itemFeatures[type][id];
|
||||||
path,
|
|
||||||
this.settings
|
|
||||||
);
|
|
||||||
if (!editedMove) return;
|
|
||||||
|
|
||||||
await this.updateAction.bind(this)(editedMove, target.dataset.type, target.dataset.id);
|
const editedBase = await game.system.api.applications.sheetConfigs.SettingFeatureConfig.configure(
|
||||||
|
featureBase,
|
||||||
|
path,
|
||||||
|
this.settings,
|
||||||
|
{ hasIcon: isDowntime, hasEffects: !isDowntime }
|
||||||
|
);
|
||||||
|
if (!editedBase) return;
|
||||||
|
|
||||||
|
await this.updateAction.bind(this)(editedBase, target.dataset.type, target.dataset.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateAction(data, type, id) {
|
async updateAction(data, type, id) {
|
||||||
|
const isDowntime = ['shortRest', 'longRest'].includes(type);
|
||||||
|
const path = isDowntime ? `restMoves.${type}.moves` : `itemFeatures.${type}`;
|
||||||
await this.settings.updateSource({
|
await this.settings.updateSource({
|
||||||
[`restMoves.${type}.moves.${id}`]: {
|
[`${path}.${id}`]: {
|
||||||
actions: data.actions,
|
actions: data.actions,
|
||||||
name: data.name,
|
name: data.name,
|
||||||
icon: data.icon,
|
icon: data.icon,
|
||||||
|
|
@ -149,12 +170,16 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
description: data.description
|
description: data.description
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
static async removeItem(_, target) {
|
static async removeItem(_, target) {
|
||||||
|
const { type, id } = target.dataset;
|
||||||
|
const isDowntime = ['shortRest', 'longRest'].includes(type);
|
||||||
|
const path = isDowntime ? `restMoves.${type}.moves` : `itemFeatures.${type}`;
|
||||||
await this.settings.updateSource({
|
await this.settings.updateSource({
|
||||||
[`restMoves.${target.dataset.type}.moves.-=${target.dataset.id}`]: null
|
[`${path}.-=${id}`]: null
|
||||||
});
|
});
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ export { default as ActionConfig } from './action-config.mjs';
|
||||||
export { default as CharacterSettings } from './character-settings.mjs';
|
export { default as CharacterSettings } from './character-settings.mjs';
|
||||||
export { default as AdversarySettings } from './adversary-settings.mjs';
|
export { default as AdversarySettings } from './adversary-settings.mjs';
|
||||||
export { default as CompanionSettings } from './companion-settings.mjs';
|
export { default as CompanionSettings } from './companion-settings.mjs';
|
||||||
export { default as DowntimeConfig } from './downtimeConfig.mjs';
|
export { default as SettingActiveEffectConfig } from './setting-active-effect-config.mjs';
|
||||||
|
export { default as SettingFeatureConfig } from './setting-feature-config.mjs';
|
||||||
export { default as EnvironmentSettings } from './environment-settings.mjs';
|
export { default as EnvironmentSettings } from './environment-settings.mjs';
|
||||||
export { default as ActiveEffectConfig } from './activeEffectConfig.mjs';
|
export { default as ActiveEffectConfig } from './activeEffectConfig.mjs';
|
||||||
export { default as DhTokenConfig } from './token-config.mjs';
|
export { default as DhTokenConfig } from './token-config.mjs';
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
|
||||||
group: 'primary',
|
group: 'primary',
|
||||||
id: 'base',
|
id: 'base',
|
||||||
icon: null,
|
icon: null,
|
||||||
label: 'Base'
|
label: 'DAGGERHEART.GENERAL.Tabs.base'
|
||||||
},
|
},
|
||||||
config: {
|
config: {
|
||||||
active: false,
|
active: false,
|
||||||
|
|
@ -74,7 +74,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
|
||||||
group: 'primary',
|
group: 'primary',
|
||||||
id: 'config',
|
id: 'config',
|
||||||
icon: null,
|
icon: null,
|
||||||
label: 'Configuration'
|
label: 'DAGGERHEART.GENERAL.Tabs.configuration'
|
||||||
},
|
},
|
||||||
effect: {
|
effect: {
|
||||||
active: false,
|
active: false,
|
||||||
|
|
@ -82,7 +82,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
|
||||||
group: 'primary',
|
group: 'primary',
|
||||||
id: 'effect',
|
id: 'effect',
|
||||||
icon: null,
|
icon: null,
|
||||||
label: 'Effect'
|
label: 'DAGGERHEART.GENERAL.Tabs.effects'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -138,7 +138,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.action.parent.metadata.isQuantifiable) {
|
if (this.action.parent.metadata?.isQuantifiable) {
|
||||||
options.quantity = {
|
options.quantity = {
|
||||||
label: 'DAGGERHEART.GENERAL.itemQuantity',
|
label: 'DAGGERHEART.GENERAL.itemQuantity',
|
||||||
group: 'Global'
|
group: 'Global'
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,13 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _prepareContext(options) {
|
||||||
|
const context = await super._prepareContext(options);
|
||||||
|
context.systemFields = context.document.system.schema.fields;
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
async _preparePartContext(partId, context) {
|
async _preparePartContext(partId, context) {
|
||||||
const partContext = await super._preparePartContext(partId, context);
|
const partContext = await super._preparePartContext(partId, context);
|
||||||
switch (partId) {
|
switch (partId) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,227 @@
|
||||||
|
import autocomplete from 'autocompleter';
|
||||||
|
|
||||||
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
||||||
|
export default class SettingActiveEffectConfig extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
|
constructor(effect) {
|
||||||
|
super({});
|
||||||
|
|
||||||
|
this.effect = foundry.utils.deepClone(effect);
|
||||||
|
const ignoredActorKeys = ['config', 'DhEnvironment'];
|
||||||
|
this.changeChoices = Object.keys(game.system.api.models.actors).reduce((acc, key) => {
|
||||||
|
if (!ignoredActorKeys.includes(key)) {
|
||||||
|
const model = game.system.api.models.actors[key];
|
||||||
|
const attributes = CONFIG.Token.documentClass.getTrackedAttributes(model);
|
||||||
|
const group = game.i18n.localize(model.metadata.label);
|
||||||
|
const choices = CONFIG.Token.documentClass
|
||||||
|
.getTrackedAttributeChoices(attributes, model)
|
||||||
|
.map(x => ({ ...x, group: group }));
|
||||||
|
acc.push(...choices);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
classes: ['daggerheart', 'sheet', 'dh-style', 'active-effect-config'],
|
||||||
|
tag: 'form',
|
||||||
|
position: {
|
||||||
|
width: 560
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
submitOnChange: false,
|
||||||
|
closeOnSubmit: false,
|
||||||
|
handler: SettingActiveEffectConfig.#onSubmit
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
editImage: SettingActiveEffectConfig.#editImage,
|
||||||
|
addChange: SettingActiveEffectConfig.#addChange,
|
||||||
|
deleteChange: SettingActiveEffectConfig.#deleteChange
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static PARTS = {
|
||||||
|
header: { template: 'systems/daggerheart/templates/sheets/activeEffect/header.hbs' },
|
||||||
|
tabs: { template: 'templates/generic/tab-navigation.hbs' },
|
||||||
|
details: { template: 'systems/daggerheart/templates/sheets/activeEffect/details.hbs', scrollable: [''] },
|
||||||
|
settings: { template: 'systems/daggerheart/templates/sheets/activeEffect/settings.hbs' },
|
||||||
|
changes: {
|
||||||
|
template: 'systems/daggerheart/templates/sheets/activeEffect/changes.hbs',
|
||||||
|
scrollable: ['ol[data-changes]']
|
||||||
|
},
|
||||||
|
footer: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-form-footer.hbs' }
|
||||||
|
};
|
||||||
|
|
||||||
|
static TABS = {
|
||||||
|
sheet: {
|
||||||
|
tabs: [
|
||||||
|
{ id: 'details', icon: 'fa-solid fa-book' },
|
||||||
|
{ id: 'settings', icon: 'fa-solid fa-bars', label: 'DAGGERHEART.GENERAL.Tabs.settings' },
|
||||||
|
{ id: 'changes', icon: 'fa-solid fa-gears' }
|
||||||
|
],
|
||||||
|
initial: 'details',
|
||||||
|
labelPrefix: 'EFFECT.TABS'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**@inheritdoc */
|
||||||
|
async _onFirstRender(context, options) {
|
||||||
|
await super._onFirstRender(context, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _prepareContext(_options) {
|
||||||
|
const context = await super._prepareContext(_options);
|
||||||
|
context.source = this.effect;
|
||||||
|
context.fields = game.system.api.documents.DhActiveEffect.schema.fields;
|
||||||
|
context.systemFields = game.system.api.data.activeEffects.BaseEffect._schema.fields;
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
_attachPartListeners(partId, htmlElement, options) {
|
||||||
|
super._attachPartListeners(partId, htmlElement, options);
|
||||||
|
const changeChoices = this.changeChoices;
|
||||||
|
|
||||||
|
htmlElement.querySelectorAll('.effect-change-input').forEach(element => {
|
||||||
|
autocomplete({
|
||||||
|
input: element,
|
||||||
|
fetch: function (text, update) {
|
||||||
|
if (!text) {
|
||||||
|
update(changeChoices);
|
||||||
|
} else {
|
||||||
|
text = text.toLowerCase();
|
||||||
|
var suggestions = changeChoices.filter(n => n.label.toLowerCase().includes(text));
|
||||||
|
update(suggestions);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
render: function (item, search) {
|
||||||
|
const label = game.i18n.localize(item.label);
|
||||||
|
const matchIndex = label.toLowerCase().indexOf(search);
|
||||||
|
|
||||||
|
const beforeText = label.slice(0, matchIndex);
|
||||||
|
const matchText = label.slice(matchIndex, matchIndex + search.length);
|
||||||
|
const after = label.slice(matchIndex + search.length, label.length);
|
||||||
|
|
||||||
|
const element = document.createElement('li');
|
||||||
|
element.innerHTML = `${beforeText}${matchText ? `<strong>${matchText}</strong>` : ''}${after}`;
|
||||||
|
if (item.hint) {
|
||||||
|
element.dataset.tooltip = game.i18n.localize(item.hint);
|
||||||
|
}
|
||||||
|
|
||||||
|
return element;
|
||||||
|
},
|
||||||
|
renderGroup: function (label) {
|
||||||
|
const itemElement = document.createElement('div');
|
||||||
|
itemElement.textContent = game.i18n.localize(label);
|
||||||
|
return itemElement;
|
||||||
|
},
|
||||||
|
onSelect: function (item) {
|
||||||
|
element.value = `system.${item.value}`;
|
||||||
|
},
|
||||||
|
click: e => e.fetch(),
|
||||||
|
customize: function (_input, _inputRect, container) {
|
||||||
|
container.style.zIndex = foundry.applications.api.ApplicationV2._maxZ;
|
||||||
|
},
|
||||||
|
minLength: 0
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async _preparePartContext(partId, context) {
|
||||||
|
if (partId in context.tabs) context.tab = context.tabs[partId];
|
||||||
|
switch (partId) {
|
||||||
|
case 'details':
|
||||||
|
context.isActorEffect = false;
|
||||||
|
context.isItemEffect = true;
|
||||||
|
const useGeneric = game.settings.get(
|
||||||
|
CONFIG.DH.id,
|
||||||
|
CONFIG.DH.SETTINGS.gameSettings.appearance
|
||||||
|
).showGenericStatusEffects;
|
||||||
|
if (!useGeneric) {
|
||||||
|
context.statuses = Object.values(CONFIG.DH.GENERAL.conditions).map(status => ({
|
||||||
|
value: status.id,
|
||||||
|
label: game.i18n.localize(status.name)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'changes':
|
||||||
|
context.modes = Object.entries(CONST.ACTIVE_EFFECT_MODES).reduce((modes, [key, value]) => {
|
||||||
|
modes[value] = game.i18n.localize(`EFFECT.MODE_${key}`);
|
||||||
|
return modes;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
context.priorities = ActiveEffectConfig.DEFAULT_PRIORITIES;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #onSubmit(event, form, formData) {
|
||||||
|
this.data = foundry.utils.expandObject(formData.object);
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edit a Document image.
|
||||||
|
* @this {DocumentSheetV2}
|
||||||
|
* @type {ApplicationClickAction}
|
||||||
|
*/
|
||||||
|
static async #editImage(_event, target) {
|
||||||
|
if (target.nodeName !== 'IMG') {
|
||||||
|
throw new Error('The editImage action is available only for IMG elements.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const attr = target.dataset.edit;
|
||||||
|
const current = foundry.utils.getProperty(this.effect, attr);
|
||||||
|
const fp = new FilePicker.implementation({
|
||||||
|
current,
|
||||||
|
type: 'image',
|
||||||
|
callback: path => (target.src = path),
|
||||||
|
position: {
|
||||||
|
top: this.position.top + 40,
|
||||||
|
left: this.position.left + 10
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await fp.browse();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new change to the effect's changes array.
|
||||||
|
* @this {ActiveEffectConfig}
|
||||||
|
* @type {ApplicationClickAction}
|
||||||
|
*/
|
||||||
|
static async #addChange() {
|
||||||
|
const submitData = foundry.utils.expandObject(new FormDataExtended(this.form).object);
|
||||||
|
const changes = Object.values(submitData.changes ?? {});
|
||||||
|
changes.push({});
|
||||||
|
|
||||||
|
this.effect.changes = changes;
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a change from the effect's changes array.
|
||||||
|
* @this {ActiveEffectConfig}
|
||||||
|
* @type {ApplicationClickAction}
|
||||||
|
*/
|
||||||
|
static async #deleteChange(event) {
|
||||||
|
const submitData = foundry.utils.expandObject(new FormDataExtended(this.form).object);
|
||||||
|
const changes = Object.values(submitData.changes);
|
||||||
|
const row = event.target.closest('li');
|
||||||
|
const index = Number(row.dataset.index) || 0;
|
||||||
|
changes.splice(index, 1);
|
||||||
|
|
||||||
|
this.effect.changes = changes;
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async configure(effect, options = {}) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const app = new this(effect, options);
|
||||||
|
app.addEventListener('close', () => resolve(app.data), { once: true });
|
||||||
|
app.render({ force: true });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,8 +3,8 @@ import DHActionConfig from './action-config.mjs';
|
||||||
|
|
||||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
||||||
export default class DowntimeConfig extends HandlebarsApplicationMixin(ApplicationV2) {
|
export default class SettingFeatureConfig extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
constructor(move, movePath, settings, options) {
|
constructor(move, movePath, settings, optionalParts, options) {
|
||||||
super(options);
|
super(options);
|
||||||
|
|
||||||
this.move = move;
|
this.move = move;
|
||||||
|
|
@ -12,6 +12,10 @@ export default class DowntimeConfig extends HandlebarsApplicationMixin(Applicati
|
||||||
this.movePath = movePath;
|
this.movePath = movePath;
|
||||||
this.actionsPath = `${movePath}.actions`;
|
this.actionsPath = `${movePath}.actions`;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
|
|
||||||
|
const { hasIcon, hasEffects } = optionalParts;
|
||||||
|
this.hasIcon = hasIcon;
|
||||||
|
this.hasEffects = hasEffects;
|
||||||
}
|
}
|
||||||
|
|
||||||
get title() {
|
get title() {
|
||||||
|
|
@ -30,6 +34,7 @@ export default class DowntimeConfig extends HandlebarsApplicationMixin(Applicati
|
||||||
addItem: this.addItem,
|
addItem: this.addItem,
|
||||||
editItem: this.editItem,
|
editItem: this.editItem,
|
||||||
removeItem: this.removeItem,
|
removeItem: this.removeItem,
|
||||||
|
addEffect: this.addEffect,
|
||||||
resetMoves: this.resetMoves,
|
resetMoves: this.resetMoves,
|
||||||
saveForm: this.saveForm
|
saveForm: this.saveForm
|
||||||
},
|
},
|
||||||
|
|
@ -41,13 +46,14 @@ export default class DowntimeConfig extends HandlebarsApplicationMixin(Applicati
|
||||||
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
||||||
main: { template: 'systems/daggerheart/templates/settings/downtime-config/main.hbs' },
|
main: { template: 'systems/daggerheart/templates/settings/downtime-config/main.hbs' },
|
||||||
actions: { template: 'systems/daggerheart/templates/settings/downtime-config/actions.hbs' },
|
actions: { template: 'systems/daggerheart/templates/settings/downtime-config/actions.hbs' },
|
||||||
|
effects: { template: 'systems/daggerheart/templates/settings/downtime-config/effects.hbs' },
|
||||||
footer: { template: 'systems/daggerheart/templates/settings/downtime-config/footer.hbs' }
|
footer: { template: 'systems/daggerheart/templates/settings/downtime-config/footer.hbs' }
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
static TABS = {
|
static TABS = {
|
||||||
primary: {
|
primary: {
|
||||||
tabs: [{ id: 'main' }, { id: 'actions' }],
|
tabs: [{ id: 'main' }, { id: 'actions' }, { id: 'effects' }],
|
||||||
initial: 'main',
|
initial: 'main',
|
||||||
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
|
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
|
||||||
}
|
}
|
||||||
|
|
@ -55,6 +61,9 @@ export default class DowntimeConfig extends HandlebarsApplicationMixin(Applicati
|
||||||
|
|
||||||
async _prepareContext(_options) {
|
async _prepareContext(_options) {
|
||||||
const context = await super._prepareContext(_options);
|
const context = await super._prepareContext(_options);
|
||||||
|
context.tabs = this._filterTabs(context.tabs);
|
||||||
|
context.hasIcon = this.hasIcon;
|
||||||
|
context.hasEffects = this.hasEffects;
|
||||||
context.move = this.move;
|
context.move = this.move;
|
||||||
context.move.enrichedDescription = await foundry.applications.ux.TextEditor.enrichHTML(
|
context.move.enrichedDescription = await foundry.applications.ux.TextEditor.enrichHTML(
|
||||||
context.move.description
|
context.move.description
|
||||||
|
|
@ -130,13 +139,30 @@ export default class DowntimeConfig extends HandlebarsApplicationMixin(Applicati
|
||||||
}
|
}
|
||||||
|
|
||||||
static async editItem(_, target) {
|
static async editItem(_, target) {
|
||||||
const actionId = target.dataset.id;
|
const { type, id } = target.dataset;
|
||||||
const action = this.move.actions.get(actionId);
|
if (type === 'effect') {
|
||||||
await new DHActionConfig(action, async updatedMove => {
|
const effectIndex = this.move.effects.findIndex(x => x.id === id);
|
||||||
await this.settings.updateSource({ [`${this.actionsPath}.${actionId}`]: updatedMove });
|
const effect = this.move.effects[effectIndex];
|
||||||
|
const updatedEffect =
|
||||||
|
await game.system.api.applications.sheetConfigs.SettingActiveEffectConfig.configure(effect);
|
||||||
|
if (!updatedEffect) return;
|
||||||
|
|
||||||
|
await this.settings.updateSource({
|
||||||
|
[`${this.movePath}.effects`]: this.move.effects.reduce((acc, effect, index) => {
|
||||||
|
acc.push(index === effectIndex ? { ...updatedEffect, id: effect.id } : effect);
|
||||||
|
return acc;
|
||||||
|
}, [])
|
||||||
|
});
|
||||||
this.move = foundry.utils.getProperty(this.settings, this.movePath);
|
this.move = foundry.utils.getProperty(this.settings, this.movePath);
|
||||||
this.render();
|
this.render();
|
||||||
}).render(true);
|
} else {
|
||||||
|
const action = this.move.actions.get(id);
|
||||||
|
await new DHActionConfig(action, async updatedMove => {
|
||||||
|
await this.settings.updateSource({ [`${this.actionsPath}.${id}`]: updatedMove });
|
||||||
|
this.move = foundry.utils.getProperty(this.settings, this.movePath);
|
||||||
|
this.render();
|
||||||
|
}).render(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async removeItem(_, target) {
|
static async removeItem(_, target) {
|
||||||
|
|
@ -145,16 +171,38 @@ export default class DowntimeConfig extends HandlebarsApplicationMixin(Applicati
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async addEffect(_, target) {
|
||||||
|
const currentEffects = foundry.utils.getProperty(this.settings, `${this.movePath}.effects`);
|
||||||
|
await this.settings.updateSource({
|
||||||
|
[`${this.movePath}.effects`]: [
|
||||||
|
...currentEffects,
|
||||||
|
game.system.api.data.activeEffects.BaseEffect.getDefaultObject()
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
this.move = foundry.utils.getProperty(this.settings, this.movePath);
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
static resetMoves() {}
|
static resetMoves() {}
|
||||||
|
|
||||||
|
_filterTabs(tabs) {
|
||||||
|
return this.hasEffects
|
||||||
|
? tabs
|
||||||
|
: Object.keys(tabs).reduce((acc, key) => {
|
||||||
|
if (key !== 'effects') acc[key] = tabs[key];
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
_onClose(options = {}) {
|
_onClose(options = {}) {
|
||||||
if (!options.submitted) this.move = null;
|
if (!options.submitted) this.move = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async configure(move, movePath, settings, options = {}) {
|
static async configure(move, movePath, settings, optionalParts, options = {}) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const app = new this(move, movePath, settings, options);
|
const app = new this(move, movePath, settings, optionalParts, options);
|
||||||
app.addEventListener('close', () => resolve(app.move), { once: true });
|
app.addEventListener('close', () => resolve(app.move), { once: true });
|
||||||
app.render({ force: true });
|
app.render({ force: true });
|
||||||
});
|
});
|
||||||
|
|
@ -232,7 +232,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
async _prepareLoadoutContext(context, _options) {
|
async _prepareLoadoutContext(context, _options) {
|
||||||
context.cardView = !game.user.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.displayDomainCardsAsList);
|
context.cardView = game.user.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.displayDomainCardsAsCard);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -722,8 +722,8 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
* @type {ApplicationClickAction}
|
* @type {ApplicationClickAction}
|
||||||
*/
|
*/
|
||||||
static async #toggleLoadoutView(_, button) {
|
static async #toggleLoadoutView(_, button) {
|
||||||
const newAbilityView = button.dataset.value !== 'true';
|
const newAbilityView = button.dataset.value === 'true';
|
||||||
await game.user.setFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.displayDomainCardsAsList, newAbilityView);
|
await game.user.setFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.displayDomainCardsAsCard, newAbilityView);
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -416,9 +416,12 @@ export default function DHApplicationMixin(Base) {
|
||||||
icon: 'fa-solid fa-ban',
|
icon: 'fa-solid fa-ban',
|
||||||
condition: target => {
|
condition: target => {
|
||||||
const doc = getDocFromElementSync(target);
|
const doc = getDocFromElementSync(target);
|
||||||
return doc && doc.system?.actions?.some(a => a.type === "beastform");
|
return doc && doc.system?.actions?.some(a => a.type === 'beastform');
|
||||||
},
|
},
|
||||||
callback: async target => game.system.api.fields.ActionFields.BeastformField.handleActiveTransformations.call(await getDocFromElement(target))
|
callback: async target =>
|
||||||
|
game.system.api.fields.ActionFields.BeastformField.handleActiveTransformations.call(
|
||||||
|
await getDocFromElement(target)
|
||||||
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
options.unshift({
|
options.unshift({
|
||||||
|
|
@ -433,7 +436,7 @@ export default function DHApplicationMixin(Base) {
|
||||||
action = doc?.system?.attack ?? doc;
|
action = doc?.system?.attack ?? doc;
|
||||||
const config = action.prepareConfig(event);
|
const config = action.prepareConfig(event);
|
||||||
config.hasRoll = false;
|
config.hasRoll = false;
|
||||||
return action && action.workflow.get("damage").execute(config, null, true);
|
return action && action.workflow.get('damage').execute(config, null, true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -452,7 +455,7 @@ export default function DHApplicationMixin(Base) {
|
||||||
options.push({
|
options.push({
|
||||||
name: 'DAGGERHEART.APPLICATIONS.ContextMenu.sendToChat',
|
name: 'DAGGERHEART.APPLICATIONS.ContextMenu.sendToChat',
|
||||||
icon: 'fa-solid fa-message',
|
icon: 'fa-solid fa-message',
|
||||||
callback: async target => (await getDocFromElement(target)).toChat(this.document.id)
|
callback: async target => (await getDocFromElement(target)).toChat(this.document.uuid)
|
||||||
});
|
});
|
||||||
|
|
||||||
if (deletable)
|
if (deletable)
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ export default class ArmorSheet extends ItemAttachmentSheet(DHBaseItemSheet) {
|
||||||
tagifyConfigs: [
|
tagifyConfigs: [
|
||||||
{
|
{
|
||||||
selector: '.features-input',
|
selector: '.features-input',
|
||||||
options: () => CONFIG.DH.ITEM.armorFeatures,
|
options: () => CONFIG.DH.ITEM.orderedArmorFeatures(),
|
||||||
callback: ArmorSheet.#onFeatureSelect
|
callback: ArmorSheet.#onFeatureSelect
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ export default class WeaponSheet extends ItemAttachmentSheet(DHBaseItemSheet) {
|
||||||
tagifyConfigs: [
|
tagifyConfigs: [
|
||||||
{
|
{
|
||||||
selector: '.features-input',
|
selector: '.features-input',
|
||||||
options: () => CONFIG.DH.ITEM.weaponFeatures,
|
options: () => CONFIG.DH.ITEM.orderedWeaponFeatures(),
|
||||||
callback: WeaponSheet.#onFeatureSelect
|
callback: WeaponSheet.#onFeatureSelect
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
2
module/applications/sidebar/_module.mjs
Normal file
2
module/applications/sidebar/_module.mjs
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { default as DaggerheartMenu } from './tabs/daggerheartMenu.mjs';
|
||||||
|
export { default as DhSidebar } from './sidebar.mjs';
|
||||||
33
module/applications/sidebar/sidebar.mjs
Normal file
33
module/applications/sidebar/sidebar.mjs
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
export default class DhSidebar extends Sidebar {
|
||||||
|
/** @override */
|
||||||
|
static TABS = {
|
||||||
|
...super.TABS,
|
||||||
|
daggerheartMenu: {
|
||||||
|
tooltip: 'DAGGERHEART.UI.Sidebar.daggerheartMenu.title',
|
||||||
|
img: 'systems/daggerheart/assets/logos/FoundryBorneLogoWhite.svg'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
tabs: {
|
||||||
|
id: 'tabs',
|
||||||
|
template: 'systems/daggerheart/templates/sidebar/tabs.hbs'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
async _prepareTabContext(context, options) {
|
||||||
|
context.tabs = Object.entries(this.constructor.TABS).reduce((obj, [k, v]) => {
|
||||||
|
let { documentName, gmOnly, tooltip, icon, img } = v;
|
||||||
|
if (gmOnly && !game.user.isGM) return obj;
|
||||||
|
if (documentName) {
|
||||||
|
tooltip ??= getDocumentClass(documentName).metadata.labelPlural;
|
||||||
|
icon ??= CONFIG[documentName]?.sidebarIcon;
|
||||||
|
}
|
||||||
|
obj[k] = { tooltip, icon, img };
|
||||||
|
obj[k].active = this.tabGroups.primary === k;
|
||||||
|
return obj;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
}
|
||||||
160
module/applications/sidebar/tabs/daggerheartMenu.mjs
Normal file
160
module/applications/sidebar/tabs/daggerheartMenu.mjs
Normal file
|
|
@ -0,0 +1,160 @@
|
||||||
|
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
||||||
|
const { AbstractSidebarTab } = foundry.applications.sidebar;
|
||||||
|
/**
|
||||||
|
* The daggerheart menu tab.
|
||||||
|
* @extends {AbstractSidebarTab}
|
||||||
|
* @mixes HandlebarsApplication
|
||||||
|
*/
|
||||||
|
export default class DaggerheartMenu extends HandlebarsApplicationMixin(AbstractSidebarTab) {
|
||||||
|
constructor(options) {
|
||||||
|
super(options);
|
||||||
|
|
||||||
|
this.refreshSelections = DaggerheartMenu.#defaultRefreshSelections();
|
||||||
|
}
|
||||||
|
|
||||||
|
static #defaultRefreshSelections() {
|
||||||
|
return {
|
||||||
|
session: { selected: false, label: game.i18n.localize('DAGGERHEART.GENERAL.RefreshType.session') },
|
||||||
|
scene: { selected: false, label: game.i18n.localize('DAGGERHEART.GENERAL.RefreshType.scene') },
|
||||||
|
longRest: { selected: false, label: game.i18n.localize('DAGGERHEART.GENERAL.RefreshType.longrest') },
|
||||||
|
shortRest: { selected: false, label: game.i18n.localize('DAGGERHEART.GENERAL.RefreshType.shortrest') }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
classes: ['dh-style'],
|
||||||
|
window: {
|
||||||
|
title: 'SIDEBAR.TabSettings'
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
selectRefreshable: DaggerheartMenu.#selectRefreshable,
|
||||||
|
refreshActors: DaggerheartMenu.#refreshActors
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static tabName = 'daggerheartMenu';
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
main: { template: 'systems/daggerheart/templates/sidebar/daggerheart-menu/main.hbs' }
|
||||||
|
};
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
async _prepareContext(options) {
|
||||||
|
const context = await super._prepareContext(options);
|
||||||
|
context.refreshables = this.refreshSelections;
|
||||||
|
context.disableRefresh = Object.values(this.refreshSelections).every(x => !x.selected);
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRefreshables(types) {
|
||||||
|
const refreshedActors = {};
|
||||||
|
for (let actor of game.actors) {
|
||||||
|
if (['character', 'adversary'].includes(actor.type) && actor.prototypeToken.actorLink) {
|
||||||
|
const updates = {};
|
||||||
|
for (let item of actor.items) {
|
||||||
|
if (item.system.metadata.hasResource && types.includes(item.system.resource?.recovery)) {
|
||||||
|
if (!refreshedActors[actor.id])
|
||||||
|
refreshedActors[actor.id] = { name: actor.name, img: actor.img, refreshed: new Set() };
|
||||||
|
refreshedActors[actor.id].refreshed.add(
|
||||||
|
game.i18n.localize(CONFIG.DH.GENERAL.refreshTypes[item.system.resource.recovery].label)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!updates[item.id]?.system) updates[item.id] = { system: {} };
|
||||||
|
|
||||||
|
const increasing =
|
||||||
|
item.system.resource.progression === CONFIG.DH.ITEM.itemResourceProgression.increasing.id;
|
||||||
|
updates[item.id].system = {
|
||||||
|
...updates[item.id].system,
|
||||||
|
'resource.value': increasing
|
||||||
|
? 0
|
||||||
|
: Roll.replaceFormulaData(item.system.resource.max, actor.getRollData())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (item.system.metadata.hasActions) {
|
||||||
|
const refreshTypes = new Set();
|
||||||
|
const actions = item.system.actions.filter(action => {
|
||||||
|
if (types.includes(action.uses.recovery)) {
|
||||||
|
refreshTypes.add(action.uses.recovery);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
if (actions.length === 0) continue;
|
||||||
|
|
||||||
|
if (!refreshedActors[actor.id])
|
||||||
|
refreshedActors[actor.id] = { name: actor.name, img: actor.img, refreshed: new Set() };
|
||||||
|
refreshedActors[actor.id].refreshed.add(
|
||||||
|
...refreshTypes.map(type => game.i18n.localize(CONFIG.DH.GENERAL.refreshTypes[type].label))
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!updates[item.id]?.system) updates[item.id] = { system: {} };
|
||||||
|
|
||||||
|
updates[item.id].system = {
|
||||||
|
...updates[item.id].system,
|
||||||
|
...actions.reduce(
|
||||||
|
(acc, action) => {
|
||||||
|
acc.actions[action.id] = { 'uses.value': 0 };
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{ actions: updates[item.id].system.actions ?? {} }
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let key in updates) {
|
||||||
|
const update = updates[key];
|
||||||
|
await actor.items.get(key).update(update);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return refreshedActors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Application Clicks Actions */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
static async #selectRefreshable(_event, button) {
|
||||||
|
const { type } = button.dataset;
|
||||||
|
this.refreshSelections[type].selected = !this.refreshSelections[type].selected;
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #refreshActors() {
|
||||||
|
const refreshKeys = Object.keys(this.refreshSelections).filter(key => this.refreshSelections[key].selected);
|
||||||
|
await this.getRefreshables(refreshKeys);
|
||||||
|
const types = refreshKeys.map(x => this.refreshSelections[x].label).join(', ');
|
||||||
|
ui.notifications.info(
|
||||||
|
game.i18n.format('DAGGERHEART.UI.Notifications.gmMenuRefresh', {
|
||||||
|
types: `[${types}]`
|
||||||
|
})
|
||||||
|
);
|
||||||
|
this.refreshSelections = DaggerheartMenu.#defaultRefreshSelections();
|
||||||
|
|
||||||
|
const cls = getDocumentClass('ChatMessage');
|
||||||
|
const msg = {
|
||||||
|
user: game.user.id,
|
||||||
|
content: await foundry.applications.handlebars.renderTemplate(
|
||||||
|
'systems/daggerheart/templates/ui/chat/refreshMessage.hbs',
|
||||||
|
{
|
||||||
|
types: types
|
||||||
|
}
|
||||||
|
),
|
||||||
|
title: game.i18n.localize('DAGGERHEART.UI.Chat.refreshMessage.title'),
|
||||||
|
speaker: cls.getSpeaker()
|
||||||
|
};
|
||||||
|
|
||||||
|
cls.create(msg);
|
||||||
|
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -112,7 +112,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
||||||
if (event.currentTarget.dataset.directDamage) {
|
if (event.currentTarget.dataset.directDamage) {
|
||||||
const config = action.prepareConfig(event);
|
const config = action.prepareConfig(event);
|
||||||
config.hasRoll = false;
|
config.hasRoll = false;
|
||||||
action.workflow.get("damage").execute(config, null, true);
|
action.workflow.get('damage').execute(config, null, true);
|
||||||
} else action.use(event);
|
} else action.use(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { emitAsGM, GMUpdateEvent, socketEvent } from "../../systemRegistration/socket.mjs";
|
import { emitAsGM, GMUpdateEvent, socketEvent } from '../../systemRegistration/socket.mjs';
|
||||||
|
|
||||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
||||||
|
|
@ -106,6 +106,10 @@ export default class FearTracker extends HandlebarsApplicationMixin(ApplicationV
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateFear(value) {
|
async updateFear(value) {
|
||||||
return emitAsGM(GMUpdateEvent.UpdateFear, game.settings.set.bind(game.settings, CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear), value);
|
return emitAsGM(
|
||||||
|
GMUpdateEvent.UpdateFear,
|
||||||
|
game.settings.set.bind(game.settings, CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear),
|
||||||
|
value
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -86,10 +86,8 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
this.presets = options.presets ?? {};
|
this.presets = options.presets ?? {};
|
||||||
|
|
||||||
const width = this.presets?.render?.noFolder === true || this.presets?.render?.lite === true ? 600 : 850;
|
const width = this.presets?.render?.noFolder === true || this.presets?.render?.lite === true ? 600 : 850;
|
||||||
if(this.rendered)
|
if (this.rendered) this.setPosition({ width });
|
||||||
this.setPosition({ width });
|
else options.position.width = width;
|
||||||
else
|
|
||||||
options.position.width = width;
|
|
||||||
|
|
||||||
await super._preRender(context, options);
|
await super._preRender(context, options);
|
||||||
}
|
}
|
||||||
|
|
@ -100,7 +98,9 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
|
|
||||||
this.element
|
this.element
|
||||||
.querySelectorAll('[data-action="selectFolder"]')
|
.querySelectorAll('[data-action="selectFolder"]')
|
||||||
.forEach(element => element.classList.toggle('is-selected', element.dataset.folderId === this.selectedMenu.path.join('.')));
|
.forEach(element =>
|
||||||
|
element.classList.toggle('is-selected', element.dataset.folderId === this.selectedMenu.path.join('.'))
|
||||||
|
);
|
||||||
|
|
||||||
this._createSearchFilter();
|
this._createSearchFilter();
|
||||||
|
|
||||||
|
|
@ -108,7 +108,8 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
this.element.classList.toggle('no-folder', this.presets?.render?.noFolder === true);
|
this.element.classList.toggle('no-folder', this.presets?.render?.noFolder === true);
|
||||||
this.element.classList.toggle('no-filter', this.presets?.render?.noFilter === true);
|
this.element.classList.toggle('no-filter', this.presets?.render?.noFilter === true);
|
||||||
this.element.querySelectorAll('.folder-list > [data-action="selectFolder"]').forEach(element => {
|
this.element.querySelectorAll('.folder-list > [data-action="selectFolder"]').forEach(element => {
|
||||||
element.hidden = this.presets.render?.folders?.length && !this.presets.render.folders.includes(element.dataset.folderId);
|
element.hidden =
|
||||||
|
this.presets.render?.folders?.length && !this.presets.render.folders.includes(element.dataset.folderId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -157,7 +158,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
: [];
|
: [];
|
||||||
folders.push(folder);
|
folders.push(folder);
|
||||||
});
|
});
|
||||||
folders.sort((a, b) => a.label.localeCompare(b.label))
|
folders.sort((a, b) => a.label.localeCompare(b.label));
|
||||||
|
|
||||||
return folders;
|
return folders;
|
||||||
}
|
}
|
||||||
|
|
@ -181,8 +182,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
|
|
||||||
await this.render({ force: true, presets: this.presets });
|
await this.render({ force: true, presets: this.presets });
|
||||||
|
|
||||||
if(this.selectedMenu?.data?.type?.length)
|
if (this.selectedMenu?.data?.type?.length) this.loadItems();
|
||||||
this.loadItems();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_replaceHTML(result, content, options) {
|
_replaceHTML(result, content, options) {
|
||||||
|
|
@ -201,24 +201,26 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
const items = await pack.getDocuments({ type__in: this.selectedMenu?.data?.type });
|
const items = await pack.getDocuments({ type__in: this.selectedMenu?.data?.type });
|
||||||
resolve(items);
|
resolve(items);
|
||||||
})
|
})
|
||||||
)
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
Promise.all(promises).then(async result => {
|
Promise.all(promises).then(async result => {
|
||||||
this.items = ItemBrowser.sortBy(result.flatMap(r => r), 'name');
|
this.items = ItemBrowser.sortBy(
|
||||||
|
result.flatMap(r => r),
|
||||||
|
'name'
|
||||||
|
);
|
||||||
this.fieldFilter = this._createFieldFilter();
|
this.fieldFilter = this._createFieldFilter();
|
||||||
|
|
||||||
if (this.presets?.filter) {
|
if (this.presets?.filter) {
|
||||||
Object.entries(this.presets.filter).forEach(
|
Object.entries(this.presets.filter).forEach(([k, v]) => {
|
||||||
([k, v]) => {
|
const filter = this.fieldFilter.find(c => c.name === k);
|
||||||
const filter = this.fieldFilter.find(c => c.name === k)
|
if (filter) filter.value = v.value;
|
||||||
if(filter) filter.value = v.value;
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
// await this._onInputFilterBrowser();
|
// await this._onInputFilterBrowser();
|
||||||
}
|
}
|
||||||
|
|
||||||
const filterList = await foundry.applications.handlebars.renderTemplate('systems/daggerheart/templates/ui/itemBrowser/filterContainer.hbs',
|
const filterList = await foundry.applications.handlebars.renderTemplate(
|
||||||
|
'systems/daggerheart/templates/ui/itemBrowser/filterContainer.hbs',
|
||||||
{
|
{
|
||||||
fieldFilter: this.fieldFilter,
|
fieldFilter: this.fieldFilter,
|
||||||
presets: this.presets,
|
presets: this.presets,
|
||||||
|
|
@ -228,12 +230,11 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
|
|
||||||
this.element.querySelector('.filter-content .wrapper').innerHTML = filterList;
|
this.element.querySelector('.filter-content .wrapper').innerHTML = filterList;
|
||||||
const filterContainer = this.element.querySelector('.filter-header > [data-action="expandContent"]');
|
const filterContainer = this.element.querySelector('.filter-header > [data-action="expandContent"]');
|
||||||
if(this.fieldFilter.length === 0)
|
if (this.fieldFilter.length === 0) filterContainer.setAttribute('disabled', '');
|
||||||
filterContainer.setAttribute('disabled', '');
|
else filterContainer.removeAttribute('disabled');
|
||||||
else
|
|
||||||
filterContainer.removeAttribute('disabled');
|
|
||||||
|
|
||||||
const itemList = await foundry.applications.handlebars.renderTemplate('systems/daggerheart/templates/ui/itemBrowser/itemContainer.hbs',
|
const itemList = await foundry.applications.handlebars.renderTemplate(
|
||||||
|
'systems/daggerheart/templates/ui/itemBrowser/itemContainer.hbs',
|
||||||
{
|
{
|
||||||
items: this.items,
|
items: this.items,
|
||||||
menu: this.selectedMenu,
|
menu: this.selectedMenu,
|
||||||
|
|
@ -255,7 +256,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
toggleLoader(state) {
|
toggleLoader(state) {
|
||||||
const container = this.element.querySelector('.item-list');
|
const container = this.element.querySelector('.item-list');
|
||||||
return setTimeout(() => {
|
return setTimeout(() => {
|
||||||
container.classList.toggle("loader", state);
|
container.classList.toggle('loader', state);
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -376,7 +377,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
for (const li of html.querySelectorAll('.item-container')) {
|
for (const li of html.querySelectorAll('.item-container')) {
|
||||||
const itemUUID = li.dataset.itemUuid,
|
const itemUUID = li.dataset.itemUuid,
|
||||||
item = this.items.find(i => i.uuid === itemUUID);
|
item = this.items.find(i => i.uuid === itemUUID);
|
||||||
if(!item) continue;
|
if (!item) continue;
|
||||||
const matchesSearch = !query || foundry.applications.ux.SearchFilter.testQuery(rgx, item.name);
|
const matchesSearch = !query || foundry.applications.ux.SearchFilter.testQuery(rgx, item.name);
|
||||||
if (matchesSearch) this.#filteredItems.browser.search.add(item.id);
|
if (matchesSearch) this.#filteredItems.browser.search.add(item.id);
|
||||||
const { input } = this.#filteredItems.browser;
|
const { input } = this.#filteredItems.browser;
|
||||||
|
|
@ -505,17 +506,17 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static injectSidebarButton(html) {
|
static injectSidebarButton(html) {
|
||||||
if(!game.user.isGM) return;
|
if (!game.user.isGM) return;
|
||||||
const sectionId = html.dataset.tab,
|
const sectionId = html.dataset.tab,
|
||||||
menus = {
|
menus = {
|
||||||
actors: {
|
actors: {
|
||||||
folder: "adversaries",
|
folder: 'adversaries',
|
||||||
render: {
|
render: {
|
||||||
folders: ["adversaries", "characters", "environments"]
|
folders: ['adversaries', 'characters', 'environments']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
items: {
|
items: {
|
||||||
folder: "equipments",
|
folder: 'equipments',
|
||||||
render: {
|
render: {
|
||||||
noFolder: true
|
noFolder: true
|
||||||
}
|
}
|
||||||
|
|
@ -523,17 +524,17 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
compendium: {}
|
compendium: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
if(Object.keys(menus).includes(sectionId)) {
|
if (Object.keys(menus).includes(sectionId)) {
|
||||||
const headerActions = html.querySelector(".header-actions");
|
const headerActions = html.querySelector('.header-actions');
|
||||||
|
|
||||||
const button = document.createElement("button");
|
const button = document.createElement('button');
|
||||||
button.type = "button";
|
button.type = 'button';
|
||||||
button.classList.add("open-compendium-browser");
|
button.classList.add('open-compendium-browser');
|
||||||
button.innerHTML = `
|
button.innerHTML = `
|
||||||
<i class="fa-solid fa-book-atlas"></i>
|
<i class="fa-solid fa-book-atlas"></i>
|
||||||
${game.i18n.localize("DAGGERHEART.UI.Tooltip.compendiumBrowser")}
|
${game.i18n.localize('DAGGERHEART.UI.Tooltip.compendiumBrowser')}
|
||||||
`;
|
`;
|
||||||
button.addEventListener("click", event => {
|
button.addEventListener('click', event => {
|
||||||
ui.compendiumBrowser.open(menus[sectionId]);
|
ui.compendiumBrowser.open(menus[sectionId]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
export const displayDomainCardsAsList = 'displayDomainCardsAsList';
|
export const displayDomainCardsAsCard = 'displayDomainCardsAsCard';
|
||||||
export const narrativeCountdown = {
|
export const narrativeCountdown = {
|
||||||
simple: 'countdown-narrative-simple',
|
simple: 'countdown-narrative-simple',
|
||||||
position: 'countdown-narrative-position'
|
position: 'countdown-narrative-position'
|
||||||
|
|
|
||||||
|
|
@ -90,22 +90,22 @@ export const rangeInclusion = {
|
||||||
export const otherTargetTypes = {
|
export const otherTargetTypes = {
|
||||||
friendly: {
|
friendly: {
|
||||||
id: 'friendly',
|
id: 'friendly',
|
||||||
label: 'Friendly'
|
label: 'DAGGERHEART.CONFIG.TargetTypes.friendly'
|
||||||
},
|
},
|
||||||
hostile: {
|
hostile: {
|
||||||
id: 'hostile',
|
id: 'hostile',
|
||||||
label: 'Hostile'
|
label: 'DAGGERHEART.CONFIG.TargetTypes.hostile'
|
||||||
},
|
},
|
||||||
any: {
|
any: {
|
||||||
id: 'any',
|
id: 'any',
|
||||||
label: 'Any'
|
label: 'DAGGERHEART.CONFIG.TargetTypes.any'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const targetTypes = {
|
export const targetTypes = {
|
||||||
self: {
|
self: {
|
||||||
id: 'self',
|
id: 'self',
|
||||||
label: 'Self'
|
label: 'DAGGERHEART.CONFIG.TargetTypes.self'
|
||||||
},
|
},
|
||||||
...otherTargetTypes
|
...otherTargetTypes
|
||||||
};
|
};
|
||||||
|
|
@ -587,17 +587,17 @@ export const abilityCosts = {
|
||||||
},
|
},
|
||||||
hope: {
|
hope: {
|
||||||
id: 'hope',
|
id: 'hope',
|
||||||
label: 'Hope',
|
label: 'DAGGERHEART.CONFIG.HealingType.hope.name',
|
||||||
group: 'TYPES.Actor.character'
|
group: 'TYPES.Actor.character'
|
||||||
},
|
},
|
||||||
armor: {
|
armor: {
|
||||||
id: 'armor',
|
id: 'armor',
|
||||||
label: 'Armor Slot',
|
label: 'DAGGERHEART.CONFIG.HealingType.armor.name',
|
||||||
group: 'TYPES.Actor.character'
|
group: 'TYPES.Actor.character'
|
||||||
},
|
},
|
||||||
fear: {
|
fear: {
|
||||||
id: 'fear',
|
id: 'fear',
|
||||||
label: 'Fear',
|
label: 'DAGGERHEART.CONFIG.HealingType.fear.name',
|
||||||
group: 'TYPES.Actor.adversary'
|
group: 'TYPES.Actor.adversary'
|
||||||
},
|
},
|
||||||
resource: itemAbilityCosts.resource
|
resource: itemAbilityCosts.resource
|
||||||
|
|
|
||||||
|
|
@ -199,8 +199,7 @@ export const typeConfig = {
|
||||||
key: 'system.itemFeatures',
|
key: 'system.itemFeatures',
|
||||||
label: 'DAGGERHEART.GENERAL.features',
|
label: 'DAGGERHEART.GENERAL.features',
|
||||||
choices: () =>
|
choices: () =>
|
||||||
Object.entries(CONFIG.DH.ITEM.weaponFeatures)
|
Object.entries(CONFIG.DH.ITEM.weaponFeatures).map(([k, v]) => ({ value: k, label: v.label })),
|
||||||
.map(([k, v]) => ({ value: k, label: v.label })),
|
|
||||||
operator: 'contains3'
|
operator: 'contains3'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -241,8 +240,7 @@ export const typeConfig = {
|
||||||
key: 'system.itemFeatures',
|
key: 'system.itemFeatures',
|
||||||
label: 'DAGGERHEART.GENERAL.features',
|
label: 'DAGGERHEART.GENERAL.features',
|
||||||
choices: () =>
|
choices: () =>
|
||||||
Object.entries(CONFIG.DH.ITEM.armorFeatures)
|
Object.entries(CONFIG.DH.ITEM.armorFeatures).map(([k, v]) => ({ value: k, label: v.label })),
|
||||||
.map(([k, v]) => ({ value: k, label: v.label })),
|
|
||||||
operator: 'contains3'
|
operator: 'contains3'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -372,14 +370,30 @@ export const typeConfig = {
|
||||||
label: 'DAGGERHEART.ITEMS.Subclass.spellcastingTrait'
|
label: 'DAGGERHEART.ITEMS.Subclass.spellcastingTrait'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
filters: []
|
||||||
|
},
|
||||||
|
beastforms: {
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
key: 'system.tier',
|
||||||
|
label: 'DAGGERHEART.GENERAL.Tiers.singular'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'system.mainTrait',
|
||||||
|
label: 'DAGGERHEART.GENERAL.Trait.single'
|
||||||
|
}
|
||||||
|
],
|
||||||
filters: [
|
filters: [
|
||||||
{
|
{
|
||||||
key: 'system.linkedClass.uuid',
|
key: 'system.linkedClass.uuid',
|
||||||
label: 'Class',
|
label: 'Class',
|
||||||
choices: (items) => {
|
choices: items => {
|
||||||
const list = items.map(item => ({ value: item.system.linkedClass.uuid, label: item.system.linkedClass.name }));
|
const list = items.map(item => ({
|
||||||
return list.reduce((a,c) => {
|
value: item.system.linkedClass.uuid,
|
||||||
if(!(a.find(i => i.value === c.value))) a.push(c);
|
label: item.system.linkedClass.name
|
||||||
|
}));
|
||||||
|
return list.reduce((a, c) => {
|
||||||
|
if (!a.find(i => i.value === c.value)) a.push(c);
|
||||||
return a;
|
return a;
|
||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
|
|
@ -417,7 +431,7 @@ export const compendiumConfig = {
|
||||||
id: 'characters',
|
id: 'characters',
|
||||||
keys: ['characters'],
|
keys: ['characters'],
|
||||||
label: 'DAGGERHEART.UI.ItemBrowser.folders.characters',
|
label: 'DAGGERHEART.UI.ItemBrowser.folders.characters',
|
||||||
type: ['character'],
|
type: ['character']
|
||||||
// listType: 'characters'
|
// listType: 'characters'
|
||||||
},
|
},
|
||||||
adversaries: {
|
adversaries: {
|
||||||
|
|
@ -431,7 +445,7 @@ export const compendiumConfig = {
|
||||||
id: 'ancestries',
|
id: 'ancestries',
|
||||||
keys: ['ancestries'],
|
keys: ['ancestries'],
|
||||||
label: 'DAGGERHEART.UI.ItemBrowser.folders.ancestries',
|
label: 'DAGGERHEART.UI.ItemBrowser.folders.ancestries',
|
||||||
type: ['ancestry'],
|
type: ['ancestry']
|
||||||
/* folders: {
|
/* folders: {
|
||||||
features: {
|
features: {
|
||||||
id: 'features',
|
id: 'features',
|
||||||
|
|
@ -516,7 +530,7 @@ export const compendiumConfig = {
|
||||||
id: 'communities',
|
id: 'communities',
|
||||||
keys: ['communities'],
|
keys: ['communities'],
|
||||||
label: 'DAGGERHEART.UI.ItemBrowser.folders.communities',
|
label: 'DAGGERHEART.UI.ItemBrowser.folders.communities',
|
||||||
type: ['community'],
|
type: ['community']
|
||||||
/* folders: {
|
/* folders: {
|
||||||
features: {
|
features: {
|
||||||
id: 'features',
|
id: 'features',
|
||||||
|
|
@ -537,7 +551,7 @@ export const compendiumConfig = {
|
||||||
keys: ['beastforms'],
|
keys: ['beastforms'],
|
||||||
label: 'DAGGERHEART.UI.ItemBrowser.folders.beastforms',
|
label: 'DAGGERHEART.UI.ItemBrowser.folders.beastforms',
|
||||||
type: ['beastform'],
|
type: ['beastform'],
|
||||||
listType: 'beastforms',
|
listType: 'beastforms'
|
||||||
/* folders: {
|
/* folders: {
|
||||||
features: {
|
features: {
|
||||||
id: 'features',
|
id: 'features',
|
||||||
|
|
|
||||||
|
|
@ -452,6 +452,34 @@ export const armorFeatures = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const allArmorFeatures = () => {
|
||||||
|
const homebrewFeatures = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).itemFeatures
|
||||||
|
.armorFeatures;
|
||||||
|
return {
|
||||||
|
...armorFeatures,
|
||||||
|
...Object.keys(homebrewFeatures).reduce((acc, key) => {
|
||||||
|
const feature = homebrewFeatures[key];
|
||||||
|
acc[key] = { ...feature, label: feature.name };
|
||||||
|
return acc;
|
||||||
|
}, {})
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const orderedArmorFeatures = () => {
|
||||||
|
const homebrewFeatures = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).itemFeatures
|
||||||
|
.armorFeatures;
|
||||||
|
const allFeatures = { ...armorFeatures, ...homebrewFeatures };
|
||||||
|
const all = Object.keys(allFeatures).map(key => {
|
||||||
|
const feature = allFeatures[key];
|
||||||
|
return {
|
||||||
|
...feature,
|
||||||
|
id: key,
|
||||||
|
label: feature.label ?? feature.name
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return Object.values(all).sort((a, b) => game.i18n.localize(a.label).localeCompare(game.i18n.localize(b.label)));
|
||||||
|
};
|
||||||
|
|
||||||
export const weaponFeatures = {
|
export const weaponFeatures = {
|
||||||
barrier: {
|
barrier: {
|
||||||
label: 'DAGGERHEART.CONFIG.WeaponFeature.barrier.name',
|
label: 'DAGGERHEART.CONFIG.WeaponFeature.barrier.name',
|
||||||
|
|
@ -1383,6 +1411,34 @@ export const weaponFeatures = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const allWeaponFeatures = () => {
|
||||||
|
const homebrewFeatures = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).itemFeatures
|
||||||
|
.weaponFeatures;
|
||||||
|
return {
|
||||||
|
...weaponFeatures,
|
||||||
|
...Object.keys(homebrewFeatures).reduce((acc, key) => {
|
||||||
|
const feature = homebrewFeatures[key];
|
||||||
|
acc[key] = { ...feature, label: feature.name };
|
||||||
|
return acc;
|
||||||
|
}, {})
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const orderedWeaponFeatures = () => {
|
||||||
|
const homebrewFeatures = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).itemFeatures
|
||||||
|
.weaponFeatures;
|
||||||
|
const allFeatures = { ...weaponFeatures, ...homebrewFeatures };
|
||||||
|
const all = Object.keys(allFeatures).map(key => {
|
||||||
|
const feature = allFeatures[key];
|
||||||
|
return {
|
||||||
|
...feature,
|
||||||
|
id: key,
|
||||||
|
label: feature.label ?? feature.name
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return Object.values(all).sort((a, b) => game.i18n.localize(a.label).localeCompare(game.i18n.localize(b.label)));
|
||||||
|
};
|
||||||
|
|
||||||
export const featureTypes = {
|
export const featureTypes = {
|
||||||
ancestry: {
|
ancestry: {
|
||||||
id: 'ancestry',
|
id: 'ancestry',
|
||||||
|
|
|
||||||
|
|
@ -32,19 +32,19 @@ export const gameSettings = {
|
||||||
|
|
||||||
export const actionAutomationChoices = {
|
export const actionAutomationChoices = {
|
||||||
never: {
|
never: {
|
||||||
id: "never",
|
id: 'never',
|
||||||
label: "Never"
|
label: 'Never'
|
||||||
},
|
},
|
||||||
showDialog: {
|
showDialog: {
|
||||||
id: "showDialog",
|
id: 'showDialog',
|
||||||
label: "Show Dialog only"
|
label: 'Show Dialog only'
|
||||||
},
|
},
|
||||||
// npcOnly: {
|
// npcOnly: {
|
||||||
// id: "npcOnly",
|
// id: "npcOnly",
|
||||||
// label: "Always for non-characters"
|
// label: "Always for non-characters"
|
||||||
// },
|
// },
|
||||||
always: {
|
always: {
|
||||||
id: "always",
|
id: 'always',
|
||||||
label: "Always"
|
label: 'Always'
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
|
||||||
|
|
@ -28,14 +28,14 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
actionType: new fields.StringField({
|
actionType: new fields.StringField({
|
||||||
choices: CONFIG.DH.ITEM.actionTypes,
|
choices: CONFIG.DH.ITEM.actionTypes,
|
||||||
initial: 'action',
|
initial: 'action',
|
||||||
nullable: true
|
nullable: false,
|
||||||
|
required: true
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
this.extraSchemas.forEach(s => {
|
this.extraSchemas.forEach(s => {
|
||||||
let clsField = this.getActionField(s);
|
let clsField = this.getActionField(s);
|
||||||
if (clsField)
|
if (clsField) schemaFields[s] = new clsField();
|
||||||
schemaFields[s] = new clsField();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return schemaFields;
|
return schemaFields;
|
||||||
|
|
@ -53,8 +53,9 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
this.constructor.extraSchemas.forEach(s => {
|
this.constructor.extraSchemas.forEach(s => {
|
||||||
let clsField = this.constructor.getActionField(s);
|
let clsField = this.constructor.getActionField(s);
|
||||||
if (clsField?.execute) {
|
if (clsField?.execute) {
|
||||||
workflow.set(s, { order: clsField.order, execute: clsField.execute.bind(this) } );
|
workflow.set(s, { order: clsField.order, execute: clsField.execute.bind(this) });
|
||||||
if( s === "damage" ) workflow.set("applyDamage", { order: 75, execute: clsField.applyDamage.bind(this) } );
|
if (s === 'damage')
|
||||||
|
workflow.set('applyDamage', { order: 75, execute: clsField.applyDamage.bind(this) });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return new Map([...workflow.entries()].sort(([aKey, aValue], [bKey, bValue]) => aValue.order - bValue.order));
|
return new Map([...workflow.entries()].sort(([aKey, aValue], [bKey, bValue]) => aValue.order - bValue.order));
|
||||||
|
|
@ -64,9 +65,9 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
* Getter returning the workflow property or creating it the first time the property is called
|
* Getter returning the workflow property or creating it the first time the property is called
|
||||||
*/
|
*/
|
||||||
get workflow() {
|
get workflow() {
|
||||||
if ( this.hasOwnProperty("_workflow") ) return this._workflow;
|
if (this.hasOwnProperty('_workflow')) return this._workflow;
|
||||||
const workflow = Object.freeze(this.defineWorkflow());
|
const workflow = Object.freeze(this.defineWorkflow());
|
||||||
Object.defineProperty(this, "_workflow", {value: workflow, writable: false});
|
Object.defineProperty(this, '_workflow', { value: workflow, writable: false });
|
||||||
return workflow;
|
return workflow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -167,9 +168,9 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
* @param {object} config Config object usually created from prepareConfig method
|
* @param {object} config Config object usually created from prepareConfig method
|
||||||
*/
|
*/
|
||||||
async executeWorkflow(config) {
|
async executeWorkflow(config) {
|
||||||
for(const [key, part] of this.workflow) {
|
for (const [key, part] of this.workflow) {
|
||||||
if (Hooks.call(`${CONFIG.DH.id}.pre${key.capitalize()}Action`, this, config) === false) return;
|
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;
|
||||||
if (Hooks.call(`${CONFIG.DH.id}.post${key.capitalize()}Action`, this, config) === false) return;
|
if (Hooks.call(`${CONFIG.DH.id}.post${key.capitalize()}Action`, this, config) === false) return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -185,7 +186,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
if (this.chatDisplay) await this.toChat();
|
if (this.chatDisplay) await this.toChat();
|
||||||
|
|
||||||
let config = this.prepareConfig(event);
|
let config = this.prepareConfig(event);
|
||||||
if(!config) return;
|
if (!config) return;
|
||||||
|
|
||||||
if (Hooks.call(`${CONFIG.DH.id}.preUseAction`, this, config) === false) return;
|
if (Hooks.call(`${CONFIG.DH.id}.preUseAction`, this, config) === false) return;
|
||||||
|
|
||||||
|
|
@ -240,9 +241,8 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
*/
|
*/
|
||||||
prepareConfig(event) {
|
prepareConfig(event) {
|
||||||
const config = this.prepareBaseConfig(event);
|
const config = this.prepareBaseConfig(event);
|
||||||
for(const clsField of Object.values(this.schema.fields)) {
|
for (const clsField of Object.values(this.schema.fields)) {
|
||||||
if (clsField?.prepareConfig)
|
if (clsField?.prepareConfig) if (clsField.prepareConfig.call(this, config) === false) return false;
|
||||||
if(clsField.prepareConfig.call(this, config) === false) return false;
|
|
||||||
}
|
}
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
@ -263,8 +263,8 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
* @param {boolean} successCost
|
* @param {boolean} successCost
|
||||||
*/
|
*/
|
||||||
async consume(config, successCost = false) {
|
async consume(config, successCost = false) {
|
||||||
await this.workflow.get("cost")?.execute(config, successCost);
|
await this.workflow.get('cost')?.execute(config, successCost);
|
||||||
await this.workflow.get("uses")?.execute(config, successCost);
|
await this.workflow.get('uses')?.execute(config, successCost);
|
||||||
|
|
||||||
if (config.roll && !config.roll.success && successCost) {
|
if (config.roll && !config.roll.success && successCost) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|
@ -291,11 +291,11 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasDamage() {
|
get hasDamage() {
|
||||||
return this.damage?.parts?.length && this.type !== 'healing'
|
return this.damage?.parts?.length && this.type !== 'healing';
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasHealing() {
|
get hasHealing() {
|
||||||
return this.damage?.parts?.length && this.type === 'healing'
|
return this.damage?.parts?.length && this.type === 'healing';
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasSave() {
|
get hasSave() {
|
||||||
|
|
|
||||||
|
|
@ -30,4 +30,24 @@ export default class BaseEffect extends foundry.abstract.TypeDataModel {
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getDefaultObject() {
|
||||||
|
return {
|
||||||
|
name: 'New Effect',
|
||||||
|
id: foundry.utils.randomID(),
|
||||||
|
disabled: false,
|
||||||
|
img: 'icons/magic/life/heart-cross-blue.webp',
|
||||||
|
description: '',
|
||||||
|
statuses: [],
|
||||||
|
changes: [],
|
||||||
|
system: {
|
||||||
|
rangeDependence: {
|
||||||
|
enabled: false,
|
||||||
|
type: CONFIG.DH.GENERAL.rangeInclusion.withinRange.id,
|
||||||
|
target: CONFIG.DH.GENERAL.otherTargetTypes.hostile.id,
|
||||||
|
range: CONFIG.DH.GENERAL.range.melee.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import DHAbilityUse from "./abilityUse.mjs";
|
import DHAbilityUse from './abilityUse.mjs';
|
||||||
import DHActorRoll from "./actorRoll.mjs";
|
import DHActorRoll from './actorRoll.mjs';
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
abilityUse: DHAbilityUse,
|
abilityUse: DHAbilityUse,
|
||||||
adversaryRoll: DHActorRoll,
|
adversaryRoll: DHActorRoll,
|
||||||
damageRoll: DHActorRoll,
|
damageRoll: DHActorRoll,
|
||||||
dualityRoll: DHActorRoll
|
dualityRoll: DHActorRoll
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -58,10 +58,8 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
||||||
const actionActor = this.actionActor,
|
const actionActor = this.actionActor,
|
||||||
actionItem = this.actionItem;
|
actionItem = this.actionItem;
|
||||||
if (!this.source.action) return null;
|
if (!this.source.action) return null;
|
||||||
if(actionItem)
|
if (actionItem) return actionItem.system.actionsList?.find(a => a.id === this.source.action);
|
||||||
return actionItem.system.actionsList?.find(a => a.id === this.source.action);
|
else if (actionActor?.system.attack?._id === this.source.action) return actionActor.system.attack;
|
||||||
else if(actionActor?.system.attack?._id === this.source.action)
|
|
||||||
return actionActor.system.attack
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import BeastformDialog from "../../../applications/dialogs/beastformDialog.mjs";
|
import BeastformDialog from '../../../applications/dialogs/beastformDialog.mjs';
|
||||||
|
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
|
|
@ -56,7 +56,7 @@ export default class BeastformField extends fields.SchemaField {
|
||||||
* @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods.
|
* @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods.
|
||||||
*/
|
*/
|
||||||
prepareConfig(config) {
|
prepareConfig(config) {
|
||||||
if(this.actor.effects.find(x => x.type === 'beastform')) {
|
if (this.actor.effects.find(x => x.type === 'beastform')) {
|
||||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.beastformAlreadyApplied'));
|
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.beastformAlreadyApplied'));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -218,7 +218,8 @@ export default class CostField extends fields.ArrayField {
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
static getRealCosts(costs) {
|
static getRealCosts(costs) {
|
||||||
const realCosts = costs?.length ? costs.filter(c => c.enabled) : [];
|
const cloneCosts = foundry.utils.deepClone(costs),
|
||||||
|
realCosts = cloneCosts?.length ? cloneCosts.filter(c => c.enabled) : [];
|
||||||
let mergedCosts = [];
|
let mergedCosts = [];
|
||||||
realCosts.forEach(c => {
|
realCosts.forEach(c => {
|
||||||
const getCost = Object.values(mergedCosts).find(gc => gc.key === c.key);
|
const getCost = Object.values(mergedCosts).find(gc => gc.key === c.key);
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,13 @@ export default class DamageField extends fields.SchemaField {
|
||||||
* @param {boolean} [force=false] If the method should be executed outside of Action workflow, for ChatMessage button for example.
|
* @param {boolean} [force=false] If the method should be executed outside of Action workflow, for ChatMessage button for example.
|
||||||
*/
|
*/
|
||||||
static async execute(config, messageId = null, force = false) {
|
static async execute(config, messageId = null, force = false) {
|
||||||
if(!this.hasDamage && !this.hasHealing) return;
|
if (!this.hasDamage && !this.hasHealing) return;
|
||||||
if((this.hasRoll && DamageField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.never.id) && !force) return;
|
if (
|
||||||
|
this.hasRoll &&
|
||||||
|
DamageField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.never.id &&
|
||||||
|
!force
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
let formulas = this.damage.parts.map(p => ({
|
let formulas = this.damage.parts.map(p => ({
|
||||||
formula: DamageField.getFormulaValue.call(this, p, config).getFormula(this.actor),
|
formula: DamageField.getFormulaValue.call(this, p, config).getFormula(this.actor),
|
||||||
|
|
@ -51,7 +56,8 @@ export default class DamageField extends fields.SchemaField {
|
||||||
};
|
};
|
||||||
delete damageConfig.evaluate;
|
delete damageConfig.evaluate;
|
||||||
|
|
||||||
if(DamageField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.always.id) damageConfig.dialog.configure = false;
|
if (DamageField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.always.id)
|
||||||
|
damageConfig.dialog.configure = false;
|
||||||
if (config.hasSave) config.onSave = damageConfig.onSave = this.save.damageMod;
|
if (config.hasSave) config.onSave = damageConfig.onSave = this.save.damageMod;
|
||||||
|
|
||||||
damageConfig.source.message = config.message?._id ?? messageId;
|
damageConfig.source.message = config.message?._id ?? messageId;
|
||||||
|
|
@ -61,7 +67,7 @@ export default class DamageField extends fields.SchemaField {
|
||||||
// await game.dice3d.waitFor3DAnimationByMessageID(damageConfig.source.message);
|
// await game.dice3d.waitFor3DAnimationByMessageID(damageConfig.source.message);
|
||||||
|
|
||||||
const damageResult = await CONFIG.Dice.daggerheart.DamageRoll.build(damageConfig);
|
const damageResult = await CONFIG.Dice.daggerheart.DamageRoll.build(damageConfig);
|
||||||
if(!damageResult) return false;
|
if (!damageResult) return false;
|
||||||
config.damage = damageResult.damage;
|
config.damage = damageResult.damage;
|
||||||
config.message ??= damageConfig.message;
|
config.message ??= damageConfig.message;
|
||||||
}
|
}
|
||||||
|
|
@ -74,15 +80,11 @@ export default class DamageField extends fields.SchemaField {
|
||||||
*/
|
*/
|
||||||
static async applyDamage(config, targets = null, force = false) {
|
static async applyDamage(config, targets = null, force = false) {
|
||||||
targets ??= config.targets.filter(target => target.hit);
|
targets ??= config.targets.filter(target => target.hit);
|
||||||
if(!config.damage || !targets?.length || (!DamageField.getApplyAutomation() && !force)) return;
|
if (!config.damage || !targets?.length || (!DamageField.getApplyAutomation() && !force)) return;
|
||||||
for (let target of targets) {
|
for (let target of targets) {
|
||||||
const actor = fromUuidSync(target.actorId);
|
const actor = fromUuidSync(target.actorId);
|
||||||
if(!actor) continue;
|
if (!actor) continue;
|
||||||
if (
|
if (!config.hasHealing && config.onSave && target.saved?.success === true) {
|
||||||
!config.hasHealing &&
|
|
||||||
config.onSave &&
|
|
||||||
target.saved?.success === true
|
|
||||||
) {
|
|
||||||
const mod = CONFIG.DH.ACTIONS.damageOnSave[config.onSave]?.mod ?? 1;
|
const mod = CONFIG.DH.ACTIONS.damageOnSave[config.onSave]?.mod ?? 1;
|
||||||
Object.entries(config.damage).forEach(([k, v]) => {
|
Object.entries(config.damage).forEach(([k, v]) => {
|
||||||
v.total = 0;
|
v.total = 0;
|
||||||
|
|
@ -145,16 +147,25 @@ export default class DamageField extends fields.SchemaField {
|
||||||
* @returns {string} Id from settingsConfig.mjs actionAutomationChoices
|
* @returns {string} Id from settingsConfig.mjs actionAutomationChoices
|
||||||
*/
|
*/
|
||||||
static getAutomation() {
|
static getAutomation() {
|
||||||
return (game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damage.gm) || (!game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damage.players)
|
return (
|
||||||
|
(game.user.isGM &&
|
||||||
|
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damage.gm) ||
|
||||||
|
(!game.user.isGM &&
|
||||||
|
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damage.players)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the automation setting for applyDamage method for current user role
|
* Return the automation setting for applyDamage method for current user role
|
||||||
* @returns {boolean} If applyDamage should be triggered automatically
|
* @returns {boolean} If applyDamage should be triggered automatically
|
||||||
*/
|
*/
|
||||||
static getApplyAutomation() {
|
static getApplyAutomation() {
|
||||||
return (game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damageApply.gm) || (!game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damageApply.players)
|
return (
|
||||||
|
(game.user.isGM &&
|
||||||
|
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damageApply.gm) ||
|
||||||
|
(!game.user.isGM &&
|
||||||
|
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damageApply.players)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -165,7 +176,9 @@ export class DHActionDiceData extends foundry.abstract.DataModel {
|
||||||
multiplier: new fields.StringField({
|
multiplier: new fields.StringField({
|
||||||
choices: CONFIG.DH.GENERAL.multiplierTypes,
|
choices: CONFIG.DH.GENERAL.multiplierTypes,
|
||||||
initial: 'prof',
|
initial: 'prof',
|
||||||
label: 'DAGGERHEART.ACTIONS.Config.damage.multiplier'
|
label: 'DAGGERHEART.ACTIONS.Config.damage.multiplier',
|
||||||
|
nullable: false,
|
||||||
|
required: true
|
||||||
}),
|
}),
|
||||||
flatMultiplier: new fields.NumberField({
|
flatMultiplier: new fields.NumberField({
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
|
@ -175,7 +188,9 @@ export class DHActionDiceData extends foundry.abstract.DataModel {
|
||||||
dice: new fields.StringField({
|
dice: new fields.StringField({
|
||||||
choices: CONFIG.DH.GENERAL.diceTypes,
|
choices: CONFIG.DH.GENERAL.diceTypes,
|
||||||
initial: 'd6',
|
initial: 'd6',
|
||||||
label: 'DAGGERHEART.GENERAL.Dice.single'
|
label: 'DAGGERHEART.GENERAL.Dice.single',
|
||||||
|
nullable: false,
|
||||||
|
required: true
|
||||||
}),
|
}),
|
||||||
bonus: new fields.NumberField({ nullable: true, initial: null, label: 'DAGGERHEART.GENERAL.bonus' }),
|
bonus: new fields.NumberField({ nullable: true, initial: null, label: 'DAGGERHEART.GENERAL.bonus' }),
|
||||||
custom: new fields.SchemaField({
|
custom: new fields.SchemaField({
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { emitAsGM, GMUpdateEvent } from "../../../systemRegistration/socket.mjs";
|
import { emitAsGM, GMUpdateEvent } from '../../../systemRegistration/socket.mjs';
|
||||||
|
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
|
|
@ -25,21 +25,16 @@ export default class EffectsField extends fields.ArrayField {
|
||||||
* @param {boolean} [force=false] If the method should be executed outside of Action workflow, for ChatMessage button for example.
|
* @param {boolean} [force=false] If the method should be executed outside of Action workflow, for ChatMessage button for example.
|
||||||
*/
|
*/
|
||||||
static async execute(config, targets = null, force = false) {
|
static async execute(config, targets = null, force = false) {
|
||||||
if(!config.hasEffect) return;
|
if (!config.hasEffect) return;
|
||||||
let message = config.message ?? ui.chat.collection.get(config.parent?._id);
|
let message = config.message ?? ui.chat.collection.get(config.parent?._id);
|
||||||
if(!message) {
|
if (!message) {
|
||||||
const roll = new CONFIG.Dice.daggerheart.DHRoll('');
|
const roll = new CONFIG.Dice.daggerheart.DHRoll('');
|
||||||
roll._evaluated = true;
|
roll._evaluated = true;
|
||||||
message = config.message = await CONFIG.Dice.daggerheart.DHRoll.toMessage(roll, config);
|
message = config.message = await CONFIG.Dice.daggerheart.DHRoll.toMessage(roll, config);
|
||||||
}
|
}
|
||||||
if(EffectsField.getAutomation() || force) {
|
if (EffectsField.getAutomation() || force) {
|
||||||
targets ??= (message.system?.targets ?? config.targets).filter(t => !config.hasRoll || t.hit);
|
targets ??= (message.system?.targets ?? config.targets).filter(t => !config.hasRoll || t.hit);
|
||||||
await emitAsGM(
|
await emitAsGM(GMUpdateEvent.UpdateEffect, EffectsField.applyEffects.bind(this), targets, this.uuid);
|
||||||
GMUpdateEvent.UpdateEffect,
|
|
||||||
EffectsField.applyEffects.bind(this),
|
|
||||||
targets,
|
|
||||||
this.uuid
|
|
||||||
);
|
|
||||||
// EffectsField.applyEffects.call(this, config.targets.filter(t => !config.hasRoll || t.hit));
|
// EffectsField.applyEffects.call(this, config.targets.filter(t => !config.hasRoll || t.hit));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -53,8 +48,7 @@ export default class EffectsField extends fields.ArrayField {
|
||||||
if (!this.effects?.length || !targets?.length) return;
|
if (!this.effects?.length || !targets?.length) return;
|
||||||
let effects = this.effects;
|
let effects = this.effects;
|
||||||
targets.forEach(async token => {
|
targets.forEach(async token => {
|
||||||
if (this.hasSave && token.saved.success === true)
|
if (this.hasSave && token.saved.success === true) effects = this.effects.filter(e => e.onSave === true);
|
||||||
effects = this.effects.filter(e => e.onSave === true);
|
|
||||||
if (!effects.length) return;
|
if (!effects.length) return;
|
||||||
effects.forEach(async e => {
|
effects.forEach(async e => {
|
||||||
const actor = canvas.tokens.get(token.id)?.actor,
|
const actor = canvas.tokens.get(token.id)?.actor,
|
||||||
|
|
@ -96,6 +90,11 @@ export default class EffectsField extends fields.ArrayField {
|
||||||
* @returns {boolean} If execute should be triggered automatically
|
* @returns {boolean} If execute should be triggered automatically
|
||||||
*/
|
*/
|
||||||
static getAutomation() {
|
static getAutomation() {
|
||||||
return (game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.effect.gm) || (!game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.effect.players)
|
return (
|
||||||
|
(game.user.isGM &&
|
||||||
|
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.effect.gm) ||
|
||||||
|
(!game.user.isGM &&
|
||||||
|
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.effect.players)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ export default class MacroField extends fields.DocumentUUIDField {
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
constructor(context = {}) {
|
constructor(context = {}) {
|
||||||
super({ type: "Macro" }, context);
|
super({ type: 'Macro' }, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
export default class RangeField extends fields.StringField {
|
export default class RangeField extends fields.StringField {
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
constructor(context = {}) {
|
constructor(context = {}) {
|
||||||
const options = {
|
const options = {
|
||||||
choices: CONFIG.DH.GENERAL.range,
|
choices: CONFIG.DH.GENERAL.range,
|
||||||
required: false,
|
required: false,
|
||||||
blank: true,
|
blank: true,
|
||||||
label: "DAGGERHEART.GENERAL.range"
|
label: 'DAGGERHEART.GENERAL.range'
|
||||||
};
|
};
|
||||||
super(options, context);
|
super(options, context);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,18 +5,27 @@ export class DHActionRollData extends foundry.abstract.DataModel {
|
||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
return {
|
return {
|
||||||
type: new fields.StringField({ nullable: true, initial: null, choices: CONFIG.DH.GENERAL.rollTypes }),
|
type: new fields.StringField({ nullable: true, initial: null, choices: CONFIG.DH.GENERAL.rollTypes }),
|
||||||
trait: new fields.StringField({ nullable: true, initial: null, choices: CONFIG.DH.ACTOR.abilities, label: "DAGGERHEART.GENERAL.Trait.single" }),
|
trait: new fields.StringField({
|
||||||
|
nullable: true,
|
||||||
|
initial: null,
|
||||||
|
choices: CONFIG.DH.ACTOR.abilities,
|
||||||
|
label: 'DAGGERHEART.GENERAL.Trait.single'
|
||||||
|
}),
|
||||||
difficulty: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }),
|
difficulty: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }),
|
||||||
bonus: new fields.NumberField({ nullable: true, initial: null, integer: true }),
|
bonus: new fields.NumberField({ nullable: true, initial: null, integer: true }),
|
||||||
advState: new fields.StringField({
|
advState: new fields.StringField({
|
||||||
choices: CONFIG.DH.ACTIONS.advantageState,
|
choices: CONFIG.DH.ACTIONS.advantageState,
|
||||||
initial: 'neutral'
|
initial: 'neutral',
|
||||||
|
nullable: false,
|
||||||
|
required: true
|
||||||
}),
|
}),
|
||||||
diceRolling: new fields.SchemaField({
|
diceRolling: new fields.SchemaField({
|
||||||
multiplier: new fields.StringField({
|
multiplier: new fields.StringField({
|
||||||
choices: CONFIG.DH.GENERAL.diceSetNumbers,
|
choices: CONFIG.DH.GENERAL.diceSetNumbers,
|
||||||
initial: 'prof',
|
initial: 'prof',
|
||||||
label: 'DAGGERHEART.ACTIONS.RollField.diceRolling.multiplier'
|
label: 'DAGGERHEART.ACTIONS.RollField.diceRolling.multiplier',
|
||||||
|
nullable: false,
|
||||||
|
required: true
|
||||||
}),
|
}),
|
||||||
flatMultiplier: new fields.NumberField({
|
flatMultiplier: new fields.NumberField({
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
|
@ -26,7 +35,9 @@ export class DHActionRollData extends foundry.abstract.DataModel {
|
||||||
dice: new fields.StringField({
|
dice: new fields.StringField({
|
||||||
choices: CONFIG.DH.GENERAL.diceTypes,
|
choices: CONFIG.DH.GENERAL.diceTypes,
|
||||||
initial: CONFIG.DH.GENERAL.diceTypes.d6,
|
initial: CONFIG.DH.GENERAL.diceTypes.d6,
|
||||||
label: 'DAGGERHEART.ACTIONS.RollField.diceRolling.dice'
|
label: 'DAGGERHEART.ACTIONS.RollField.diceRolling.dice',
|
||||||
|
nullable: false,
|
||||||
|
required: true
|
||||||
}),
|
}),
|
||||||
compare: new fields.StringField({
|
compare: new fields.StringField({
|
||||||
choices: CONFIG.DH.ACTIONS.diceCompare,
|
choices: CONFIG.DH.ACTIONS.diceCompare,
|
||||||
|
|
@ -86,14 +97,14 @@ export class DHActionRollData extends foundry.abstract.DataModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
get rollTrait() {
|
get rollTrait() {
|
||||||
if(this.parent?.actor?.type !== "character") return null;
|
if (this.parent?.actor?.type !== 'character') return null;
|
||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case CONFIG.DH.GENERAL.rollTypes.spellcast.id:
|
case CONFIG.DH.GENERAL.rollTypes.spellcast.id:
|
||||||
return this.parent.actor?.system?.spellcastModifierTrait?.key ?? 'agility';
|
return this.parent.actor?.system?.spellcastModifierTrait?.key ?? 'agility';
|
||||||
case CONFIG.DH.GENERAL.rollTypes.attack.id:
|
case CONFIG.DH.GENERAL.rollTypes.attack.id:
|
||||||
case CONFIG.DH.GENERAL.rollTypes.trait.id:
|
case CONFIG.DH.GENERAL.rollTypes.trait.id:
|
||||||
return this.useDefault || !this.trait
|
return this.useDefault || !this.trait
|
||||||
? this.parent.item.system.attack?.roll?.trait ?? 'agility'
|
? (this.parent.item.system.attack?.roll?.trait ?? 'agility')
|
||||||
: this.trait;
|
: this.trait;
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -118,9 +129,9 @@ export default class RollField extends fields.EmbeddedDataField {
|
||||||
* @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods.
|
* @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods.
|
||||||
*/
|
*/
|
||||||
static async execute(config) {
|
static async execute(config) {
|
||||||
if(!config.hasRoll) return;
|
if (!config.hasRoll) return;
|
||||||
config = await this.actor.diceRoll(config);
|
config = await this.actor.diceRoll(config);
|
||||||
if(!config) return false;
|
if (!config) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -129,7 +140,7 @@ export default class RollField extends fields.EmbeddedDataField {
|
||||||
* @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods.
|
* @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods.
|
||||||
*/
|
*/
|
||||||
prepareConfig(config) {
|
prepareConfig(config) {
|
||||||
if(!config.hasRoll) return;
|
if (!config.hasRoll) return;
|
||||||
|
|
||||||
config.dialog.configure = RollField.getAutomation() ? !config.dialog.configure : config.dialog.configure;
|
config.dialog.configure = RollField.getAutomation() ? !config.dialog.configure : config.dialog.configure;
|
||||||
|
|
||||||
|
|
@ -152,6 +163,11 @@ export default class RollField extends fields.EmbeddedDataField {
|
||||||
* @returns {boolean} If execute should be triggered automatically
|
* @returns {boolean} If execute should be triggered automatically
|
||||||
*/
|
*/
|
||||||
static getAutomation() {
|
static getAutomation() {
|
||||||
return (game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.roll.gm) || (!game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.roll.players)
|
return (
|
||||||
|
(game.user.isGM &&
|
||||||
|
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.roll.gm) ||
|
||||||
|
(!game.user.isGM &&
|
||||||
|
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.roll.players)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { abilities } from "../../../config/actorConfig.mjs";
|
import { abilities } from '../../../config/actorConfig.mjs';
|
||||||
|
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
|
|
@ -19,7 +19,9 @@ export default class SaveField extends fields.SchemaField {
|
||||||
difficulty: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }),
|
difficulty: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }),
|
||||||
damageMod: new fields.StringField({
|
damageMod: new fields.StringField({
|
||||||
initial: CONFIG.DH.ACTIONS.damageOnSave.none.id,
|
initial: CONFIG.DH.ACTIONS.damageOnSave.none.id,
|
||||||
choices: CONFIG.DH.ACTIONS.damageOnSave
|
choices: CONFIG.DH.ACTIONS.damageOnSave,
|
||||||
|
nullable: false,
|
||||||
|
required: true
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
super(saveFields, options, context);
|
super(saveFields, options, context);
|
||||||
|
|
@ -33,15 +35,15 @@ export default class SaveField extends fields.SchemaField {
|
||||||
* @param {boolean} [force=false] If the method should be executed outside of Action workflow, for ChatMessage button for example.
|
* @param {boolean} [force=false] If the method should be executed outside of Action workflow, for ChatMessage button for example.
|
||||||
*/
|
*/
|
||||||
static async execute(config, targets = null, force = false) {
|
static async execute(config, targets = null, force = false) {
|
||||||
if(!config.hasSave) return;
|
if (!config.hasSave) return;
|
||||||
let message = config.message ?? ui.chat.collection.get(config.parent?._id);
|
let message = config.message ?? ui.chat.collection.get(config.parent?._id);
|
||||||
|
|
||||||
if(!message) {
|
if (!message) {
|
||||||
const roll = new CONFIG.Dice.daggerheart.DHRoll('');
|
const roll = new CONFIG.Dice.daggerheart.DHRoll('');
|
||||||
roll._evaluated = true;
|
roll._evaluated = true;
|
||||||
message = config.message = await CONFIG.Dice.daggerheart.DHRoll.toMessage(roll, config);
|
message = config.message = await CONFIG.Dice.daggerheart.DHRoll.toMessage(roll, config);
|
||||||
}
|
}
|
||||||
if(SaveField.getAutomation() !== CONFIG.DH.SETTINGS.actionAutomationChoices.never.id || force) {
|
if (SaveField.getAutomation() !== CONFIG.DH.SETTINGS.actionAutomationChoices.never.id || force) {
|
||||||
targets ??= config.targets.filter(t => !config.hasRoll || t.hit);
|
targets ??= config.targets.filter(t => !config.hasRoll || t.hit);
|
||||||
await SaveField.rollAllSave.call(this, targets, config.event, message);
|
await SaveField.rollAllSave.call(this, targets, config.event, message);
|
||||||
} else return false;
|
} else return false;
|
||||||
|
|
@ -55,32 +57,32 @@ export default class SaveField extends fields.SchemaField {
|
||||||
* @param {ChatMessage} message The ChatMessage the triggered button comes from.
|
* @param {ChatMessage} message The ChatMessage the triggered button comes from.
|
||||||
*/
|
*/
|
||||||
static async rollAllSave(targets, event, message) {
|
static async rollAllSave(targets, event, message) {
|
||||||
if(!targets) return;
|
if (!targets) return;
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const aPromise = [];
|
const aPromise = [];
|
||||||
targets.forEach(target => {
|
targets.forEach(target => {
|
||||||
aPromise.push(
|
aPromise.push(
|
||||||
new Promise(async subResolve => {
|
new Promise(async subResolve => {
|
||||||
const actor = fromUuidSync(target.actorId);
|
const actor = fromUuidSync(target.actorId);
|
||||||
if(actor) {
|
if (actor) {
|
||||||
const rollSave = game.user === actor.owner ?
|
const rollSave =
|
||||||
SaveField.rollSave.call(this, actor, event)
|
game.user === actor.owner
|
||||||
: actor.owner
|
? SaveField.rollSave.call(this, actor, event)
|
||||||
.query('reactionRoll', {
|
: actor.owner.query('reactionRoll', {
|
||||||
actionId: this.uuid,
|
actionId: this.uuid,
|
||||||
actorId: actor.uuid,
|
actorId: actor.uuid,
|
||||||
event,
|
event,
|
||||||
message
|
message
|
||||||
});
|
});
|
||||||
const result = await rollSave;
|
const result = await rollSave;
|
||||||
await SaveField.updateSaveMessage.call(this, result, message, target.id);
|
await SaveField.updateSaveMessage.call(this, result, message, target.id);
|
||||||
subResolve();
|
subResolve();
|
||||||
} else subResolve();
|
} else subResolve();
|
||||||
})
|
})
|
||||||
)
|
);
|
||||||
});
|
});
|
||||||
Promise.all(aPromise).then(result => resolve());
|
Promise.all(aPromise).then(result => resolve());
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -93,10 +95,10 @@ export default class SaveField extends fields.SchemaField {
|
||||||
static async rollSave(actor, event) {
|
static async rollSave(actor, event) {
|
||||||
if (!actor) return;
|
if (!actor) return;
|
||||||
const title = actor.isNPC
|
const title = actor.isNPC
|
||||||
? game.i18n.localize('DAGGERHEART.GENERAL.reactionRoll')
|
? game.i18n.localize('DAGGERHEART.GENERAL.reactionRoll')
|
||||||
: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
|
: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
|
||||||
ability: game.i18n.localize(abilities[this.save.trait]?.label)
|
ability: game.i18n.localize(abilities[this.save.trait]?.label)
|
||||||
}),
|
}),
|
||||||
rollConfig = {
|
rollConfig = {
|
||||||
event,
|
event,
|
||||||
title,
|
title,
|
||||||
|
|
@ -109,7 +111,8 @@ export default class SaveField extends fields.SchemaField {
|
||||||
hasRoll: true,
|
hasRoll: true,
|
||||||
data: actor.getRollData()
|
data: actor.getRollData()
|
||||||
};
|
};
|
||||||
if(SaveField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.always.id) rollConfig.dialog = { configure: false };
|
if (SaveField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.always.id)
|
||||||
|
rollConfig.dialog = { configure: false };
|
||||||
return actor.diceRoll(rollConfig);
|
return actor.diceRoll(rollConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -121,27 +124,28 @@ export default class SaveField extends fields.SchemaField {
|
||||||
*/
|
*/
|
||||||
static async updateSaveMessage(result, message, targetId) {
|
static async updateSaveMessage(result, message, targetId) {
|
||||||
if (!result) return;
|
if (!result) return;
|
||||||
const updateMsg = async function(message, targetId, result) {
|
const updateMsg = async function (message, targetId, result) {
|
||||||
// setTimeout(async () => {
|
// setTimeout(async () => {
|
||||||
const chatMessage = ui.chat.collection.get(message._id),
|
const chatMessage = ui.chat.collection.get(message._id),
|
||||||
changes = {
|
changes = {
|
||||||
flags: {
|
flags: {
|
||||||
[game.system.id]: {
|
[game.system.id]: {
|
||||||
reactionRolls: {
|
reactionRolls: {
|
||||||
[targetId]:
|
[targetId]: {
|
||||||
{
|
result: result.roll.total,
|
||||||
result: result.roll.total,
|
success: result.roll.success
|
||||||
success: result.roll.success
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
await chatMessage.update(changes);
|
};
|
||||||
|
await chatMessage.update(changes);
|
||||||
// }, 100);
|
// }, 100);
|
||||||
};
|
};
|
||||||
if (game.modules.get('dice-so-nice')?.active)
|
if (game.modules.get('dice-so-nice')?.active)
|
||||||
game.dice3d.waitFor3DAnimationByMessageID(result.message.id ?? result.message._id).then(async () => await updateMsg(message, targetId, result));
|
game.dice3d
|
||||||
|
.waitFor3DAnimationByMessageID(result.message.id ?? result.message._id)
|
||||||
|
.then(async () => await updateMsg(message, targetId, result));
|
||||||
else await updateMsg(message, targetId, result);
|
else await updateMsg(message, targetId, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -150,7 +154,12 @@ export default class SaveField extends fields.SchemaField {
|
||||||
* @returns {string} Id from settingsConfig.mjs actionAutomationChoices
|
* @returns {string} Id from settingsConfig.mjs actionAutomationChoices
|
||||||
*/
|
*/
|
||||||
static getAutomation() {
|
static getAutomation() {
|
||||||
return (game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.save.gm) || (!game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.save.players)
|
return (
|
||||||
|
(game.user.isGM &&
|
||||||
|
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.save.gm) ||
|
||||||
|
(!game.user.isGM &&
|
||||||
|
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.save.players)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -167,8 +176,7 @@ export default class SaveField extends fields.SchemaField {
|
||||||
const actor = await fromUuid(actorId),
|
const actor = await fromUuid(actorId),
|
||||||
action = await fromUuid(actionId);
|
action = await fromUuid(actionId);
|
||||||
if (!actor || !actor?.isOwner) reject();
|
if (!actor || !actor?.isOwner) reject();
|
||||||
SaveField.rollSave.call(action, actor, event, message)
|
SaveField.rollSave.call(action, actor, event, message).then(result => resolve(result));
|
||||||
.then(result => resolve(result));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
export default class TargetField extends fields.SchemaField {
|
export default class TargetField extends fields.SchemaField {
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
constructor(options = {}, context = {}) {
|
constructor(options = {}, context = {}) {
|
||||||
const targetFields = {
|
const targetFields = {
|
||||||
type: new fields.StringField({
|
type: new fields.StringField({
|
||||||
choices: CONFIG.DH.GENERAL.targetTypes,
|
choices: CONFIG.DH.GENERAL.targetTypes,
|
||||||
initial: CONFIG.DH.GENERAL.targetTypes.any.id,
|
initial: CONFIG.DH.GENERAL.targetTypes.any.id,
|
||||||
nullable: true
|
nullable: true,
|
||||||
|
blank: true
|
||||||
}),
|
}),
|
||||||
amount: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 })
|
amount: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 })
|
||||||
};
|
};
|
||||||
|
|
@ -21,7 +21,7 @@ export default class TargetField extends fields.SchemaField {
|
||||||
* @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods.
|
* @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods.
|
||||||
*/
|
*/
|
||||||
prepareConfig(config) {
|
prepareConfig(config) {
|
||||||
if (!this.target?.type) return config.targets = [];
|
if (!this.target?.type) return (config.targets = []);
|
||||||
config.hasTarget = true;
|
config.hasTarget = true;
|
||||||
let targets;
|
let targets;
|
||||||
// If the Action is configured as self-targeted, set targets as the owner.
|
// If the Action is configured as self-targeted, set targets as the owner.
|
||||||
|
|
@ -62,15 +62,11 @@ export default class TargetField extends fields.SchemaField {
|
||||||
* @returns {boolean} If both actors respect the provided type.
|
* @returns {boolean} If both actors respect the provided type.
|
||||||
*/
|
*/
|
||||||
static isTargetFriendly(actor, target, type) {
|
static isTargetFriendly(actor, target, type) {
|
||||||
const actorDisposition = actor.token
|
const actorDisposition = actor.token ? actor.token.disposition : actor.prototypeToken.disposition,
|
||||||
? actor.token.disposition
|
|
||||||
: actor.prototypeToken.disposition,
|
|
||||||
targetDisposition = target.document.disposition;
|
targetDisposition = target.document.disposition;
|
||||||
return (
|
return (
|
||||||
(type === CONFIG.DH.GENERAL.targetTypes.friendly.id &&
|
(type === CONFIG.DH.GENERAL.targetTypes.friendly.id && actorDisposition === targetDisposition) ||
|
||||||
actorDisposition === targetDisposition) ||
|
(type === CONFIG.DH.GENERAL.targetTypes.hostile.id && actorDisposition + targetDisposition === 0)
|
||||||
(type === CONFIG.DH.GENERAL.targetTypes.hostile.id &&
|
|
||||||
actorDisposition + targetDisposition === 0)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import AttachableItem from './attachableItem.mjs';
|
import AttachableItem from './attachableItem.mjs';
|
||||||
import { armorFeatures } from '../../config/itemConfig.mjs';
|
|
||||||
|
|
||||||
export default class DHArmor extends AttachableItem {
|
export default class DHArmor extends AttachableItem {
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
|
|
@ -25,7 +24,7 @@ export default class DHArmor extends AttachableItem {
|
||||||
new fields.SchemaField({
|
new fields.SchemaField({
|
||||||
value: new fields.StringField({
|
value: new fields.StringField({
|
||||||
required: true,
|
required: true,
|
||||||
choices: CONFIG.DH.ITEM.armorFeatures,
|
choices: CONFIG.DH.ITEM.allArmorFeatures,
|
||||||
blank: true
|
blank: true
|
||||||
}),
|
}),
|
||||||
effectIds: new fields.ArrayField(new fields.StringField({ required: true })),
|
effectIds: new fields.ArrayField(new fields.StringField({ required: true })),
|
||||||
|
|
@ -60,13 +59,14 @@ export default class DHArmor extends AttachableItem {
|
||||||
const allowed = await super._preUpdate(changes, options, user);
|
const allowed = await super._preUpdate(changes, options, user);
|
||||||
if (allowed === false) return false;
|
if (allowed === false) return false;
|
||||||
|
|
||||||
|
const changedArmorFeatures = changes.system?.armorFeatures ?? [];
|
||||||
|
const removedFeatures = this.armorFeatures.filter(x => changedArmorFeatures.every(y => y.value !== x.value));
|
||||||
if (changes.system?.armorFeatures) {
|
if (changes.system?.armorFeatures) {
|
||||||
const removed = this.armorFeatures.filter(x => !changes.system.armorFeatures.includes(x));
|
const added = changedArmorFeatures.filter(x => this.armorFeatures.every(y => y.value !== x.value));
|
||||||
const added = changes.system.armorFeatures.filter(x => !this.armorFeatures.includes(x));
|
|
||||||
|
|
||||||
const effectIds = [];
|
const effectIds = [];
|
||||||
const actionIds = [];
|
const actionIds = [];
|
||||||
for (var feature of removed) {
|
for (var feature of removedFeatures) {
|
||||||
effectIds.push(...feature.effectIds);
|
effectIds.push(...feature.effectIds);
|
||||||
actionIds.push(...feature.actionIds);
|
actionIds.push(...feature.actionIds);
|
||||||
}
|
}
|
||||||
|
|
@ -76,8 +76,9 @@ export default class DHArmor extends AttachableItem {
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
|
const allFeatures = CONFIG.DH.ITEM.allArmorFeatures();
|
||||||
for (const feature of added) {
|
for (const feature of added) {
|
||||||
const featureData = armorFeatures[feature.value];
|
const featureData = allFeatures[feature.value];
|
||||||
if (featureData.effects?.length > 0) {
|
if (featureData.effects?.length > 0) {
|
||||||
const embeddedItems = await this.parent.createEmbeddedDocuments(
|
const embeddedItems = await this.parent.createEmbeddedDocuments(
|
||||||
'ActiveEffect',
|
'ActiveEffect',
|
||||||
|
|
@ -91,7 +92,7 @@ export default class DHArmor extends AttachableItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
const newActions = {};
|
const newActions = {};
|
||||||
if (featureData.actions?.length > 0) {
|
if (featureData.actions?.length > 0 || featureData.actions?.size > 0) {
|
||||||
for (let action of featureData.actions) {
|
for (let action of featureData.actions) {
|
||||||
const embeddedEffects = await this.parent.createEmbeddedDocuments(
|
const embeddedEffects = await this.parent.createEmbeddedDocuments(
|
||||||
'ActiveEffect',
|
'ActiveEffect',
|
||||||
|
|
@ -110,10 +111,12 @@ export default class DHArmor extends AttachableItem {
|
||||||
{
|
{
|
||||||
...cls.getSourceConfig(this),
|
...cls.getSourceConfig(this),
|
||||||
...action,
|
...action,
|
||||||
|
type: action.type,
|
||||||
_id: actionId,
|
_id: actionId,
|
||||||
name: game.i18n.localize(action.name),
|
name: game.i18n.localize(action.name),
|
||||||
description: game.i18n.localize(action.description),
|
description: game.i18n.localize(action.description),
|
||||||
effects: embeddedEffects.map(x => ({ _id: x.id }))
|
effects: embeddedEffects.map(x => ({ _id: x.id })),
|
||||||
|
systemPath: 'actions'
|
||||||
},
|
},
|
||||||
{ parent: this }
|
{ parent: this }
|
||||||
);
|
);
|
||||||
|
|
@ -126,6 +129,10 @@ export default class DHArmor extends AttachableItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_onUpdate(a, b, c) {
|
||||||
|
super._onUpdate(a, b, c);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a list of localized tags based on this item's type-specific properties.
|
* Generates a list of localized tags based on this item's type-specific properties.
|
||||||
* @returns {string[]} An array of localized tag strings.
|
* @returns {string[]} An array of localized tag strings.
|
||||||
|
|
@ -145,7 +152,8 @@ export default class DHArmor extends AttachableItem {
|
||||||
*/
|
*/
|
||||||
_getLabels() {
|
_getLabels() {
|
||||||
const labels = [];
|
const labels = [];
|
||||||
if(this.baseScore) labels.push(`${game.i18n.localize('DAGGERHEART.ITEMS.Armor.baseScore')}: ${this.baseScore}`)
|
if (this.baseScore)
|
||||||
|
labels.push(`${game.i18n.localize('DAGGERHEART.ITEMS.Armor.baseScore')}: ${this.baseScore}`);
|
||||||
return labels;
|
return labels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ export default class DHWeapon extends AttachableItem {
|
||||||
new fields.SchemaField({
|
new fields.SchemaField({
|
||||||
value: new fields.StringField({
|
value: new fields.StringField({
|
||||||
required: true,
|
required: true,
|
||||||
choices: CONFIG.DH.ITEM.weaponFeatures,
|
choices: CONFIG.DH.ITEM.allWeaponFeatures,
|
||||||
blank: true
|
blank: true
|
||||||
}),
|
}),
|
||||||
effectIds: new fields.ArrayField(new fields.StringField({ required: true })),
|
effectIds: new fields.ArrayField(new fields.StringField({ required: true })),
|
||||||
|
|
@ -116,13 +116,14 @@ export default class DHWeapon extends AttachableItem {
|
||||||
const allowed = await super._preUpdate(changes, options, user);
|
const allowed = await super._preUpdate(changes, options, user);
|
||||||
if (allowed === false) return false;
|
if (allowed === false) return false;
|
||||||
|
|
||||||
|
const changedWeaponFeatures = changes.system?.weaponFeatures ?? [];
|
||||||
|
const removedFeatures = this.weaponFeatures.filter(x => changedWeaponFeatures.every(y => y.value !== x.value));
|
||||||
if (changes.system?.weaponFeatures) {
|
if (changes.system?.weaponFeatures) {
|
||||||
const removed = this.weaponFeatures.filter(x => !changes.system.weaponFeatures.includes(x));
|
const added = changedWeaponFeatures.filter(x => this.weaponFeatures.every(y => y.value !== x.value));
|
||||||
const added = changes.system.weaponFeatures.filter(x => !this.weaponFeatures.includes(x));
|
|
||||||
|
|
||||||
const removedEffectsUpdate = [];
|
const removedEffectsUpdate = [];
|
||||||
const removedActionsUpdate = [];
|
const removedActionsUpdate = [];
|
||||||
for (let weaponFeature of removed) {
|
for (let weaponFeature of removedFeatures) {
|
||||||
removedEffectsUpdate.push(...weaponFeature.effectIds);
|
removedEffectsUpdate.push(...weaponFeature.effectIds);
|
||||||
removedActionsUpdate.push(...weaponFeature.actionIds);
|
removedActionsUpdate.push(...weaponFeature.actionIds);
|
||||||
}
|
}
|
||||||
|
|
@ -133,8 +134,9 @@ export default class DHWeapon extends AttachableItem {
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
|
const allFeatures = CONFIG.DH.ITEM.allWeaponFeatures();
|
||||||
for (let weaponFeature of added) {
|
for (let weaponFeature of added) {
|
||||||
const featureData = CONFIG.DH.ITEM.weaponFeatures[weaponFeature.value];
|
const featureData = allFeatures[weaponFeature.value];
|
||||||
if (featureData.effects?.length > 0) {
|
if (featureData.effects?.length > 0) {
|
||||||
const embeddedItems = await this.parent.createEmbeddedDocuments(
|
const embeddedItems = await this.parent.createEmbeddedDocuments(
|
||||||
'ActiveEffect',
|
'ActiveEffect',
|
||||||
|
|
@ -148,7 +150,7 @@ export default class DHWeapon extends AttachableItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
const newActions = {};
|
const newActions = {};
|
||||||
if (featureData.actions?.length > 0) {
|
if (featureData.actions?.length > 0 || featureData.actions?.size > 0) {
|
||||||
for (let action of featureData.actions) {
|
for (let action of featureData.actions) {
|
||||||
const embeddedEffects = await this.parent.createEmbeddedDocuments(
|
const embeddedEffects = await this.parent.createEmbeddedDocuments(
|
||||||
'ActiveEffect',
|
'ActiveEffect',
|
||||||
|
|
@ -170,10 +172,12 @@ export default class DHWeapon extends AttachableItem {
|
||||||
{
|
{
|
||||||
...cls.getSourceConfig(this),
|
...cls.getSourceConfig(this),
|
||||||
...action,
|
...action,
|
||||||
|
type: action.type,
|
||||||
_id: actionId,
|
_id: actionId,
|
||||||
name: game.i18n.localize(action.name),
|
name: game.i18n.localize(action.name),
|
||||||
description: game.i18n.localize(action.description),
|
description: game.i18n.localize(action.description),
|
||||||
effects: embeddedEffects.map(x => ({ _id: x.id }))
|
effects: embeddedEffects.map(x => ({ _id: x.id })),
|
||||||
|
systemPath: 'actions'
|
||||||
},
|
},
|
||||||
{ parent: this }
|
{ parent: this }
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -169,7 +169,7 @@ export const defaultLevelTiers = {
|
||||||
tiers: {
|
tiers: {
|
||||||
2: {
|
2: {
|
||||||
tier: 2,
|
tier: 2,
|
||||||
name: 'Tier 2',
|
name: 'DAGGERHEART.APPLICATIONS.Levelup.tier2.name',
|
||||||
levels: {
|
levels: {
|
||||||
start: 2,
|
start: 2,
|
||||||
end: 4
|
end: 4
|
||||||
|
|
@ -232,7 +232,7 @@ export const defaultLevelTiers = {
|
||||||
},
|
},
|
||||||
3: {
|
3: {
|
||||||
tier: 3,
|
tier: 3,
|
||||||
name: 'Tier 3',
|
name: 'DAGGERHEART.APPLICATIONS.Levelup.tier3.name',
|
||||||
levels: {
|
levels: {
|
||||||
start: 5,
|
start: 5,
|
||||||
end: 7
|
end: 7
|
||||||
|
|
@ -313,7 +313,7 @@ export const defaultLevelTiers = {
|
||||||
},
|
},
|
||||||
4: {
|
4: {
|
||||||
tier: 4,
|
tier: 4,
|
||||||
name: 'Tier 4',
|
name: 'DAGGERHEART.APPLICATIONS.Levelup.tier4.name',
|
||||||
levels: {
|
levels: {
|
||||||
start: 8,
|
start: 8,
|
||||||
end: 10
|
end: 10
|
||||||
|
|
|
||||||
|
|
@ -234,7 +234,7 @@ export class DhLevelup extends foundry.abstract.DataModel {
|
||||||
const subclassInTier = subclasses.some(x => x.tier === Number(tierKey));
|
const subclassInTier = subclasses.some(x => x.tier === Number(tierKey));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: tier.name,
|
name: game.i18n.localize(tier.name),
|
||||||
active: this.currentLevel >= Math.min(...tier.belongingLevels),
|
active: this.currentLevel >= Math.min(...tier.belongingLevels),
|
||||||
groups: Object.keys(tier.options).map(optionKey => {
|
groups: Object.keys(tier.options).map(optionKey => {
|
||||||
const option = tier.options[optionKey];
|
const option = tier.options[optionKey];
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,27 @@
|
||||||
export default class DhAppearance extends foundry.abstract.DataModel {
|
export default class DhAppearance extends foundry.abstract.DataModel {
|
||||||
static LOCALIZATION_PREFIXES = ["DAGGERHEART.SETTINGS.Appearance"];
|
static LOCALIZATION_PREFIXES = ['DAGGERHEART.SETTINGS.Appearance'];
|
||||||
|
|
||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
const { StringField, ColorField, BooleanField, SchemaField } = foundry.data.fields;
|
const { StringField, ColorField, BooleanField, SchemaField } = foundry.data.fields;
|
||||||
|
|
||||||
// helper to create dice style schema
|
// helper to create dice style schema
|
||||||
const diceStyle = ({ fg, bg, outline, edge }) => new SchemaField({
|
const diceStyle = ({ fg, bg, outline, edge }) =>
|
||||||
foreground: new ColorField({ required: true, initial: fg }),
|
new SchemaField({
|
||||||
background: new ColorField({ required: true, initial: bg }),
|
foreground: new ColorField({ required: true, initial: fg }),
|
||||||
outline: new ColorField({ required: true, initial: outline }),
|
background: new ColorField({ required: true, initial: bg }),
|
||||||
edge: new ColorField({ required: true, initial: edge }),
|
outline: new ColorField({ required: true, initial: outline }),
|
||||||
texture: new StringField({ initial: 'astralsea', required: true, blank: false }),
|
edge: new ColorField({ required: true, initial: edge }),
|
||||||
colorset: new StringField({ initial: 'inspired', required: true, blank: false }),
|
texture: new StringField({ initial: 'astralsea', required: true, blank: false }),
|
||||||
material: new StringField({ initial: 'metal', required: true, blank: false }),
|
colorset: new StringField({ initial: 'inspired', required: true, blank: false }),
|
||||||
system: new StringField({ initial: 'standard', required: true, blank: false })
|
material: new StringField({ initial: 'metal', required: true, blank: false }),
|
||||||
});
|
system: new StringField({ initial: 'standard', required: true, blank: false })
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
displayFear: new StringField({
|
displayFear: new StringField({
|
||||||
required: true,
|
required: true,
|
||||||
choices: CONFIG.DH.GENERAL.fearDisplay,
|
choices: CONFIG.DH.GENERAL.fearDisplay,
|
||||||
initial: CONFIG.DH.GENERAL.fearDisplay.token.value,
|
initial: CONFIG.DH.GENERAL.fearDisplay.token.value
|
||||||
}),
|
}),
|
||||||
diceSoNice: new SchemaField({
|
diceSoNice: new SchemaField({
|
||||||
hope: diceStyle({ fg: '#ffffff', bg: '#ffe760', outline: '#000000', edge: '#ffffff' }),
|
hope: diceStyle({ fg: '#ffffff', bg: '#ffe760', outline: '#000000', edge: '#ffffff' }),
|
||||||
|
|
@ -39,7 +40,7 @@ export default class DhAppearance extends foundry.abstract.DataModel {
|
||||||
target: new BooleanField()
|
target: new BooleanField()
|
||||||
}),
|
}),
|
||||||
hideAttribution: new BooleanField(),
|
hideAttribution: new BooleanField(),
|
||||||
showGenericStatusEffects: new BooleanField({ initial: true }),
|
showGenericStatusEffects: new BooleanField({ initial: true })
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -97,13 +97,13 @@ export default class DhAutomation extends foundry.abstract.DataModel {
|
||||||
damage: new fields.SchemaField({
|
damage: new fields.SchemaField({
|
||||||
gm: new fields.StringField({
|
gm: new fields.StringField({
|
||||||
required: true,
|
required: true,
|
||||||
initial: "never",
|
initial: 'never',
|
||||||
choices: CONFIG.DH.SETTINGS.actionAutomationChoices,
|
choices: CONFIG.DH.SETTINGS.actionAutomationChoices,
|
||||||
label: 'DAGGERHEART.GENERAL.gm'
|
label: 'DAGGERHEART.GENERAL.gm'
|
||||||
}),
|
}),
|
||||||
players: new fields.StringField({
|
players: new fields.StringField({
|
||||||
required: true,
|
required: true,
|
||||||
initial: "never",
|
initial: 'never',
|
||||||
choices: CONFIG.DH.SETTINGS.actionAutomationChoices,
|
choices: CONFIG.DH.SETTINGS.actionAutomationChoices,
|
||||||
label: 'DAGGERHEART.GENERAL.player.plurial'
|
label: 'DAGGERHEART.GENERAL.player.plurial'
|
||||||
})
|
})
|
||||||
|
|
@ -111,13 +111,13 @@ export default class DhAutomation extends foundry.abstract.DataModel {
|
||||||
save: new fields.SchemaField({
|
save: new fields.SchemaField({
|
||||||
gm: new fields.StringField({
|
gm: new fields.StringField({
|
||||||
required: true,
|
required: true,
|
||||||
initial: "never",
|
initial: 'never',
|
||||||
choices: CONFIG.DH.SETTINGS.actionAutomationChoices,
|
choices: CONFIG.DH.SETTINGS.actionAutomationChoices,
|
||||||
label: 'DAGGERHEART.GENERAL.gm'
|
label: 'DAGGERHEART.GENERAL.gm'
|
||||||
}),
|
}),
|
||||||
players: new fields.StringField({
|
players: new fields.StringField({
|
||||||
required: true,
|
required: true,
|
||||||
initial: "never",
|
initial: 'never',
|
||||||
choices: CONFIG.DH.SETTINGS.actionAutomationChoices,
|
choices: CONFIG.DH.SETTINGS.actionAutomationChoices,
|
||||||
label: 'DAGGERHEART.GENERAL.player.plurial'
|
label: 'DAGGERHEART.GENERAL.player.plurial'
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,35 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
|
||||||
label: new fields.StringField({ required: true, label: 'DAGGERHEART.GENERAL.label' }),
|
label: new fields.StringField({ required: true, label: 'DAGGERHEART.GENERAL.label' }),
|
||||||
description: new fields.StringField()
|
description: new fields.StringField()
|
||||||
})
|
})
|
||||||
)
|
),
|
||||||
|
itemFeatures: new fields.SchemaField({
|
||||||
|
weaponFeatures: new fields.TypedObjectField(
|
||||||
|
new fields.SchemaField({
|
||||||
|
name: new fields.StringField({ required: true }),
|
||||||
|
img: new fields.FilePathField({
|
||||||
|
initial: 'icons/magic/life/cross-worn-green.webp',
|
||||||
|
categories: ['IMAGE'],
|
||||||
|
base64: false
|
||||||
|
}),
|
||||||
|
description: new fields.HTMLField(),
|
||||||
|
actions: new ActionsField(),
|
||||||
|
effects: new fields.ArrayField(new fields.ObjectField())
|
||||||
|
})
|
||||||
|
),
|
||||||
|
armorFeatures: new fields.TypedObjectField(
|
||||||
|
new fields.SchemaField({
|
||||||
|
name: new fields.StringField({ required: true }),
|
||||||
|
img: new fields.FilePathField({
|
||||||
|
initial: 'icons/magic/life/cross-worn-green.webp',
|
||||||
|
categories: ['IMAGE'],
|
||||||
|
base64: false
|
||||||
|
}),
|
||||||
|
description: new fields.HTMLField(),
|
||||||
|
actions: new ActionsField(),
|
||||||
|
effects: new fields.ArrayField(new fields.ObjectField())
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,7 @@ export default class DamageRoll extends DHRoll {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
await super.buildPost(roll, config, message);
|
await super.buildPost(roll, config, message);
|
||||||
if (config.source?.message)
|
if (config.source?.message) chatMessage.update({ 'system.damage': config.damage });
|
||||||
chatMessage.update({ 'system.damage': config.damage });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static unifyDamageRoll(rolls) {
|
static unifyDamageRoll(rolls) {
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,10 @@ export default class DHRoll extends Roll {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const hook of config.hooks) {
|
for (const hook of config.hooks) {
|
||||||
if (Hooks.call(`${CONFIG.DH.id}.post${hook.capitalize()}RollConfiguration`, roll, config, message) === false) return [];
|
if (
|
||||||
|
Hooks.call(`${CONFIG.DH.id}.post${hook.capitalize()}RollConfiguration`, roll, config, message) === false
|
||||||
|
)
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
return roll;
|
return roll;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -156,7 +156,10 @@ export default class DualityRoll extends D20Roll {
|
||||||
|
|
||||||
if (this.options.roll.trait && this.data.traits?.[this.options.roll.trait])
|
if (this.options.roll.trait && this.data.traits?.[this.options.roll.trait])
|
||||||
modifiers.unshift({
|
modifiers.unshift({
|
||||||
label: this.options.roll.type === CONFIG.DH.GENERAL.rollTypes.spellcast.id ? "DAGGERHEART.CONFIG.RollTypes.spellcast.name" : `DAGGERHEART.CONFIG.Traits.${this.options.roll.trait}.name`,
|
label:
|
||||||
|
this.options.roll.type === CONFIG.DH.GENERAL.rollTypes.spellcast.id
|
||||||
|
? 'DAGGERHEART.CONFIG.RollTypes.spellcast.name'
|
||||||
|
: `DAGGERHEART.CONFIG.Traits.${this.options.roll.trait}.name`,
|
||||||
value: this.data.traits[this.options.roll.trait].value
|
value: this.data.traits[this.options.roll.trait].value
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { emitAsGM, GMUpdateEvent } from "../systemRegistration/socket.mjs";
|
import { emitAsGM, GMUpdateEvent } from '../systemRegistration/socket.mjs';
|
||||||
|
|
||||||
export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
||||||
targetHook = null;
|
targetHook = null;
|
||||||
|
|
@ -105,10 +105,10 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
||||||
if (itemDesc && autoExpandRoll.desc) itemDesc.setAttribute('open', '');
|
if (itemDesc && autoExpandRoll.desc) itemDesc.setAttribute('open', '');
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!this.isAuthor && !this.speakerActor?.isOwner) {
|
if (!this.isAuthor && !this.speakerActor?.isOwner) {
|
||||||
const applyButtons = html.querySelector(".apply-buttons");
|
const applyButtons = html.querySelector('.apply-buttons');
|
||||||
applyButtons?.remove();
|
applyButtons?.remove();
|
||||||
const buttons = html.querySelectorAll(".ability-card-footer > .ability-use-button");
|
const buttons = html.querySelectorAll('.ability-card-footer > .ability-use-button');
|
||||||
buttons.forEach(b => b.remove());
|
buttons.forEach(b => b.remove());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -149,7 +149,7 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
const config = foundry.utils.deepClone(this.system);
|
const config = foundry.utils.deepClone(this.system);
|
||||||
config.event = event;
|
config.event = event;
|
||||||
this.system.action?.workflow.get("damage")?.execute(config, this._id, true);
|
this.system.action?.workflow.get('damage')?.execute(config, this._id, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
async onApplyDamage(event) {
|
async onApplyDamage(event) {
|
||||||
|
|
@ -173,7 +173,7 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
||||||
return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelectedOrPerm'));
|
return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelectedOrPerm'));
|
||||||
|
|
||||||
this.consumeOnSuccess();
|
this.consumeOnSuccess();
|
||||||
this.system.action?.workflow.get("applyDamage")?.execute(config, targets, true);
|
this.system.action?.workflow.get('applyDamage')?.execute(config, targets, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
async onRollSave(event) {
|
async onRollSave(event) {
|
||||||
|
|
@ -187,7 +187,12 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
||||||
game.system.api.fields.ActionFields.SaveField.rollSave.call(action, token.actor, event).then(result =>
|
game.system.api.fields.ActionFields.SaveField.rollSave.call(action, token.actor, event).then(result =>
|
||||||
emitAsGM(
|
emitAsGM(
|
||||||
GMUpdateEvent.UpdateSaveMessage,
|
GMUpdateEvent.UpdateSaveMessage,
|
||||||
game.system.api.fields.ActionFields.SaveField.updateSaveMessage.bind(action, result, this, token.id),
|
game.system.api.fields.ActionFields.SaveField.updateSaveMessage.bind(
|
||||||
|
action,
|
||||||
|
result,
|
||||||
|
this,
|
||||||
|
token.id
|
||||||
|
),
|
||||||
{
|
{
|
||||||
action: action.uuid,
|
action: action.uuid,
|
||||||
message: this._id,
|
message: this._id,
|
||||||
|
|
@ -205,7 +210,7 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
||||||
const targets = this.system.hitTargets,
|
const targets = this.system.hitTargets,
|
||||||
config = foundry.utils.deepClone(this.system);
|
config = foundry.utils.deepClone(this.system);
|
||||||
config.event = event;
|
config.event = event;
|
||||||
this.system.action?.workflow.get("save")?.execute(config, targets, true);
|
this.system.action?.workflow.get('save')?.execute(config, targets, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
async onApplyEffect(event) {
|
async onApplyEffect(event) {
|
||||||
|
|
@ -216,16 +221,15 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
||||||
if (targets.length === 0)
|
if (targets.length === 0)
|
||||||
ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelectedOrPerm'));
|
ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelectedOrPerm'));
|
||||||
this.consumeOnSuccess();
|
this.consumeOnSuccess();
|
||||||
this.system.action?.workflow.get("effects")?.execute(config, targets, true);
|
this.system.action?.workflow.get('effects')?.execute(config, targets, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
filterPermTargets(targets) {
|
filterPermTargets(targets) {
|
||||||
return targets.filter(t => fromUuidSync(t.actorId)?.canUserModify(game.user, "update"))
|
return targets.filter(t => fromUuidSync(t.actorId)?.canUserModify(game.user, 'update'));
|
||||||
}
|
}
|
||||||
|
|
||||||
consumeOnSuccess() {
|
consumeOnSuccess() {
|
||||||
if (!this.system.successConsumed && !this.targetSelection)
|
if (!this.system.successConsumed && !this.targetSelection) this.system.action?.consume(this.system, true);
|
||||||
this.system.action?.consume(this.system, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hoverTarget(event) {
|
hoverTarget(event) {
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ export default class DHItem extends foundry.documents.Item {
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
static migrateData(source) {
|
static migrateData(source) {
|
||||||
if(source.system?.attack && !source.system.attack.type) source.system.attack.type = "attack";
|
if (source.system?.attack && !source.system.attack.type) source.system.attack.type = 'attack';
|
||||||
return super.migrateData(source);
|
return super.migrateData(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,12 @@ export default function DhDualityRollEnricher(match, _options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDualityMessage(roll, flavor) {
|
function getDualityMessage(roll, flavor) {
|
||||||
const trait = roll.trait && abilities[roll.trait] ? game.i18n.localize(abilities[roll.trait].label) : null;
|
const trait = roll?.trait && abilities[roll.trait] ? game.i18n.localize(abilities[roll.trait].label) : null;
|
||||||
const label =
|
const label =
|
||||||
flavor ??
|
flavor ??
|
||||||
(roll.trait
|
(roll?.trait
|
||||||
? game.i18n.format('DAGGERHEART.GENERAL.rollWith', { roll: trait })
|
? game.i18n.format('DAGGERHEART.GENERAL.rollWith', { roll: trait })
|
||||||
: roll.reaction
|
: roll?.reaction
|
||||||
? game.i18n.localize('DAGGERHEART.GENERAL.reactionRoll')
|
? game.i18n.localize('DAGGERHEART.GENERAL.reactionRoll')
|
||||||
: game.i18n.localize('DAGGERHEART.GENERAL.duality'));
|
: game.i18n.localize('DAGGERHEART.GENERAL.duality'));
|
||||||
|
|
||||||
|
|
@ -22,9 +22,9 @@ function getDualityMessage(roll, flavor) {
|
||||||
? game.i18n.localize(abilities[roll.trait].label)
|
? game.i18n.localize(abilities[roll.trait].label)
|
||||||
: game.i18n.localize('DAGGERHEART.GENERAL.duality');
|
: game.i18n.localize('DAGGERHEART.GENERAL.duality');
|
||||||
|
|
||||||
const advantage = roll.advantage
|
const advantage = roll?.advantage
|
||||||
? CONFIG.DH.ACTIONS.advantageState.advantage.value
|
? CONFIG.DH.ACTIONS.advantageState.advantage.value
|
||||||
: roll.disadvantage
|
: roll?.disadvantage
|
||||||
? CONFIG.DH.ACTIONS.advantageState.disadvantage.value
|
? CONFIG.DH.ACTIONS.advantageState.disadvantage.value
|
||||||
: undefined;
|
: undefined;
|
||||||
const advantageLabel =
|
const advantageLabel =
|
||||||
|
|
@ -36,21 +36,21 @@ function getDualityMessage(roll, flavor) {
|
||||||
|
|
||||||
const dualityElement = document.createElement('span');
|
const dualityElement = document.createElement('span');
|
||||||
dualityElement.innerHTML = `
|
dualityElement.innerHTML = `
|
||||||
<button type="button" class="duality-roll-button${roll.inline ? ' inline' : ''}"
|
<button type="button" class="duality-roll-button${roll?.inline ? ' inline' : ''}"
|
||||||
data-title="${label}"
|
data-title="${label}"
|
||||||
data-label="${dataLabel}"
|
data-label="${dataLabel}"
|
||||||
data-reaction="${roll.reaction ? 'true' : 'false'}"
|
data-reaction="${roll?.reaction ? 'true' : 'false'}"
|
||||||
data-hope="${roll.hope ?? 'd12'}"
|
data-hope="${roll?.hope ?? 'd12'}"
|
||||||
data-fear="${roll.fear ?? 'd12'}"
|
data-fear="${roll?.fear ?? 'd12'}"
|
||||||
${advantage ? `data-advantage="${advantage}"` : ''}
|
${advantage ? `data-advantage="${advantage}"` : ''}
|
||||||
${roll.difficulty !== undefined ? `data-difficulty="${roll.difficulty}"` : ''}
|
${roll?.difficulty !== undefined ? `data-difficulty="${roll.difficulty}"` : ''}
|
||||||
${roll.trait && abilities[roll.trait] ? `data-trait="${roll.trait}"` : ''}
|
${roll?.trait && abilities[roll.trait] ? `data-trait="${roll.trait}"` : ''}
|
||||||
${roll.advantage ? 'data-advantage="true"' : ''}
|
${roll?.advantage ? 'data-advantage="true"' : ''}
|
||||||
${roll.disadvantage ? 'data-disadvantage="true"' : ''}
|
${roll?.disadvantage ? 'data-disadvantage="true"' : ''}
|
||||||
>
|
>
|
||||||
${roll.reaction ? '<i class="fa-solid fa-reply"></i>' : '<i class="fa-solid fa-circle-half-stroke"></i>'}
|
${roll?.reaction ? '<i class="fa-solid fa-reply"></i>' : '<i class="fa-solid fa-circle-half-stroke"></i>'}
|
||||||
${label}
|
${label}
|
||||||
${!flavor && (roll.difficulty || advantageLabel) ? `(${[roll.difficulty, advantageLabel ? game.i18n.localize(`DAGGERHEART.GENERAL.${advantageLabel}.short`) : null].filter(x => x).join(' ')})` : ''}
|
${!flavor && (roll?.difficulty || advantageLabel) ? `(${[roll.difficulty, advantageLabel ? game.i18n.localize(`DAGGERHEART.GENERAL.${advantageLabel}.short`) : null].filter(x => x).join(' ')})` : ''}
|
||||||
</button>
|
</button>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,10 +34,7 @@ export const preloadHandlebarsTemplates = async function () {
|
||||||
'systems/daggerheart/templates/ui/chat/parts/damage-part.hbs',
|
'systems/daggerheart/templates/ui/chat/parts/damage-part.hbs',
|
||||||
'systems/daggerheart/templates/ui/chat/parts/target-part.hbs',
|
'systems/daggerheart/templates/ui/chat/parts/target-part.hbs',
|
||||||
'systems/daggerheart/templates/ui/chat/parts/button-part.hbs',
|
'systems/daggerheart/templates/ui/chat/parts/button-part.hbs',
|
||||||
|
|
||||||
'systems/daggerheart/templates/ui/itemBrowser/itemContainer.hbs',
|
'systems/daggerheart/templates/ui/itemBrowser/itemContainer.hbs',
|
||||||
|
'systems/daggerheart/templates/scene/dh-config.hbs'
|
||||||
'systems/daggerheart/templates/scene/dh-config.hbs',
|
|
||||||
|
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -38,8 +38,7 @@ export const registerSocketHooks = () => {
|
||||||
const document = data.uuid ? await fromUuid(data.uuid) : null;
|
const document = data.uuid ? await fromUuid(data.uuid) : null;
|
||||||
switch (data.action) {
|
switch (data.action) {
|
||||||
case GMUpdateEvent.UpdateDocument:
|
case GMUpdateEvent.UpdateDocument:
|
||||||
if (document && data.update)
|
if (document && data.update) await document.update(data.update);
|
||||||
await document.update(data.update);
|
|
||||||
break;
|
break;
|
||||||
case GMUpdateEvent.UpdateEffect:
|
case GMUpdateEvent.UpdateEffect:
|
||||||
if (document && data.update)
|
if (document && data.update)
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
---
|
---
|
||||||
name: Pull Request
|
name: Pull Request
|
||||||
about: Create a new pull request
|
about: Create a new pull request
|
||||||
title: "[PR] <Insert Title here>"
|
title: '[PR] <Insert Title here>'
|
||||||
labels: pr
|
labels: pr
|
||||||
assignees: ''
|
assignees: ''
|
||||||
---
|
---
|
||||||
|
|
||||||
Is this a community PR? Please go to preview tab and click [here](?expand=1&template=community_pull_request_template.md). If not, delete this line.
|
Is this a community PR? Please go to preview tab and click [here](?expand=1&template=community_pull_request_template.md). If not, delete this line.
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
|
||||||
|
|
@ -438,14 +438,14 @@
|
||||||
"_id": "UpFsnlbZkyvM2Ftv",
|
"_id": "UpFsnlbZkyvM2Ftv",
|
||||||
"img": "icons/magic/acid/projectile-smoke-glowing.webp",
|
"img": "icons/magic/acid/projectile-smoke-glowing.webp",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p>Make an attack against all targets in front of the Burrower within Close range. Targets the Burrower succeeds against take <strong>2d6</strong> physical damage and must mark an Armor Slot without receiving its benefi ts (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP and you gain a Fear.</p><p>@Template[type:inFront|range:c]</p>",
|
"description": "<p>Make an attack against all targets in front of the Burrower within Close range. Targets the Burrower succeeds against take <strong>2d6</strong> physical damage and must mark an Armor Slot without receiving its benefits (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP and you gain a Fear.</p><p>@Template[type:inFront|range:c]</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"yd10HwK6Wa3OEvv2": {
|
"yd10HwK6Wa3OEvv2": {
|
||||||
"type": "attack",
|
"type": "attack",
|
||||||
"_id": "yd10HwK6Wa3OEvv2",
|
"_id": "yd10HwK6Wa3OEvv2",
|
||||||
"systemPath": "actions",
|
"systemPath": "actions",
|
||||||
"description": "<p>Make an attack against all targets in front of the Burrower within Close range. Targets the Burrower succeeds against take <strong>2d6</strong> physical damage and must mark an Armor Slot without receiving its benefi ts (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP and you gain a Fear.</p><p>@Template[type:inFront|range:c]</p>",
|
"description": "<p>Make an attack against all targets in front of the Burrower within Close range. Targets the Burrower succeeds against take <strong>2d6</strong> physical damage and must mark an Armor Slot without receiving its benefits (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP and you gain a Fear.</p><p>@Template[type:inFront|range:c]</p>",
|
||||||
"chatDisplay": true,
|
"chatDisplay": true,
|
||||||
"actionType": "action",
|
"actionType": "action",
|
||||||
"cost": [],
|
"cost": [],
|
||||||
|
|
@ -557,11 +557,11 @@
|
||||||
"compendiumSource": null,
|
"compendiumSource": null,
|
||||||
"duplicateSource": null,
|
"duplicateSource": null,
|
||||||
"exportSource": null,
|
"exportSource": null,
|
||||||
"coreVersion": "13.346",
|
"coreVersion": "13.348",
|
||||||
"systemId": "daggerheart",
|
"systemId": "daggerheart",
|
||||||
"systemVersion": "0.0.1",
|
"systemVersion": "1.1.2",
|
||||||
"lastModifiedBy": "MQSznptE5yLT7kj8",
|
"lastModifiedBy": "mdk78Q6pOyHh6aBg",
|
||||||
"modifiedTime": 1754143653876
|
"modifiedTime": 1756510879809
|
||||||
},
|
},
|
||||||
"_key": "!actors.items!89yAh30vaNQOALlz.UpFsnlbZkyvM2Ftv"
|
"_key": "!actors.items!89yAh30vaNQOALlz.UpFsnlbZkyvM2Ftv"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -269,14 +269,14 @@
|
||||||
"name": "Crushing Blows",
|
"name": "Crushing Blows",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p>When the Elemental makes a successful attack, the target must mark an Armor Slot without receiving its benefi ts (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP.</p>",
|
"description": "<p>When the Elemental makes a successful attack, the target must mark an Armor Slot without receiving its benefits (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"0sXciTiPc30v8czv": {
|
"0sXciTiPc30v8czv": {
|
||||||
"type": "damage",
|
"type": "damage",
|
||||||
"_id": "0sXciTiPc30v8czv",
|
"_id": "0sXciTiPc30v8czv",
|
||||||
"systemPath": "actions",
|
"systemPath": "actions",
|
||||||
"description": "<p>When the Elemental makes a successful attack, the target must mark an Armor Slot without receiving its benefi ts (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP.</p>",
|
"description": "<p>When the Elemental makes a successful attack, the target must mark an Armor Slot without receiving its benefits (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP.</p>",
|
||||||
"chatDisplay": true,
|
"chatDisplay": true,
|
||||||
"actionType": "action",
|
"actionType": "action",
|
||||||
"cost": [],
|
"cost": [],
|
||||||
|
|
@ -342,12 +342,12 @@
|
||||||
"compendiumSource": null,
|
"compendiumSource": null,
|
||||||
"duplicateSource": null,
|
"duplicateSource": null,
|
||||||
"exportSource": null,
|
"exportSource": null,
|
||||||
"coreVersion": "13.346",
|
"coreVersion": "13.348",
|
||||||
"systemId": "daggerheart",
|
"systemId": "daggerheart",
|
||||||
"systemVersion": "0.0.1",
|
"systemVersion": "1.1.2",
|
||||||
"createdTime": 1754127683751,
|
"createdTime": 1754127683751,
|
||||||
"modifiedTime": 1754127795809,
|
"modifiedTime": 1756511006257,
|
||||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
"lastModifiedBy": "mdk78Q6pOyHh6aBg"
|
||||||
},
|
},
|
||||||
"_key": "!actors.items!dsfB3YhoL5SudvS2.NnCkXIuATO0s3tSR"
|
"_key": "!actors.items!dsfB3YhoL5SudvS2.NnCkXIuATO0s3tSR"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -275,14 +275,14 @@
|
||||||
"name": "Acidic Form",
|
"name": "Acidic Form",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p>When the Ooze makes a successful attack, the target must mark an Armor Slot without receiving its benefi ts (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP.</p>",
|
"description": "<p>When the Ooze makes a successful attack, the target must mark an Armor Slot without receiving its benefits (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"gtT2oHSyZg9OHHJD": {
|
"gtT2oHSyZg9OHHJD": {
|
||||||
"type": "damage",
|
"type": "damage",
|
||||||
"_id": "gtT2oHSyZg9OHHJD",
|
"_id": "gtT2oHSyZg9OHHJD",
|
||||||
"systemPath": "actions",
|
"systemPath": "actions",
|
||||||
"description": "<p>When the Ooze makes a successful attack, the target must mark an Armor Slot without receiving its benefi ts (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP.</p>",
|
"description": "<p>When the Ooze makes a successful attack, the target must mark an Armor Slot without receiving its benefits (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP.</p>",
|
||||||
"chatDisplay": true,
|
"chatDisplay": true,
|
||||||
"actionType": "action",
|
"actionType": "action",
|
||||||
"cost": [],
|
"cost": [],
|
||||||
|
|
@ -348,12 +348,12 @@
|
||||||
"compendiumSource": null,
|
"compendiumSource": null,
|
||||||
"duplicateSource": null,
|
"duplicateSource": null,
|
||||||
"exportSource": null,
|
"exportSource": null,
|
||||||
"coreVersion": "13.346",
|
"coreVersion": "13.348",
|
||||||
"systemId": "daggerheart",
|
"systemId": "daggerheart",
|
||||||
"systemVersion": "0.0.1",
|
"systemVersion": "1.1.2",
|
||||||
"createdTime": 1754129153649,
|
"createdTime": 1754129153649,
|
||||||
"modifiedTime": 1754129204931,
|
"modifiedTime": 1756510982337,
|
||||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
"lastModifiedBy": "mdk78Q6pOyHh6aBg"
|
||||||
},
|
},
|
||||||
"_key": "!actors.items!6hbqmxDXFOzZJDk4.BQsVuuwFYByKwesR"
|
"_key": "!actors.items!6hbqmxDXFOzZJDk4.BQsVuuwFYByKwesR"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -110,19 +110,20 @@
|
||||||
"source": "Daggerheart SRD",
|
"source": "Daggerheart SRD",
|
||||||
"page": 95,
|
"page": 95,
|
||||||
"artist": ""
|
"artist": ""
|
||||||
}
|
},
|
||||||
|
"motivesAndTactics": "Hide in plain sight, preserve the forest, root down, swing branches"
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"_stats": {
|
"_stats": {
|
||||||
"compendiumSource": null,
|
"compendiumSource": null,
|
||||||
"duplicateSource": null,
|
"duplicateSource": null,
|
||||||
"exportSource": null,
|
"exportSource": null,
|
||||||
"coreVersion": "13.347",
|
"coreVersion": "13.348",
|
||||||
"systemId": "daggerheart",
|
"systemId": "daggerheart",
|
||||||
"systemVersion": "1.0.5",
|
"systemVersion": "1.1.2",
|
||||||
"createdTime": 1753922784314,
|
"createdTime": 1753922784314,
|
||||||
"modifiedTime": 1755385515496,
|
"modifiedTime": 1757057641714,
|
||||||
"lastModifiedBy": "VZIeX2YDvX338Zvr"
|
"lastModifiedBy": "mdk78Q6pOyHh6aBg"
|
||||||
},
|
},
|
||||||
"_id": "XK78QUfY8c8Go8Uv",
|
"_id": "XK78QUfY8c8Go8Uv",
|
||||||
"sort": 3400000,
|
"sort": 3400000,
|
||||||
|
|
|
||||||
|
|
@ -431,7 +431,7 @@
|
||||||
"_key": "!actors.items!3aAS2Qm3R6cgaYfE.tQgxiSS48TJ3X1Dl"
|
"_key": "!actors.items!3aAS2Qm3R6cgaYfE.tQgxiSS48TJ3X1Dl"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Avalance Roar",
|
"name": "Avalanche Roar",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p><strong>Spend a Fear</strong> to roar while within a cave and cause a cave-in. All targets within Close range must succeed on an Agility Reaction Roll (14) or take <strong>2d10</strong> physical damage. The rubble can be cleared with a Progress Countdown (8).</p><p>@Template[type:emanation|range:c]</p>",
|
"description": "<p><strong>Spend a Fear</strong> to roar while within a cave and cause a cave-in. All targets within Close range must succeed on an Agility Reaction Roll (14) or take <strong>2d10</strong> physical damage. The rubble can be cleared with a Progress Countdown (8).</p><p>@Template[type:emanation|range:c]</p>",
|
||||||
|
|
@ -535,12 +535,12 @@
|
||||||
"compendiumSource": null,
|
"compendiumSource": null,
|
||||||
"duplicateSource": null,
|
"duplicateSource": null,
|
||||||
"exportSource": null,
|
"exportSource": null,
|
||||||
"coreVersion": "13.346",
|
"coreVersion": "13.347",
|
||||||
"systemId": "daggerheart",
|
"systemId": "daggerheart",
|
||||||
"systemVersion": "0.0.1",
|
"systemVersion": "0.0.1",
|
||||||
"createdTime": 1754085059319,
|
"createdTime": 1754085059319,
|
||||||
"modifiedTime": 1754143365810,
|
"modifiedTime": 1756256613353,
|
||||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
"lastModifiedBy": "CEZZA7TXd7uT8O2c"
|
||||||
},
|
},
|
||||||
"_key": "!actors.items!3aAS2Qm3R6cgaYfE.9Z0i0uURfBMVIapJ"
|
"_key": "!actors.items!3aAS2Qm3R6cgaYfE.9Z0i0uURfBMVIapJ"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -229,14 +229,14 @@
|
||||||
"_id": "WpOh5kHHx7lcTvEY",
|
"_id": "WpOh5kHHx7lcTvEY",
|
||||||
"img": "icons/magic/acid/dissolve-drip-droplet-smoke.webp",
|
"img": "icons/magic/acid/dissolve-drip-droplet-smoke.webp",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p>When the Ooze makes a successful attack, the target must mark an Armor Slot without receiving its benefi ts (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP.</p>",
|
"description": "<p>When the Ooze makes a successful attack, the target must mark an Armor Slot without receiving its benefits (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {
|
"actions": {
|
||||||
"HfK0u0c7NRppuF1Q": {
|
"HfK0u0c7NRppuF1Q": {
|
||||||
"type": "damage",
|
"type": "damage",
|
||||||
"_id": "HfK0u0c7NRppuF1Q",
|
"_id": "HfK0u0c7NRppuF1Q",
|
||||||
"systemPath": "actions",
|
"systemPath": "actions",
|
||||||
"description": "<p>When the Ooze makes a successful attack, the target must mark an Armor Slot without receiving its benefi ts (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP.</p>",
|
"description": "<p>When the Ooze makes a successful attack, the target must mark an Armor Slot without receiving its benefits (they can still use armor to reduce the damage). If they can’t mark an Armor Slot, they must mark an additional HP.</p>",
|
||||||
"chatDisplay": true,
|
"chatDisplay": true,
|
||||||
"actionType": "action",
|
"actionType": "action",
|
||||||
"cost": [],
|
"cost": [],
|
||||||
|
|
@ -301,12 +301,12 @@
|
||||||
"compendiumSource": null,
|
"compendiumSource": null,
|
||||||
"duplicateSource": null,
|
"duplicateSource": null,
|
||||||
"exportSource": null,
|
"exportSource": null,
|
||||||
"coreVersion": "13.346",
|
"coreVersion": "13.348",
|
||||||
"systemId": "daggerheart",
|
"systemId": "daggerheart",
|
||||||
"systemVersion": "0.0.1",
|
"systemVersion": "1.1.2",
|
||||||
"createdTime": 1754055148507,
|
"createdTime": 1754055148507,
|
||||||
"modifiedTime": 1754145130460,
|
"modifiedTime": 1756510967769,
|
||||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
"lastModifiedBy": "mdk78Q6pOyHh6aBg"
|
||||||
},
|
},
|
||||||
"_key": "!actors.items!aLkLFuVoKz2NLoBK.WpOh5kHHx7lcTvEY"
|
"_key": "!actors.items!aLkLFuVoKz2NLoBK.WpOh5kHHx7lcTvEY"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -552,7 +552,7 @@
|
||||||
"_key": "!actors.items!UGPiPLJsPvMTSKEF.QV2ytK4b1VWF71OS"
|
"_key": "!actors.items!UGPiPLJsPvMTSKEF.QV2ytK4b1VWF71OS"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Avalance",
|
"name": "Avalanche",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p><strong>Spend a Fear</strong> to have the Dragon unleash a huge downfall of snow and ice, covering all other creatures within Far range. All targets within this area must succeed on an Instinct Reaction Roll or be buried in snow and rocks, becoming <em>Vulnerable</em> until they dig themselves out from the debris. For each PC that fails the reaction roll, you gain a Fear.</p><p>@Template[type:emanation|range:f]</p>",
|
"description": "<p><strong>Spend a Fear</strong> to have the Dragon unleash a huge downfall of snow and ice, covering all other creatures within Far range. All targets within this area must succeed on an Instinct Reaction Roll or be buried in snow and rocks, becoming <em>Vulnerable</em> until they dig themselves out from the debris. For each PC that fails the reaction roll, you gain a Fear.</p><p>@Template[type:emanation|range:f]</p>",
|
||||||
|
|
@ -681,12 +681,12 @@
|
||||||
"compendiumSource": null,
|
"compendiumSource": null,
|
||||||
"duplicateSource": null,
|
"duplicateSource": null,
|
||||||
"exportSource": null,
|
"exportSource": null,
|
||||||
"coreVersion": "13.346",
|
"coreVersion": "13.347",
|
||||||
"systemId": "daggerheart",
|
"systemId": "daggerheart",
|
||||||
"systemVersion": "0.0.1",
|
"systemVersion": "0.0.1",
|
||||||
"createdTime": 1754131703390,
|
"createdTime": 1754131703390,
|
||||||
"modifiedTime": 1754131790034,
|
"modifiedTime": 1756256581072,
|
||||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
"lastModifiedBy": "CEZZA7TXd7uT8O2c"
|
||||||
},
|
},
|
||||||
"_key": "!actors.items!UGPiPLJsPvMTSKEF.CcRTxCDCJskiu3fI"
|
"_key": "!actors.items!UGPiPLJsPvMTSKEF.CcRTxCDCJskiu3fI"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,7 @@
|
||||||
"_key": "!actors.items!acMu9wJrMZZzLSTJ.cIAMenvMXHPTpOFn"
|
"_key": "!actors.items!acMu9wJrMZZzLSTJ.cIAMenvMXHPTpOFn"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Avalance",
|
"name": "Avalanche",
|
||||||
"type": "feature",
|
"type": "feature",
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p>Spend a Fear to carve the mountain with an icy torrent, causing an avalanche. All PCs in its path must succeed on an Agility or Strength Reaction Roll or be bowled over and carried down the mountain. A PC using rope, pitons, or other climbing gear gains advantage on this roll. Targets who fail are knocked down the mountain to Far range, take <strong>2d20</strong> physical damage, and must mark a Stress. Targets who succeed must mark a Stress. </p><section id=\"secret-3gM8fEJj1vD9W88k\" class=\"secret\"><p><em>How do the PCs try to weather the avalanche? What approach do the characters take to fi nd one another when their companions go hurtling down the mountainside?</em></p></section>",
|
"description": "<p>Spend a Fear to carve the mountain with an icy torrent, causing an avalanche. All PCs in its path must succeed on an Agility or Strength Reaction Roll or be bowled over and carried down the mountain. A PC using rope, pitons, or other climbing gear gains advantage on this roll. Targets who fail are knocked down the mountain to Far range, take <strong>2d20</strong> physical damage, and must mark a Stress. Targets who succeed must mark a Stress. </p><section id=\"secret-3gM8fEJj1vD9W88k\" class=\"secret\"><p><em>How do the PCs try to weather the avalanche? What approach do the characters take to fi nd one another when their companions go hurtling down the mountainside?</em></p></section>",
|
||||||
|
|
@ -257,12 +257,12 @@
|
||||||
"compendiumSource": null,
|
"compendiumSource": null,
|
||||||
"duplicateSource": null,
|
"duplicateSource": null,
|
||||||
"exportSource": null,
|
"exportSource": null,
|
||||||
"coreVersion": "13.346",
|
"coreVersion": "13.347",
|
||||||
"systemId": "daggerheart",
|
"systemId": "daggerheart",
|
||||||
"systemVersion": "0.0.1",
|
"systemVersion": "0.0.1",
|
||||||
"createdTime": 1754217019442,
|
"createdTime": 1754217019442,
|
||||||
"modifiedTime": 1754217102897,
|
"modifiedTime": 1756256534443,
|
||||||
"lastModifiedBy": "MQSznptE5yLT7kj8"
|
"lastModifiedBy": "CEZZA7TXd7uT8O2c"
|
||||||
},
|
},
|
||||||
"_key": "!actors.items!acMu9wJrMZZzLSTJ.jkm03DXYYajsRk2j"
|
"_key": "!actors.items!acMu9wJrMZZzLSTJ.jkm03DXYYajsRk2j"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -110,13 +110,14 @@
|
||||||
"effects": [
|
"effects": [
|
||||||
{
|
{
|
||||||
"name": "Protective",
|
"name": "Protective",
|
||||||
"description": "Add your character's Tier to your Armor Score",
|
"description": "<p>Add the item's Tier to your Armor Score</p>",
|
||||||
"img": "icons/skills/melee/shield-block-gray-orange.webp",
|
"img": "icons/skills/melee/shield-block-gray-orange.webp",
|
||||||
"changes": [
|
"changes": [
|
||||||
{
|
{
|
||||||
"key": "system.armorScore",
|
"key": "system.armorScore",
|
||||||
"mode": 2,
|
"mode": 2,
|
||||||
"value": "ITEM.@system.tier"
|
"value": "ITEM.@system.tier",
|
||||||
|
"priority": null
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_id": "i5HfkF5aKQuUCTEG",
|
"_id": "i5HfkF5aKQuUCTEG",
|
||||||
|
|
@ -125,7 +126,12 @@
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"startTime": null,
|
||||||
"combat": null
|
"combat": null,
|
||||||
|
"seconds": null,
|
||||||
|
"rounds": null,
|
||||||
|
"turns": null,
|
||||||
|
"startRound": null,
|
||||||
|
"startTurn": null
|
||||||
},
|
},
|
||||||
"origin": null,
|
"origin": null,
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
|
|
@ -137,12 +143,12 @@
|
||||||
"compendiumSource": null,
|
"compendiumSource": null,
|
||||||
"duplicateSource": null,
|
"duplicateSource": null,
|
||||||
"exportSource": null,
|
"exportSource": null,
|
||||||
"coreVersion": "13.346",
|
"coreVersion": "13.348",
|
||||||
"systemId": "daggerheart",
|
"systemId": "daggerheart",
|
||||||
"systemVersion": "0.0.1",
|
"systemVersion": "0.0.1",
|
||||||
"createdTime": 1753794875150,
|
"createdTime": 1753794875150,
|
||||||
"modifiedTime": 1753794875150,
|
"modifiedTime": 1756682958806,
|
||||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
"lastModifiedBy": "mdk78Q6pOyHh6aBg"
|
||||||
},
|
},
|
||||||
"_key": "!items.effects!hiEOGF2reabGLUoi.i5HfkF5aKQuUCTEG"
|
"_key": "!items.effects!hiEOGF2reabGLUoi.i5HfkF5aKQuUCTEG"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -110,13 +110,14 @@
|
||||||
"effects": [
|
"effects": [
|
||||||
{
|
{
|
||||||
"name": "Protective",
|
"name": "Protective",
|
||||||
"description": "Add your character's Tier to your Armor Score",
|
"description": "<p>Add the item's Tier to your Armor Score</p>",
|
||||||
"img": "icons/skills/melee/shield-block-gray-orange.webp",
|
"img": "icons/skills/melee/shield-block-gray-orange.webp",
|
||||||
"changes": [
|
"changes": [
|
||||||
{
|
{
|
||||||
"key": "system.armorScore",
|
"key": "system.armorScore",
|
||||||
"mode": 2,
|
"mode": 2,
|
||||||
"value": "ITEM.@system.tier"
|
"value": "ITEM.@system.tier",
|
||||||
|
"priority": null
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_id": "cXWSV50apzaNQkdA",
|
"_id": "cXWSV50apzaNQkdA",
|
||||||
|
|
@ -125,7 +126,12 @@
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"startTime": null,
|
||||||
"combat": null
|
"combat": null,
|
||||||
|
"seconds": null,
|
||||||
|
"rounds": null,
|
||||||
|
"turns": null,
|
||||||
|
"startRound": null,
|
||||||
|
"startTurn": null
|
||||||
},
|
},
|
||||||
"origin": null,
|
"origin": null,
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
|
|
@ -137,12 +143,12 @@
|
||||||
"compendiumSource": null,
|
"compendiumSource": null,
|
||||||
"duplicateSource": null,
|
"duplicateSource": null,
|
||||||
"exportSource": null,
|
"exportSource": null,
|
||||||
"coreVersion": "13.346",
|
"coreVersion": "13.348",
|
||||||
"systemId": "daggerheart",
|
"systemId": "daggerheart",
|
||||||
"systemVersion": "0.0.1",
|
"systemVersion": "0.0.1",
|
||||||
"createdTime": 1753794098464,
|
"createdTime": 1753794098464,
|
||||||
"modifiedTime": 1753794098464,
|
"modifiedTime": 1756682973559,
|
||||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
"lastModifiedBy": "mdk78Q6pOyHh6aBg"
|
||||||
},
|
},
|
||||||
"_key": "!items.effects!DlinEBGZfIlvreO3.cXWSV50apzaNQkdA"
|
"_key": "!items.effects!DlinEBGZfIlvreO3.cXWSV50apzaNQkdA"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -110,13 +110,14 @@
|
||||||
"effects": [
|
"effects": [
|
||||||
{
|
{
|
||||||
"name": "Protective",
|
"name": "Protective",
|
||||||
"description": "Add your character's Tier to your Armor Score",
|
"description": "<p>Add the item's Tier to your Armor Score</p>",
|
||||||
"img": "icons/skills/melee/shield-block-gray-orange.webp",
|
"img": "icons/skills/melee/shield-block-gray-orange.webp",
|
||||||
"changes": [
|
"changes": [
|
||||||
{
|
{
|
||||||
"key": "system.armorScore",
|
"key": "system.armorScore",
|
||||||
"mode": 2,
|
"mode": 2,
|
||||||
"value": "ITEM.@system.tier"
|
"value": "ITEM.@system.tier",
|
||||||
|
"priority": null
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_id": "Z2p00q5h6x6seXys",
|
"_id": "Z2p00q5h6x6seXys",
|
||||||
|
|
@ -125,7 +126,12 @@
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"startTime": null,
|
||||||
"combat": null
|
"combat": null,
|
||||||
|
"seconds": null,
|
||||||
|
"rounds": null,
|
||||||
|
"turns": null,
|
||||||
|
"startRound": null,
|
||||||
|
"startTurn": null
|
||||||
},
|
},
|
||||||
"origin": null,
|
"origin": null,
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
|
|
@ -137,12 +143,12 @@
|
||||||
"compendiumSource": null,
|
"compendiumSource": null,
|
||||||
"duplicateSource": null,
|
"duplicateSource": null,
|
||||||
"exportSource": null,
|
"exportSource": null,
|
||||||
"coreVersion": "13.346",
|
"coreVersion": "13.348",
|
||||||
"systemId": "daggerheart",
|
"systemId": "daggerheart",
|
||||||
"systemVersion": "0.0.1",
|
"systemVersion": "0.0.1",
|
||||||
"createdTime": 1753796983285,
|
"createdTime": 1753796983285,
|
||||||
"modifiedTime": 1753796983285,
|
"modifiedTime": 1756682777682,
|
||||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
"lastModifiedBy": "mdk78Q6pOyHh6aBg"
|
||||||
},
|
},
|
||||||
"_key": "!items.effects!A28WL9E2lJ3iLZHW.Z2p00q5h6x6seXys"
|
"_key": "!items.effects!A28WL9E2lJ3iLZHW.Z2p00q5h6x6seXys"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -110,13 +110,14 @@
|
||||||
"effects": [
|
"effects": [
|
||||||
{
|
{
|
||||||
"name": "Protective",
|
"name": "Protective",
|
||||||
"description": "Add your character's Tier to your Armor Score",
|
"description": "<p>Add the item's Tier to your Armor Score.</p>",
|
||||||
"img": "icons/skills/melee/shield-block-gray-orange.webp",
|
"img": "icons/skills/melee/shield-block-gray-orange.webp",
|
||||||
"changes": [
|
"changes": [
|
||||||
{
|
{
|
||||||
"key": "system.armorScore",
|
"key": "system.armorScore",
|
||||||
"mode": 2,
|
"mode": 2,
|
||||||
"value": "ITEM.@system.tier"
|
"value": "ITEM.@system.tier",
|
||||||
|
"priority": null
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_id": "M70a81e0Mg66jHRL",
|
"_id": "M70a81e0Mg66jHRL",
|
||||||
|
|
@ -125,7 +126,12 @@
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"duration": {
|
"duration": {
|
||||||
"startTime": null,
|
"startTime": null,
|
||||||
"combat": null
|
"combat": null,
|
||||||
|
"seconds": null,
|
||||||
|
"rounds": null,
|
||||||
|
"turns": null,
|
||||||
|
"startRound": null,
|
||||||
|
"startTurn": null
|
||||||
},
|
},
|
||||||
"origin": null,
|
"origin": null,
|
||||||
"tint": "#ffffff",
|
"tint": "#ffffff",
|
||||||
|
|
@ -137,12 +143,12 @@
|
||||||
"compendiumSource": null,
|
"compendiumSource": null,
|
||||||
"duplicateSource": null,
|
"duplicateSource": null,
|
||||||
"exportSource": null,
|
"exportSource": null,
|
||||||
"coreVersion": "13.346",
|
"coreVersion": "13.348",
|
||||||
"systemId": "daggerheart",
|
"systemId": "daggerheart",
|
||||||
"systemVersion": "0.0.1",
|
"systemVersion": "0.0.1",
|
||||||
"createdTime": 1753794114980,
|
"createdTime": 1753794114980,
|
||||||
"modifiedTime": 1753794114980,
|
"modifiedTime": 1756682994216,
|
||||||
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
|
"lastModifiedBy": "mdk78Q6pOyHh6aBg"
|
||||||
},
|
},
|
||||||
"_key": "!items.effects!mxwWKDujgsRcZWPT.M70a81e0Mg66jHRL"
|
"_key": "!items.effects!mxwWKDujgsRcZWPT.M70a81e0Mg66jHRL"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -210,7 +210,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
margin: 0;
|
&:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
|
|
@ -603,7 +609,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: .25rem .5rem;
|
gap: 0.25rem 0.5rem;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
||||||
label {
|
label {
|
||||||
|
|
@ -620,7 +626,7 @@
|
||||||
&.setting-two-values {
|
&.setting-two-values {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(3, 1fr);
|
grid-template-columns: repeat(3, 1fr);
|
||||||
gap: .25rem .5rem;
|
gap: 0.25rem 0.5rem;
|
||||||
|
|
||||||
.form-group {
|
.form-group {
|
||||||
justify-content: end;
|
justify-content: end;
|
||||||
|
|
|
||||||
|
|
@ -29,11 +29,11 @@
|
||||||
overflow: hidden !important;
|
overflow: hidden !important;
|
||||||
|
|
||||||
div {
|
div {
|
||||||
opacity: .5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
font-family: "Font Awesome 6 Pro";
|
font-family: 'Font Awesome 6 Pro';
|
||||||
content: '\f110';
|
content: '\f110';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
@ -46,6 +46,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes spinner {
|
@keyframes spinner {
|
||||||
to { transform: rotate(360deg); }
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -15,7 +15,7 @@ body.game:is(.performance-low, .noblur) {
|
||||||
.themed.theme-dark.application.daggerheart.sheet.dh-style,
|
.themed.theme-dark.application.daggerheart.sheet.dh-style,
|
||||||
&.theme-dark .application.daggerheart {
|
&.theme-dark .application.daggerheart {
|
||||||
background: @dark-blue;
|
background: @dark-blue;
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.application.sheet.dh-style {
|
.application.sheet.dh-style {
|
||||||
|
|
|
||||||
13
styles/less/ui/chat/refresh-message.less
Normal file
13
styles/less/ui/chat/refresh-message.less
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
.daggerheart.chat.refresh-message {
|
||||||
|
header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
font-size: 18;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
@import './chat/action.less';
|
@import './chat/action.less';
|
||||||
@import './chat/chat.less';
|
@import './chat/chat.less';
|
||||||
@import './chat/downtime.less';
|
@import './chat/downtime.less';
|
||||||
|
@import './chat/refresh-message.less';
|
||||||
@import './chat/sheet.less';
|
@import './chat/sheet.less';
|
||||||
|
|
||||||
@import './combat-sidebar/combat-sidebar.less';
|
@import './combat-sidebar/combat-sidebar.less';
|
||||||
|
|
@ -19,6 +20,8 @@
|
||||||
@import './resources/resources.less';
|
@import './resources/resources.less';
|
||||||
|
|
||||||
@import './settings/settings.less';
|
@import './settings/settings.less';
|
||||||
|
|
||||||
@import './settings/homebrew-settings/domains.less';
|
@import './settings/homebrew-settings/domains.less';
|
||||||
@import './settings/homebrew-settings/types.less';
|
@import './settings/homebrew-settings/types.less';
|
||||||
|
|
||||||
|
@import './sidebar/tabs.less';
|
||||||
|
@import './sidebar/daggerheartMenu.less';
|
||||||
|
|
|
||||||
|
|
@ -409,7 +409,8 @@
|
||||||
|
|
||||||
&.lite,
|
&.lite,
|
||||||
&.no-folder {
|
&.no-folder {
|
||||||
.compendium-sidebar, .menu-path {
|
.compendium-sidebar,
|
||||||
|
.menu-path {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
fieldset {
|
fieldset {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: .5rem;
|
gap: 0.5rem;
|
||||||
|
|
||||||
&.two-columns {
|
&.two-columns {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|
|
||||||
38
styles/less/ui/sidebar/daggerheartMenu.less
Normal file
38
styles/less/ui/sidebar/daggerheartMenu.less
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
.tab.sidebar-tab.daggerheartMenu-sidebar {
|
||||||
|
padding: 0 4px;
|
||||||
|
|
||||||
|
.menu-refresh-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.menu-refresh-inner-container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.experience-chip {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 5px;
|
||||||
|
width: fit-content;
|
||||||
|
gap: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 5px;
|
||||||
|
background: light-dark(@dark-blue-10, @golden-10);
|
||||||
|
color: light-dark(@dark-blue, @golden);
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
background: light-dark(@dark-blue-40, @golden-40);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
8
styles/less/ui/sidebar/tabs.less
Normal file
8
styles/less/ui/sidebar/tabs.less
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#interface #ui-right #sidebar {
|
||||||
|
menu li button {
|
||||||
|
img {
|
||||||
|
width: 22px;
|
||||||
|
max-width: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<fieldset class="one-column" data-key="cost">
|
<fieldset class="one-column" data-key="cost">
|
||||||
<legend>
|
<legend>
|
||||||
Cost
|
{{localize "DAGGERHEART.GENERAL.Cost.single"}}
|
||||||
<a><i class="fa-solid fa-plus icon-button" data-action="addElement"></i></a>
|
<a><i class="fa-solid fa-plus icon-button" data-action="addElement"></i></a>
|
||||||
</legend>
|
</legend>
|
||||||
{{#each source as |cost index|}}
|
{{#each source as |cost index|}}
|
||||||
|
|
@ -8,10 +8,10 @@
|
||||||
{{formField ../fields.consumeOnSuccess value=cost.consumeOnSuccess name=(concat "cost." index ".consumeOnSuccess") classes="checkbox" rootId=partId localize=true}}
|
{{formField ../fields.consumeOnSuccess value=cost.consumeOnSuccess name=(concat "cost." index ".consumeOnSuccess") classes="checkbox" rootId=partId localize=true}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<div class="nest-inputs">
|
<div class="nest-inputs">
|
||||||
{{formField ../fields.scalable label="Scalable" value=cost.scalable name=(concat "cost." index ".scalable") classes="checkbox"}}
|
{{formField ../fields.scalable label="DAGGERHEART.GENERAL.scalable" value=cost.scalable name=(concat "cost." index ".scalable") classes="checkbox" localize=true}}
|
||||||
{{formField ../fields.key choices=(@root.disableOption index @root.costOptions ../source) label="Resource" value=cost.key name=(concat "cost." index ".key") localize=true blank=false}}
|
{{formField ../fields.key choices=(@root.disableOption index @root.costOptions ../source) label="DAGGERHEART.GENERAL.resource" value=cost.key name=(concat "cost." index ".key") localize=true blank=false}}
|
||||||
{{formField ../fields.value label="Amount" value=cost.value name=(concat "cost." index ".value")}}
|
{{formField ../fields.value label="DAGGERHEART.GENERAL.amount" value=cost.value name=(concat "cost." index ".value") localize=true}}
|
||||||
{{formField ../fields.step label="Step" value=cost.step name=(concat "cost." index ".step") disabled=(not cost.scalable)}}
|
{{formField ../fields.step label="DAGGERHEART.GENERAL.step" value=cost.step name=(concat "cost." index ".step") disabled=(not cost.scalable) localize=true}}
|
||||||
<a class="btn" data-tooltip="{{localize "CONTROLS.CommonDelete"}}" data-action="removeElement" data-index="{{index}}"><i class="fas fa-trash"></i></a>
|
<a class="btn" data-tooltip="{{localize "CONTROLS.CommonDelete"}}" data-action="removeElement" data-index="{{index}}"><i class="fas fa-trash"></i></a>
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
<fieldset class="one-column">
|
<fieldset class="one-column">
|
||||||
<legend>{{localize "DAGGERHEART.GENERAL.range"}}{{#if fields.target}} & {{localize "DAGGERHEART.GENERAL.Target.single"}}{{/if}}</legend>
|
<legend>{{localize "DAGGERHEART.GENERAL.range"}}{{#if fields.target}} & {{localize "DAGGERHEART.GENERAL.Target.single"}}{{/if}}</legend>
|
||||||
{{formField fields.range value=source.range label="Range" name=(concat path "range") localize=true}}
|
{{formField fields.range value=source.range label="DAGGERHEART.GENERAL.range" name=(concat path "range") localize=true}}
|
||||||
{{#if fields.target}}
|
{{#if fields.target}}
|
||||||
<div class="nest-inputs">
|
<div class="nest-inputs">
|
||||||
{{#if (and source.target.type (not (eq source.target.type 'self')))}}
|
{{#if (and source.target.type (not (eq source.target.type 'self')))}}
|
||||||
{{ formField fields.target.amount value=source.target.amount label="Amount" name=(concat path "target.amount") }}
|
{{ formField fields.target.amount value=source.target.amount label="DAGGERHEART.GENERAL.amount" name=(concat path "target.amount") localize=true}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{ formField fields.target.type value=source.target.type label="Target" name=(concat path "target.type") localize=true }}
|
{{ formField fields.target.type value=source.target.type label="DAGGERHEART.GENERAL.Target.single" name=(concat path "target.type") localize=true }}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
@ -4,8 +4,8 @@
|
||||||
{{formField fields.consumeOnSuccess value=source.consumeOnSuccess name="uses.consumeOnSuccess" classes="checkbox" rootId=partId localize=true}}
|
{{formField fields.consumeOnSuccess value=source.consumeOnSuccess name="uses.consumeOnSuccess" classes="checkbox" rootId=partId localize=true}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<div class="nest-inputs">
|
<div class="nest-inputs">
|
||||||
{{formField fields.value label="Spent" value=source.value name="uses.value" rootId=partId}}
|
{{formField fields.value label="DAGGERHEART.GENERAL.spent" value=source.value name="uses.value" rootId=partId localize=true}}
|
||||||
{{formField fields.max label="Max" value=source.max name="uses.max" rootId=partId}}
|
{{formField fields.max label="DAGGERHEART.GENERAL.max" value=source.max name="uses.max" rootId=partId localize=true}}
|
||||||
</div>
|
</div>
|
||||||
{{formField fields.recovery label="Recovery" value=source.recovery name="uses.recovery" rootId=partId localize=true}}
|
{{formField fields.recovery label="DAGGERHEART.GENERAL.recovery" value=source.recovery name="uses.recovery" rootId=partId localize=true}}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
@ -146,7 +146,7 @@
|
||||||
<select class="roll-mode-select" name="selectedRollMode">
|
<select class="roll-mode-select" name="selectedRollMode">
|
||||||
{{selectOptions rollModes selected=selectedRollMode valueAttr="action" labelAttr="label" localize=true}}
|
{{selectOptions rollModes selected=selectedRollMode valueAttr="action" labelAttr="label" localize=true}}
|
||||||
</select>
|
</select>
|
||||||
<button class="sunmit-btn" data-action="submitRoll"{{#unless canRoll}} disabled{{/unless}}>
|
<button class="submit-btn" data-action="submitRoll"{{#unless canRoll}} disabled{{/unless}}>
|
||||||
<i class="fa-solid fa-dice"></i>
|
<i class="fa-solid fa-dice"></i>
|
||||||
<span class="label">
|
<span class="label">
|
||||||
{{#if @root.rollConfig.roll.difficulty}}
|
{{#if @root.rollConfig.roll.difficulty}}
|
||||||
|
|
@ -162,7 +162,7 @@
|
||||||
{{> 'systems/daggerheart/templates/dialogs/dice-roll/costSelection.hbs'}}
|
{{> 'systems/daggerheart/templates/dialogs/dice-roll/costSelection.hbs'}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<div class="roll-dialog-controls">
|
<div class="roll-dialog-controls">
|
||||||
<button class="sunmit-btn" data-action="submitRoll"{{#unless canRoll}} disabled{{/unless}}>
|
<button class="submit-btn" data-action="submitRoll"{{#unless canRoll}} disabled{{/unless}}>
|
||||||
<span class="label">{{localize "DAGGERHEART.GENERAL.continue"}}</span>
|
<span class="label">{{localize "DAGGERHEART.GENERAL.continue"}}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
15
templates/settings/downtime-config/effects.hbs
Normal file
15
templates/settings/downtime-config/effects.hbs
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
<section
|
||||||
|
class='tab {{tabs.effects.cssClass}} {{tabs.effects.id}}'
|
||||||
|
data-tab='{{tabs.effects.id}}'
|
||||||
|
data-group='{{tabs.effects.group}}'
|
||||||
|
>
|
||||||
|
<fieldset class="one-column">
|
||||||
|
<legend>{{localize "DAGGERHEART.GENERAL.Effect.plural"}} <a><i class="fa-solid fa-plus icon-button" data-action="addEffect"></i></a></legend>
|
||||||
|
|
||||||
|
<div class="settings-items">
|
||||||
|
{{#each move.effects}}
|
||||||
|
{{> "systems/daggerheart/templates/settings/components/settings-item-line.hbs" id=this.id type="effect" }}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</section>
|
||||||
|
|
@ -3,11 +3,13 @@
|
||||||
data-tab='{{tabs.main.id}}'
|
data-tab='{{tabs.main.id}}'
|
||||||
data-group='{{tabs.main.group}}'
|
data-group='{{tabs.main.group}}'
|
||||||
>
|
>
|
||||||
<fieldset class="one-column">
|
{{#if hasIcon}}
|
||||||
<legend>{{localize "Icon"}}</legend>
|
<fieldset class="one-column">
|
||||||
|
<legend>{{localize "Icon"}}</legend>
|
||||||
|
|
||||||
<input type="text" name="icon" value="{{move.icon}}" />
|
<input type="text" name="icon" value="{{move.icon}}" />
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
<fieldset class="one-column">
|
<fieldset class="one-column">
|
||||||
<legend>{{localize "Description"}}</legend>
|
<legend>{{localize "Description"}}</legend>
|
||||||
|
|
|
||||||
35
templates/settings/homebrew-settings/itemFeatures.hbs
Normal file
35
templates/settings/homebrew-settings/itemFeatures.hbs
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
<section
|
||||||
|
class="tab {{tabs.itemFeatures.cssClass}} {{tabs.itemFeatures.id}}"
|
||||||
|
data-tab="{{tabs.itemFeatures.id}}"
|
||||||
|
data-group="{{tabs.itemFeatures.group}}"
|
||||||
|
>
|
||||||
|
<div class="two-columns even">
|
||||||
|
<fieldset class="start-align">
|
||||||
|
<legend>
|
||||||
|
{{localize "DAGGERHEART.GENERAL.weaponFeatures"}}
|
||||||
|
<a data-action="addItem" data-type="weaponFeatures"><i class="fa-solid fa-plus"></i></a>
|
||||||
|
<a data-action="resetMoves" data-type="weaponFeatures"><i class="fa-solid fa-arrow-rotate-left"></i></a>
|
||||||
|
</legend>
|
||||||
|
|
||||||
|
<div class="settings-items">
|
||||||
|
{{#each settingFields._source.itemFeatures.weaponFeatures as |feature id|}}
|
||||||
|
{{> "systems/daggerheart/templates/settings/components/settings-item-line.hbs" this type="weaponFeatures" id=id }}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset class="start-align">
|
||||||
|
<legend>
|
||||||
|
{{localize "DAGGERHEART.GENERAL.armorFeatures"}}
|
||||||
|
<a data-action="addItem" data-type="armorFeatures"><i class="fa-solid fa-plus"></i></a>
|
||||||
|
<a data-action="resetMoves" data-type="armorFeatures"><i class="fa-solid fa-arrow-rotate-left"></i></a>
|
||||||
|
</legend>
|
||||||
|
|
||||||
|
<div class="settings-items">
|
||||||
|
{{#each settingFields._source.itemFeatures.armorFeatures as |feature id|}}
|
||||||
|
{{> "systems/daggerheart/templates/settings/components/settings-item-line.hbs" this type="armorFeatures" id=id }}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
@ -8,24 +8,24 @@
|
||||||
</header>
|
</header>
|
||||||
<ol class="scrollable" data-changes>
|
<ol class="scrollable" data-changes>
|
||||||
{{#each source.changes as |change i|}}
|
{{#each source.changes as |change i|}}
|
||||||
{{#with ../fields.changes.element.fields as |changeFields|}}
|
{{#with ../fields.changes.element.fields as |changeFields|}}
|
||||||
<li data-index="{{i}}">
|
<li data-index="{{i}}">
|
||||||
<div class="key">
|
<div class="key">
|
||||||
<input type="text" class="effect-change-input" name="{{concat "changes." i ".key"}}" value="{{change.key}}" />
|
<input type="text" class="effect-change-input" name="{{concat "changes." i ".key"}}" value="{{change.key}}" />
|
||||||
</div>
|
</div>
|
||||||
<div class="mode">
|
<div class="mode">
|
||||||
{{formInput changeFields.mode name=(concat "changes." i ".mode") value=change.mode choices=@root.modes}}
|
{{formInput changeFields.mode name=(concat "changes." i ".mode") value=change.mode choices=@root.modes}}
|
||||||
</div>
|
</div>
|
||||||
<div class="value">
|
<div class="value">
|
||||||
{{formInput changeFields.value name=(concat "changes." i ".value") value=change.value}}
|
{{formInput changeFields.value name=(concat "changes." i ".value") value=change.value}}
|
||||||
</div>
|
</div>
|
||||||
<div class="priority">
|
<div class="priority">
|
||||||
{{formInput changeFields.priority name=(concat "changes." i ".priority") value=change.priority
|
{{formInput changeFields.priority name=(concat "changes." i ".priority") value=change.priority
|
||||||
placeholder=(lookup ../../priorities change.mode)}}
|
placeholder=(lookup ../../priorities change.mode)}}
|
||||||
</div>
|
</div>
|
||||||
<div class="controls"><a data-action="deleteChange"><i class="fa-solid fa-trash"></i></a></div>
|
<div class="controls"><a data-action="deleteChange"><i class="fa-solid fa-trash"></i></a></div>
|
||||||
</li>
|
</li>
|
||||||
{{/with}}
|
{{/with}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ol>
|
</ol>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -2,10 +2,10 @@
|
||||||
<fieldset class="one-column">
|
<fieldset class="one-column">
|
||||||
<legend>{{localize "DAGGERHEART.ACTIVEEFFECT.Config.rangeDependence.title"}}</legend>
|
<legend>{{localize "DAGGERHEART.ACTIVEEFFECT.Config.rangeDependence.title"}}</legend>
|
||||||
|
|
||||||
{{formGroup document.system.schema.fields.rangeDependence.fields.enabled value=source.system.rangeDependence.enabled localize=true }}
|
{{formGroup systemFields.rangeDependence.fields.enabled value=source.system.rangeDependence.enabled localize=true }}
|
||||||
{{formGroup document.system.schema.fields.rangeDependence.fields.type value=source.system.rangeDependence.type localize=true }}
|
{{formGroup systemFields.rangeDependence.fields.type value=source.system.rangeDependence.type localize=true }}
|
||||||
{{formGroup document.system.schema.fields.rangeDependence.fields.target value=source.system.rangeDependence.target localize=true }}
|
{{formGroup systemFields.rangeDependence.fields.target value=source.system.rangeDependence.target localize=true }}
|
||||||
{{formGroup document.system.schema.fields.rangeDependence.fields.range value=source.system.rangeDependence.range localize=true }}
|
{{formGroup systemFields.rangeDependence.fields.range value=source.system.rangeDependence.range localize=true }}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<fieldset class="one-column">
|
<fieldset class="one-column">
|
||||||
|
|
|
||||||
22
templates/sidebar/daggerheart-menu/main.hbs
Normal file
22
templates/sidebar/daggerheart-menu/main.hbs
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
<div>
|
||||||
|
<fieldset>
|
||||||
|
<legend>{{localize "Refresh Features"}}</legend>
|
||||||
|
|
||||||
|
<div class="menu-refresh-container">
|
||||||
|
<div class="menu-refresh-inner-container">
|
||||||
|
{{#each refreshables as |type key|}}
|
||||||
|
<div class="experience-chip {{#if type.selected}}selected{{/if}}" data-action="selectRefreshable" data-type="{{key}}">
|
||||||
|
{{#if type.selected}}
|
||||||
|
<span><i class="fa-solid fa-circle"></i></span>
|
||||||
|
{{else}}
|
||||||
|
<span><i class="fa-regular fa-circle"></i></span>
|
||||||
|
{{/if}}
|
||||||
|
<span>{{type.label}}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
<button data-action="refreshActors" {{disabled disableRefresh}}>{{localize "Refresh"}}</button>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
18
templates/sidebar/tabs.hbs
Normal file
18
templates/sidebar/tabs.hbs
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
<nav class="tabs faded-ui" role="tablist" data-tooltip-direction="LEFT">
|
||||||
|
<menu class="flexcol">
|
||||||
|
{{#each tabs}}
|
||||||
|
<li>
|
||||||
|
<button type="button" class="ui-control plain icon {{#if icon}}{{icon}}{{/if}}" data-action="tab" data-tab="{{ @key }}"
|
||||||
|
role="tab" aria-pressed="{{ active }}" data-group="primary" aria-label="{{ localize tooltip }}"
|
||||||
|
aria-controls="{{ @key }}" data-tooltip>
|
||||||
|
{{#if img}}<img src="{{img}}" />{{/if}}
|
||||||
|
</button>
|
||||||
|
<div class="notification-pip"></div>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
<li>
|
||||||
|
<button type="button" class="collapse ui-control plain icon fas fa-caret-left" data-tooltip
|
||||||
|
aria-label="{{ localize "Expand" }}" data-action="toggleState"></button>
|
||||||
|
</li>
|
||||||
|
</menu>
|
||||||
|
</nav>
|
||||||
6
templates/ui/chat/refreshMessage.hbs
Normal file
6
templates/ui/chat/refreshMessage.hbs
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<div class="daggerheart chat refresh-message">
|
||||||
|
<header>
|
||||||
|
<div class="subtitle">{{localize "DAGGERHEART.UI.Chat.refreshMessage.header"}}</div>
|
||||||
|
<div class="types">{{types}}</div>
|
||||||
|
</header>
|
||||||
|
</div>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue