mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-15 13:11:08 +01:00
Merge branch 'development' into feature-fix/template-enricher-functionality
This commit is contained in:
commit
121f6839a9
71 changed files with 1290 additions and 427 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();
|
||||||
|
|
||||||
registerCountdownHooks();
|
registerCountdownHooks();
|
||||||
|
|
@ -309,5 +313,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));
|
||||||
|
|
|
||||||
17
lang/en.json
17
lang/en.json
|
|
@ -1917,6 +1917,7 @@
|
||||||
"roll": "Roll",
|
"roll": "Roll",
|
||||||
"rules": "Rules",
|
"rules": "Rules",
|
||||||
"types": "Types",
|
"types": "Types",
|
||||||
|
"itemFeatures": "Item Features",
|
||||||
"questions": "Questions"
|
"questions": "Questions"
|
||||||
},
|
},
|
||||||
"Tiers": {
|
"Tiers": {
|
||||||
|
|
@ -1934,6 +1935,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",
|
||||||
|
|
@ -2046,6 +2048,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}"
|
||||||
},
|
},
|
||||||
|
|
@ -2276,7 +2279,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?",
|
||||||
|
|
@ -2420,6 +2425,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?"
|
||||||
|
|
@ -2534,8 +2543,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';
|
||||||
|
|
|
||||||
|
|
@ -536,7 +536,7 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
||||||
static async viewCompendium(event, target) {
|
static async viewCompendium(event, target) {
|
||||||
const type = target.dataset.compendium ?? target.dataset.type;
|
const type = target.dataset.compendium ?? target.dataset.type;
|
||||||
|
|
||||||
const presets = {
|
const presets = {
|
||||||
folder: type,
|
folder: type,
|
||||||
render: {
|
render: {
|
||||||
noFolder: true
|
noFolder: true
|
||||||
|
|
|
||||||
|
|
@ -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';
|
||||||
|
|
|
||||||
|
|
@ -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 });
|
||||||
});
|
});
|
||||||
|
|
@ -416,11 +416,14 @@ 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({
|
||||||
name: 'DAGGERHEART.GENERAL.damage',
|
name: 'DAGGERHEART.GENERAL.damage',
|
||||||
icon: 'fa-solid fa-explosion',
|
icon: 'fa-solid fa-explosion',
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,12 +84,10 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
async _preRender(context, options) {
|
async _preRender(context, options) {
|
||||||
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,15 +98,18 @@ 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();
|
||||||
|
|
||||||
this.element.classList.toggle('lite', this.presets?.render?.lite === true);
|
this.element.classList.toggle('lite', this.presets?.render?.lite === true);
|
||||||
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) {
|
||||||
|
|
@ -194,53 +194,54 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
let loadTimeout = this.toggleLoader(true);
|
let loadTimeout = this.toggleLoader(true);
|
||||||
|
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
|
||||||
game.packs.forEach(pack => {
|
game.packs.forEach(pack => {
|
||||||
promises.push(
|
promises.push(
|
||||||
new Promise(async resolve => {
|
new Promise(async resolve => {
|
||||||
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,
|
||||||
formatChoices: this.formatChoices
|
formatChoices: this.formatChoices
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
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,
|
||||||
formatLabel: this.formatLabel
|
formatLabel: this.formatLabel
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
this.element.querySelector('.item-list').innerHTML = itemList;
|
this.element.querySelector('.item-list').innerHTML = itemList;
|
||||||
|
|
||||||
this._createFilterInputs();
|
this._createFilterInputs();
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -399,7 +400,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
item = this.items.find(i => i.uuid === itemUUID);
|
item = this.items.find(i => i.uuid === itemUUID);
|
||||||
|
|
||||||
if (!item) continue;
|
if (!item) continue;
|
||||||
|
|
||||||
const matchesMenu =
|
const matchesMenu =
|
||||||
this.fieldFilter.length === 0 ||
|
this.fieldFilter.length === 0 ||
|
||||||
this.fieldFilter.every(
|
this.fieldFilter.every(
|
||||||
|
|
@ -503,19 +504,19 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
_canDragStart() {
|
_canDragStart() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
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,20 +524,20 @@ 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]);
|
||||||
});
|
});
|
||||||
|
|
||||||
headerActions.append(button);
|
headerActions.append(button);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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',
|
||||||
|
|
|
||||||
|
|
@ -31,20 +31,20 @@ 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'
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
|
||||||
|
|
@ -34,8 +34,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
|
|
||||||
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;
|
||||||
|
|
@ -43,7 +42,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a Map containing each Action step based on fields define in schema. Ordered by Fields order property.
|
* Create a Map containing each Action step based on fields define in schema. Ordered by Fields order property.
|
||||||
*
|
*
|
||||||
* Each step can be called individually as long as needed config is provided.
|
* Each step can be called individually as long as needed config is provided.
|
||||||
* Ex: <action>.workflow.get("damage").execute(config)
|
* Ex: <action>.workflow.get("damage").execute(config)
|
||||||
* @returns {Map}
|
* @returns {Map}
|
||||||
|
|
@ -53,8 +52,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 +64,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -117,7 +117,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare base data based on Action Type & Parent Type
|
* Prepare base data based on Action Type & Parent Type
|
||||||
* @param {object} parent
|
* @param {object} parent
|
||||||
* @returns {object}
|
* @returns {object}
|
||||||
*/
|
*/
|
||||||
static getSourceConfig(parent) {
|
static getSourceConfig(parent) {
|
||||||
|
|
@ -167,9 +167,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 +185,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;
|
||||||
|
|
||||||
|
|
@ -194,7 +194,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
config = await D20RollDialog.configure(null, config);
|
config = await D20RollDialog.configure(null, config);
|
||||||
if (!config) return;
|
if (!config) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute the Action Worflow in order based of schema fields
|
// Execute the Action Worflow in order based of schema fields
|
||||||
await this.executeWorkflow(config);
|
await this.executeWorkflow(config);
|
||||||
|
|
||||||
|
|
@ -206,7 +206,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
/**
|
/**
|
||||||
* Create the basic config common to every action type
|
* Create the basic config common to every action type
|
||||||
* @param {Event} event Event from the button used to trigger the Action
|
* @param {Event} event Event from the button used to trigger the Action
|
||||||
* @returns {object}
|
* @returns {object}
|
||||||
*/
|
*/
|
||||||
prepareBaseConfig(event) {
|
prepareBaseConfig(event) {
|
||||||
const config = {
|
const config = {
|
||||||
|
|
@ -236,13 +236,12 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
/**
|
/**
|
||||||
* Create the config for that action used for its workflow
|
* Create the config for that action used for its workflow
|
||||||
* @param {Event} event Event from the button used to trigger the Action
|
* @param {Event} event Event from the button used to trigger the Action
|
||||||
* @returns {object}
|
* @returns {object}
|
||||||
*/
|
*/
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
@ -260,11 +259,11 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
* Consume Action configured resources & uses.
|
* Consume Action configured resources & uses.
|
||||||
* That method is only used when we want those resources to be consumed outside of the use method workflow.
|
* That method is only used when we want those resources to be consumed outside of the use method workflow.
|
||||||
* @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.
|
||||||
* @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,13 +290,13 @@ 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() {
|
||||||
return !!this.save?.trait;
|
return !!this.save?.trait;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
@ -49,14 +49,14 @@ export default class BeastformField extends fields.SchemaField {
|
||||||
|
|
||||||
return await BeastformField.transform.call(this, selected, evolved, hybrid);
|
return await BeastformField.transform.call(this, selected, evolved, hybrid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update Action Workflow config object.
|
* Update Action Workflow config object.
|
||||||
* Must be called within Action context.
|
* Must be called within Action context.
|
||||||
* @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;
|
||||||
}
|
}
|
||||||
|
|
@ -73,10 +73,10 @@ export default class BeastformField extends fields.SchemaField {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO by Harry
|
* TODO by Harry
|
||||||
* @param {*} selectedForm
|
* @param {*} selectedForm
|
||||||
* @param {*} evolvedData
|
* @param {*} evolvedData
|
||||||
* @param {*} hybridData
|
* @param {*} hybridData
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
static async transform(selectedForm, evolvedData, hybridData) {
|
static async transform(selectedForm, evolvedData, hybridData) {
|
||||||
const formData = evolvedData?.form ? evolvedData.form.toObject() : selectedForm.toObject();
|
const formData = evolvedData?.form ? evolvedData.form.toObject() : selectedForm.toObject();
|
||||||
|
|
|
||||||
|
|
@ -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,9 +56,10 @@ 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;
|
||||||
damageConfig.directDamage = !!damageConfig.source?.message;
|
damageConfig.directDamage = !!damageConfig.source?.message;
|
||||||
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
@ -70,19 +76,15 @@ export default class DamageField extends fields.SchemaField {
|
||||||
* Apply Damage/Healing Action Worflow part.
|
* Apply Damage/Healing Action Worflow part.
|
||||||
* @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.
|
||||||
* @param {*[]} targets Arrays of targets to bypass pre-selected ones.
|
* @param {*[]} targets Arrays of targets to bypass pre-selected ones.
|
||||||
* @param {boolean} force If the method should be executed outside of Action workflow, for ChatMessage button for example.
|
* @param {boolean} force If the method should be executed outside of Action workflow, for ChatMessage button for example.
|
||||||
*/
|
*/
|
||||||
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;
|
||||||
|
|
@ -97,17 +99,17 @@ export default class DamageField extends fields.SchemaField {
|
||||||
else actor.takeDamage(config.damage, config.isDirect);
|
else actor.takeDamage(config.damage, config.isDirect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return value or valueAlt from damage part
|
* Return value or valueAlt from damage part
|
||||||
* Must be called within Action context or similar.
|
* Must be called within Action context or similar.
|
||||||
* @param {object} part Damage Part
|
* @param {object} part Damage Part
|
||||||
* @param {object} data Action getRollData
|
* @param {object} data Action getRollData
|
||||||
* @returns Formula value object
|
* @returns Formula value object
|
||||||
*/
|
*/
|
||||||
static getFormulaValue(part, data) {
|
static getFormulaValue(part, data) {
|
||||||
let formulaValue = part.value;
|
let formulaValue = part.value;
|
||||||
|
|
||||||
if (data.hasRoll && part.resultBased && data.roll.result.duality === -1) return part.valueAlt;
|
if (data.hasRoll && part.resultBased && data.roll.result.duality === -1) return part.valueAlt;
|
||||||
|
|
||||||
const isAdversary = this.actor.type === 'adversary';
|
const isAdversary = this.actor.type === 'adversary';
|
||||||
|
|
@ -123,8 +125,8 @@ export default class DamageField extends fields.SchemaField {
|
||||||
* Prepare formulas for Damage Roll
|
* Prepare formulas for Damage Roll
|
||||||
* Must be called within Action context or similar.
|
* Must be called within Action context or similar.
|
||||||
* @param {object[]} formulas Array of formatted formulas object
|
* @param {object[]} formulas Array of formatted formulas object
|
||||||
* @param {object} data Action getRollData
|
* @param {object} data Action getRollData
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
static formatFormulas(formulas, data) {
|
static formatFormulas(formulas, data) {
|
||||||
const formattedFormulas = [];
|
const formattedFormulas = [];
|
||||||
|
|
@ -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)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,7 +5,12 @@ 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({
|
||||||
|
|
@ -86,14 +91,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,21 +123,21 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update Action Workflow config object.
|
* Update Action Workflow config object.
|
||||||
* Must be called within Action context.
|
* Must be called within Action context.
|
||||||
* @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;
|
||||||
|
|
||||||
const roll = {
|
const roll = {
|
||||||
baseModifiers: this.roll.getModifier(),
|
baseModifiers: this.roll.getModifier(),
|
||||||
label: 'Attack',
|
label: 'Attack',
|
||||||
|
|
@ -152,6 +157,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;
|
||||||
|
|
||||||
|
|
@ -33,15 +33,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;
|
||||||
|
|
@ -52,35 +52,35 @@ export default class SaveField extends fields.SchemaField {
|
||||||
* Must be called within Action context.
|
* Must be called within Action context.
|
||||||
* @param {object[]} targets Array of formatted targets.
|
* @param {object[]} targets Array of formatted targets.
|
||||||
* @param {Event} event Triggering event
|
* @param {Event} event Triggering event
|
||||||
* @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 +93,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 +109,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -117,31 +118,32 @@ export default class SaveField extends fields.SchemaField {
|
||||||
* Update a Roll ChatMessage for a token according to his Reaction Roll result.
|
* Update a Roll ChatMessage for a token according to his Reaction Roll result.
|
||||||
* @param {object} result Result from the Reaction Roll
|
* @param {object} result Result from the Reaction Roll
|
||||||
* @param {object} message ChatMessage to update
|
* @param {object} message ChatMessage to update
|
||||||
* @param {string} targetId Token ID
|
* @param {string} targetId Token ID
|
||||||
*/
|
*/
|
||||||
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,25 +152,29 @@ 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)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a query to an Actor owner to roll a Reaction Roll then send back the result.
|
* Send a query to an Actor owner to roll a Reaction Roll then send back the result.
|
||||||
* @param {object} param0
|
* @param {object} param0
|
||||||
* @param {string} param0.actionId Action ID
|
* @param {string} param0.actionId Action ID
|
||||||
* @param {string} param0.actorId Actor ID
|
* @param {string} param0.actorId Actor ID
|
||||||
* @param {Event} param0.event Triggering event
|
* @param {Event} param0.event Triggering event
|
||||||
* @param {ChatMessage} param0.message Chat Message to update
|
* @param {ChatMessage} param0.message Chat Message to update
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
static rollSaveQuery({ actionId, actorId, event, message }) {
|
static rollSaveQuery({ actionId, actorId, event, message }) {
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
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,7 +1,6 @@
|
||||||
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 = {
|
||||||
|
|
@ -21,7 +20,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 +61,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)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ export default class UsesField extends fields.SchemaField {
|
||||||
* Action Workflow order
|
* Action Workflow order
|
||||||
*/
|
*/
|
||||||
static order = 160;
|
static order = 160;
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
constructor(options = {}, context = {}) {
|
constructor(options = {}, context = {}) {
|
||||||
const usesFields = {
|
const usesFields = {
|
||||||
|
|
@ -62,7 +62,7 @@ export default class UsesField extends fields.SchemaField {
|
||||||
/**
|
/**
|
||||||
* Prepare Uses object for Action Workflow
|
* Prepare Uses object for Action Workflow
|
||||||
* Must be called within Action context.
|
* Must be called within Action context.
|
||||||
* @param {object} uses
|
* @param {object} uses
|
||||||
* @returns {object}
|
* @returns {object}
|
||||||
*/
|
*/
|
||||||
static calcUses(uses) {
|
static calcUses(uses) {
|
||||||
|
|
@ -77,7 +77,7 @@ export default class UsesField extends fields.SchemaField {
|
||||||
/**
|
/**
|
||||||
* Check if the Action still get atleast one unspent uses.
|
* Check if the Action still get atleast one unspent uses.
|
||||||
* Must be called within Action context.
|
* Must be called within Action context.
|
||||||
* @param {*} uses
|
* @param {*} uses
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
static hasUses(uses) {
|
static hasUses(uses) {
|
||||||
|
|
|
||||||
|
|
@ -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 }
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ export default class DHRoll extends Roll {
|
||||||
static async buildConfigure(config = {}, message = {}) {
|
static async buildConfigure(config = {}, message = {}) {
|
||||||
config.hooks = [...this.getHooks(), ''];
|
config.hooks = [...this.getHooks(), ''];
|
||||||
config.dialog ??= {};
|
config.dialog ??= {};
|
||||||
|
|
||||||
for (const hook of config.hooks) {
|
for (const hook of config.hooks) {
|
||||||
if (Hooks.call(`${CONFIG.DH.id}.preRoll${hook.capitalize()}`, config, message) === false) return null;
|
if (Hooks.call(`${CONFIG.DH.id}.preRoll${hook.capitalize()}`, config, message) === false) return null;
|
||||||
}
|
}
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
@ -91,7 +94,7 @@ export default class DHRoll extends Roll {
|
||||||
system: config,
|
system: config,
|
||||||
rolls: [roll]
|
rolls: [roll]
|
||||||
};
|
};
|
||||||
|
|
||||||
config.selectedRollMode ??= game.settings.get('core', 'rollMode');
|
config.selectedRollMode ??= game.settings.get('core', 'rollMode');
|
||||||
|
|
||||||
if (roll._evaluated) {
|
if (roll._evaluated) {
|
||||||
|
|
|
||||||
|
|
@ -153,10 +153,13 @@ export default class DualityRoll extends D20Roll {
|
||||||
|
|
||||||
applyBaseBonus() {
|
applyBaseBonus() {
|
||||||
const modifiers = super.applyBaseBonus();
|
const modifiers = super.applyBaseBonus();
|
||||||
|
|
||||||
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;
|
||||||
|
|
@ -104,11 +104,11 @@ 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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -125,7 +125,7 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
||||||
html.querySelectorAll('.target-save').forEach(element =>
|
html.querySelectorAll('.target-save').forEach(element =>
|
||||||
element.addEventListener('click', this.onRollSave.bind(this))
|
element.addEventListener('click', this.onRollSave.bind(this))
|
||||||
);
|
);
|
||||||
|
|
||||||
html.querySelectorAll('.roll-all-save-button').forEach(element =>
|
html.querySelectorAll('.roll-all-save-button').forEach(element =>
|
||||||
element.addEventListener('click', this.onRollAllSave.bind(this))
|
element.addEventListener('click', this.onRollAllSave.bind(this))
|
||||||
);
|
);
|
||||||
|
|
@ -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) {
|
||||||
|
|
@ -171,9 +171,9 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
||||||
|
|
||||||
if (targets.length === 0)
|
if (targets.length === 0)
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,10 +76,10 @@ export default class RegisterHandlebarsHelpers {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pluralize helper that returns the appropriate localized string based on count
|
* Pluralize helper that returns the appropriate localized string based on count
|
||||||
* @param {number} count - The number to check for plurality
|
* @param {number} count - The number to check for plurality
|
||||||
* @param {string} baseKey - The base localization key (e.g., "DAGGERHEART.GENERAL.Target")
|
* @param {string} baseKey - The base localization key (e.g., "DAGGERHEART.GENERAL.Target")
|
||||||
* @returns {string} The localized singular or plural string
|
* @returns {string} The localized singular or plural string
|
||||||
*
|
*
|
||||||
* Usage: {{pluralize currentTargets.length "DAGGERHEART.GENERAL.Target"}}
|
* Usage: {{pluralize currentTargets.length "DAGGERHEART.GENERAL.Target"}}
|
||||||
* Returns: "Target" if count is exactly 1, "Targets" if count is 0, 2+, or invalid
|
* Returns: "Target" if count is exactly 1, "Targets" if count is 0, 2+, or invalid
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -603,7 +603,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 +620,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%;
|
||||||
|
|
@ -41,11 +41,13 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
animation: spinner 1.5s linear infinite;
|
animation: spinner 1.5s linear infinite;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@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 {
|
||||||
|
|
|
||||||
|
|
@ -14,4 +14,4 @@
|
||||||
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
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';
|
||||||
|
|
|
||||||
|
|
@ -395,7 +395,7 @@
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hint {
|
.hint {
|
||||||
flex: unset;
|
flex: unset;
|
||||||
}
|
}
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -127,4 +127,4 @@
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
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