mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-04-21 23:13:39 +02:00
Merge branch 'main' into feature/tag-team-rework
This commit is contained in:
commit
43114187b9
96 changed files with 1942 additions and 664 deletions
|
|
@ -233,6 +233,11 @@ Hooks.once('init', () => {
|
||||||
return handlebarsRegistration();
|
return handlebarsRegistration();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Hooks.on('i18nInit', () => {
|
||||||
|
// Setup homebrew resources
|
||||||
|
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).refreshConfig();
|
||||||
|
});
|
||||||
|
|
||||||
Hooks.on('setup', () => {
|
Hooks.on('setup', () => {
|
||||||
CONFIG.statusEffects = [
|
CONFIG.statusEffects = [
|
||||||
...CONFIG.statusEffects.filter(x => !['dead', 'unconscious'].includes(x.id)),
|
...CONFIG.statusEffects.filter(x => !['dead', 'unconscious'].includes(x.id)),
|
||||||
|
|
|
||||||
64
lang/en.json
64
lang/en.json
|
|
@ -74,6 +74,15 @@
|
||||||
"invalidDrop": "You can only drop Actor entities to summon.",
|
"invalidDrop": "You can only drop Actor entities to summon.",
|
||||||
"chatMessageTitle": "Test2",
|
"chatMessageTitle": "Test2",
|
||||||
"chatMessageHeaderTitle": "Summoning"
|
"chatMessageHeaderTitle": "Summoning"
|
||||||
|
},
|
||||||
|
"transform": {
|
||||||
|
"name": "Transform",
|
||||||
|
"tooltip": "Transform one actor into another",
|
||||||
|
"noTransformActor": "There is no assigned actor to transform into",
|
||||||
|
"transformActorMissing": "The assigned actor to transform into does not exist. It was probably deleted or moved in/out of a compendium",
|
||||||
|
"canvasError": "There is no active scene.",
|
||||||
|
"prototypeError": "You can only use a transform action from a Token",
|
||||||
|
"actorLinkError": "You cannot transform a token with Actor Link set to true"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Config": {
|
"Config": {
|
||||||
|
|
@ -129,6 +138,12 @@
|
||||||
},
|
},
|
||||||
"summon": {
|
"summon": {
|
||||||
"dropSummonsHere": "Drop Summons Here"
|
"dropSummonsHere": "Drop Summons Here"
|
||||||
|
},
|
||||||
|
"transform": {
|
||||||
|
"dropTransformHere": "Drop Transform Here",
|
||||||
|
"actorIsMissing": "The linked actor is missing. You should delete this link.",
|
||||||
|
"clearHitPoints": "Clear Hitpoints",
|
||||||
|
"clearStress": "Clear Stress"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -1031,7 +1046,8 @@
|
||||||
},
|
},
|
||||||
"vulnerable": {
|
"vulnerable": {
|
||||||
"name": "Vulnerable",
|
"name": "Vulnerable",
|
||||||
"description": "While a creature is Vulnerable, all rolls targeting them have advantage.\nA creature who is already Vulnerable can’t be made to take the condition again."
|
"description": "While a creature is Vulnerable, all rolls targeting them have advantage.\nA creature who is already Vulnerable can’t be made to take the condition again.",
|
||||||
|
"autoAppliedByLabel": "Max Stress"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"CountdownType": {
|
"CountdownType": {
|
||||||
|
|
@ -1166,12 +1182,12 @@
|
||||||
},
|
},
|
||||||
"far": {
|
"far": {
|
||||||
"name": "Far",
|
"name": "Far",
|
||||||
"description": "means a distance where one can see the appearance of a person or object, but probably not in great detail-- across a small battlefield or down a large corridor. This is usually about 30-100 feet away. While under danger, a PC will likely have to make an Agility check to get here safely. Anything on a battle map that is within the length of a standard piece of paper (~10-11 inches) can usually be considered far.",
|
"description": "means a distance where one can see the appearance of a person or object, but probably not in great detail-- across a small battlefield or down a large corridor. This is usually about 30-100 feet away. While under danger, a PC will likely have to make an Agility roll to get here safely. Anything on a battle map that is within the length of a standard piece of paper (~10-11 inches) can usually be considered far.",
|
||||||
"short": "Far"
|
"short": "Far"
|
||||||
},
|
},
|
||||||
"veryFar": {
|
"veryFar": {
|
||||||
"name": "Very Far",
|
"name": "Very Far",
|
||||||
"description": "means a distance where you can see the shape of a person or object, but probably not make outany details-- across a large battlefield or down a long street, generally about 100-300 feet away. While under danger, a PC likely has to make an Agility check to get here safely. Anything on a battle map that is beyond far distance, but still within sight of the characters can usually be considered very far.",
|
"description": "means a distance where you can see the shape of a person or object, but probably not make outany details-- across a large battlefield or down a long street, generally about 100-300 feet away. While under danger, a PC likely has to make an Agility roll to get here safely. Anything on a battle map that is beyond far distance, but still within sight of the characters can usually be considered very far.",
|
||||||
"short": "V. Far"
|
"short": "V. Far"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -1294,6 +1310,7 @@
|
||||||
"triggerTexts": {
|
"triggerTexts": {
|
||||||
"strangePatternsContentTitle": "Matched {nr} times.",
|
"strangePatternsContentTitle": "Matched {nr} times.",
|
||||||
"strangePatternsContentSubTitle": "Increase hope and stress to a total of {nr}.",
|
"strangePatternsContentSubTitle": "Increase hope and stress to a total of {nr}.",
|
||||||
|
"strangePatternsActionExplanation": "Left click to increase, right click to decrease",
|
||||||
"ferocityContent": "Spend 2 Hope to gain {bonus} bonus Evasion until after the next attack against you?",
|
"ferocityContent": "Spend 2 Hope to gain {bonus} bonus Evasion until after the next attack against you?",
|
||||||
"ferocityEffectDescription": "Your evasion is increased by {bonus}. This bonus lasts until after the next attack made against you."
|
"ferocityEffectDescription": "Your evasion is increased by {bonus}. This bonus lasts until after the next attack made against you."
|
||||||
},
|
},
|
||||||
|
|
@ -2268,6 +2285,7 @@
|
||||||
"identify": "Identity",
|
"identify": "Identity",
|
||||||
"imagePath": "Image Path",
|
"imagePath": "Image Path",
|
||||||
"inactiveEffects": "Inactive Effects",
|
"inactiveEffects": "Inactive Effects",
|
||||||
|
"initial": "Initial",
|
||||||
"inventory": "Inventory",
|
"inventory": "Inventory",
|
||||||
"itemResource": "Item Resource",
|
"itemResource": "Item Resource",
|
||||||
"itemQuantity": "Item Quantity",
|
"itemQuantity": "Item Quantity",
|
||||||
|
|
@ -2559,6 +2577,10 @@
|
||||||
"gm": { "label": "GM" },
|
"gm": { "label": "GM" },
|
||||||
"players": { "label": "Players" }
|
"players": { "label": "Players" }
|
||||||
},
|
},
|
||||||
|
"vulnerableAutomation": {
|
||||||
|
"label": "Vulnerable Automation",
|
||||||
|
"hint": "Automatically apply the Vulnerable condition when a actor reaches max stress"
|
||||||
|
},
|
||||||
"countdownAutomation": {
|
"countdownAutomation": {
|
||||||
"label": "Countdown Automation",
|
"label": "Countdown Automation",
|
||||||
"hint": "Automatically progress countdowns based on their progression settings"
|
"hint": "Automatically progress countdowns based on their progression settings"
|
||||||
|
|
@ -2627,6 +2649,14 @@
|
||||||
"title": "Triggers"
|
"title": "Triggers"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Metagaming": {
|
||||||
|
"FIELDS": {
|
||||||
|
"hideObserverPermissionInChat": {
|
||||||
|
"label": "Hide Chat Info From Players",
|
||||||
|
"hint": "Information such as hit/miss on attack rolls against adversaries will be hidden"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Homebrew": {
|
"Homebrew": {
|
||||||
"newDowntimeMove": "Downtime Move",
|
"newDowntimeMove": "Downtime Move",
|
||||||
"downtimeMove": "Downtime Move",
|
"downtimeMove": "Downtime Move",
|
||||||
|
|
@ -2641,6 +2671,8 @@
|
||||||
"resetMovesText": "Are you sure you want to reset?",
|
"resetMovesText": "Are you sure you want to reset?",
|
||||||
"deleteItemTitle": "Delete Homebrew Item",
|
"deleteItemTitle": "Delete Homebrew Item",
|
||||||
"deleteItemText": "Are you sure you want to delete the item?",
|
"deleteItemText": "Are you sure you want to delete the item?",
|
||||||
|
"deleteResourceTitle": "Delete Homebrew Resource",
|
||||||
|
"deleteResourceText": "Are you sure you want to delete the resource?",
|
||||||
"FIELDS": {
|
"FIELDS": {
|
||||||
"maxFear": { "label": "Max Fear" },
|
"maxFear": { "label": "Max Fear" },
|
||||||
"maxHope": { "label": "Max Hope" },
|
"maxHope": { "label": "Max Hope" },
|
||||||
|
|
@ -2649,6 +2681,13 @@
|
||||||
"label": "Max Cards in Loadout",
|
"label": "Max Cards in Loadout",
|
||||||
"hint": "Set to blank or 0 for unlimited maximum"
|
"hint": "Set to blank or 0 for unlimited maximum"
|
||||||
},
|
},
|
||||||
|
"resources": {
|
||||||
|
"resources": {
|
||||||
|
"value": { "label": "Icon" },
|
||||||
|
"isIcon": { "label": "Font Awesome Icon" },
|
||||||
|
"noColorFilter": { "label": "Disable Color Filter" }
|
||||||
|
}
|
||||||
|
},
|
||||||
"maxDomains": { "label": "Max Class Domains", "hint": "Max domains you can set on a class" }
|
"maxDomains": { "label": "Max Class Domains", "hint": "Max domains you can set on a class" }
|
||||||
},
|
},
|
||||||
"currency": {
|
"currency": {
|
||||||
|
|
@ -2677,6 +2716,13 @@
|
||||||
"adversaryType": {
|
"adversaryType": {
|
||||||
"title": "Custom Adversary Types",
|
"title": "Custom Adversary Types",
|
||||||
"newType": "Adversary Type"
|
"newType": "Adversary Type"
|
||||||
|
},
|
||||||
|
"resources": {
|
||||||
|
"typeTitle": "{type} Resources",
|
||||||
|
"filledIcon": "Filled Icon",
|
||||||
|
"emptyIcon": "Empty Icon",
|
||||||
|
"resourceIdentifier": "Resource Identifier",
|
||||||
|
"setResourceIdentifier": "Set Resource Identifier"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Menu": {
|
"Menu": {
|
||||||
|
|
@ -2686,6 +2732,11 @@
|
||||||
"label": "Configure Automation",
|
"label": "Configure Automation",
|
||||||
"hint": "Various settings automating resource management and more"
|
"hint": "Various settings automating resource management and more"
|
||||||
},
|
},
|
||||||
|
"metagaming": {
|
||||||
|
"name": "Metagaming Settings",
|
||||||
|
"label": "Configure Metagaming",
|
||||||
|
"hint": "Various settings controlling the flow of information to players"
|
||||||
|
},
|
||||||
"homebrew": {
|
"homebrew": {
|
||||||
"name": "Homebrew Settings",
|
"name": "Homebrew Settings",
|
||||||
"label": "Configure Homebrew",
|
"label": "Configure Homebrew",
|
||||||
|
|
@ -2809,7 +2860,7 @@
|
||||||
"title": "Domain Card"
|
"title": "Domain Card"
|
||||||
},
|
},
|
||||||
"dualityRoll": {
|
"dualityRoll": {
|
||||||
"abilityCheckTitle": "{ability} Check"
|
"abilityCheckTitle": "{ability} Roll"
|
||||||
},
|
},
|
||||||
"effectSummary": {
|
"effectSummary": {
|
||||||
"title": "Effects Applied",
|
"title": "Effects Applied",
|
||||||
|
|
@ -2824,7 +2875,7 @@
|
||||||
"selectLeader": "Select a Leader",
|
"selectLeader": "Select a Leader",
|
||||||
"selectMember": "Select a Member",
|
"selectMember": "Select a Member",
|
||||||
"rerollTitle": "Reroll Group Roll",
|
"rerollTitle": "Reroll Group Roll",
|
||||||
"rerollContent": "Are you sure you want to reroll your {trait} check?",
|
"rerollContent": "Are you sure you want to reroll your {trait} roll?",
|
||||||
"rerollTooltip": "Reroll",
|
"rerollTooltip": "Reroll",
|
||||||
"wholePartySelected": "The whole party is selected"
|
"wholePartySelected": "The whole party is selected"
|
||||||
},
|
},
|
||||||
|
|
@ -2990,7 +3041,8 @@
|
||||||
"tokenActorMissing": "{name} is missing an Actor",
|
"tokenActorMissing": "{name} is missing an Actor",
|
||||||
"tokenActorsMissing": "[{names}] missing Actors",
|
"tokenActorsMissing": "[{names}] missing Actors",
|
||||||
"domainTouchRequirement": "This domain card requires {nr} {domain} cards in the loadout to be used",
|
"domainTouchRequirement": "This domain card requires {nr} {domain} cards in the loadout to be used",
|
||||||
"knowTheTide": "Know The Tide gained a token"
|
"knowTheTide": "Know The Tide gained a token",
|
||||||
|
"lackingItemTransferPermission": "User {user} lacks owner permission needed to transfer items to {target}"
|
||||||
},
|
},
|
||||||
"Sidebar": {
|
"Sidebar": {
|
||||||
"actorDirectory": {
|
"actorDirectory": {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
export { default as DhAppearanceSettings } from './appearanceSettings.mjs';
|
export { default as DhAppearanceSettings } from './appearanceSettings.mjs';
|
||||||
export { default as DhAutomationSettings } from './automationSettings.mjs';
|
export { default as DhAutomationSettings } from './automationSettings.mjs';
|
||||||
export { default as DhHomebrewSettings } from './homebrewSettings.mjs';
|
export { default as DhHomebrewSettings } from './homebrewSettings.mjs';
|
||||||
|
export { default as DhMetagamingSettings } from './metagamingSettings.mjs';
|
||||||
export { default as DhVariantRuleSettings } from './variantRuleSettings.mjs';
|
export { default as DhVariantRuleSettings } from './variantRuleSettings.mjs';
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,8 @@ export default class DhAutomationSettings extends HandlebarsApplicationMixin(App
|
||||||
};
|
};
|
||||||
|
|
||||||
static PARTS = {
|
static PARTS = {
|
||||||
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
|
||||||
header: { template: 'systems/daggerheart/templates/settings/automation-settings/header.hbs' },
|
header: { template: 'systems/daggerheart/templates/settings/automation-settings/header.hbs' },
|
||||||
|
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
||||||
general: { template: 'systems/daggerheart/templates/settings/automation-settings/general.hbs' },
|
general: { template: 'systems/daggerheart/templates/settings/automation-settings/general.hbs' },
|
||||||
rules: { template: 'systems/daggerheart/templates/settings/automation-settings/deathMoves.hbs' },
|
rules: { template: 'systems/daggerheart/templates/settings/automation-settings/deathMoves.hbs' },
|
||||||
roll: { template: 'systems/daggerheart/templates/settings/automation-settings/roll.hbs' },
|
roll: { template: 'systems/daggerheart/templates/settings/automation-settings/roll.hbs' },
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { DhHomebrew } from '../../data/settings/_module.mjs';
|
import { DhHomebrew } from '../../data/settings/_module.mjs';
|
||||||
|
import { Resource } from '../../data/settings/Homebrew.mjs';
|
||||||
import { slugify } from '../../helpers/utils.mjs';
|
import { slugify } from '../../helpers/utils.mjs';
|
||||||
|
|
||||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
@ -44,6 +45,9 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
addAdversaryType: this.addAdversaryType,
|
addAdversaryType: this.addAdversaryType,
|
||||||
deleteAdversaryType: this.deleteAdversaryType,
|
deleteAdversaryType: this.deleteAdversaryType,
|
||||||
selectAdversaryType: this.selectAdversaryType,
|
selectAdversaryType: this.selectAdversaryType,
|
||||||
|
addResource: this.addResource,
|
||||||
|
removeResource: this.removeResource,
|
||||||
|
resetResourceImage: this.resetResourceImage,
|
||||||
save: this.save,
|
save: this.save,
|
||||||
resetTokenSizes: this.resetTokenSizes,
|
resetTokenSizes: this.resetTokenSizes,
|
||||||
reset: this.reset
|
reset: this.reset
|
||||||
|
|
@ -56,6 +60,10 @@ 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' },
|
||||||
|
resources: {
|
||||||
|
template: 'systems/daggerheart/templates/settings/homebrew-settings/resources.hbs',
|
||||||
|
scrollable: ['.resource-types-container']
|
||||||
|
},
|
||||||
itemTypes: { template: 'systems/daggerheart/templates/settings/homebrew-settings/itemFeatures.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' }
|
||||||
|
|
@ -64,7 +72,14 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
static TABS = {
|
static TABS = {
|
||||||
main: {
|
main: {
|
||||||
tabs: [{ id: 'settings' }, { id: 'domains' }, { id: 'types' }, { id: 'itemFeatures' }, { id: 'downtime' }],
|
tabs: [
|
||||||
|
{ id: 'settings' },
|
||||||
|
{ id: 'domains' },
|
||||||
|
{ id: 'types' },
|
||||||
|
{ id: 'resources' },
|
||||||
|
{ id: 'itemFeatures' },
|
||||||
|
{ id: 'downtime' }
|
||||||
|
],
|
||||||
initial: 'settings',
|
initial: 'settings',
|
||||||
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
|
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
|
||||||
}
|
}
|
||||||
|
|
@ -77,9 +92,17 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_attachPartListeners(partId, htmlElement, options) {
|
||||||
|
super._attachPartListeners(partId, htmlElement, options);
|
||||||
|
|
||||||
|
for (const element of htmlElement.querySelectorAll('.path-field input'))
|
||||||
|
element.addEventListener('change', this.toggleResourceIsIcon.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
async _prepareContext(_options) {
|
async _prepareContext(_options) {
|
||||||
const context = await super._prepareContext(_options);
|
const context = await super._prepareContext(_options);
|
||||||
context.settingFields = this.settings;
|
context.settingFields = this.settings;
|
||||||
|
context.schemaFields = context.settingFields.schema.fields;
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
@ -103,6 +126,8 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
? { id: this.selected.adversaryType, ...this.settings.adversaryTypes[this.selected.adversaryType] }
|
? { id: this.selected.adversaryType, ...this.settings.adversaryTypes[this.selected.adversaryType] }
|
||||||
: null;
|
: null;
|
||||||
break;
|
break;
|
||||||
|
case 'resources':
|
||||||
|
break;
|
||||||
case 'downtime':
|
case 'downtime':
|
||||||
context.restOptions = {
|
context.restOptions = {
|
||||||
shortRest: CONFIG.DH.GENERAL.defaultRestOptions.shortRest(),
|
shortRest: CONFIG.DH.GENERAL.defaultRestOptions.shortRest(),
|
||||||
|
|
@ -124,6 +149,33 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async toggleResourceIsIcon(event) {
|
||||||
|
const element = event.target.closest('.resource-icon-container');
|
||||||
|
const { actorType, resourceKey, imageKey } = element.dataset;
|
||||||
|
|
||||||
|
const current = this.settings.resources[actorType].resources[resourceKey].images[imageKey];
|
||||||
|
await this.settings.updateSource({
|
||||||
|
[`resources.${actorType}.resources.${resourceKey}.images.${imageKey}`]: {
|
||||||
|
isIcon: !current.isIcon,
|
||||||
|
value: ''
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async resetResourceImage(_event, button) {
|
||||||
|
const element = button.closest('.resource-icon-container');
|
||||||
|
const { actorType, resourceKey, imageKey } = element.dataset;
|
||||||
|
|
||||||
|
await this.settings.updateSource({
|
||||||
|
[`resources.${actorType}.resources.${resourceKey}.images.${imageKey}`]:
|
||||||
|
Resource.getDefaultImageData(imageKey)
|
||||||
|
});
|
||||||
|
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
static async changeCurrencyIcon(_, target) {
|
static async changeCurrencyIcon(_, target) {
|
||||||
const type = target.dataset.currency;
|
const type = target.dataset.currency;
|
||||||
const currentIcon = this.settings.currency[type].icon;
|
const currentIcon = this.settings.currency[type].icon;
|
||||||
|
|
@ -187,6 +239,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -227,6 +280,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -246,6 +300,8 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
await this.settings.updateSource({
|
await this.settings.updateSource({
|
||||||
[`${path}.-=${id}`]: null
|
[`${path}.-=${id}`]: null
|
||||||
});
|
});
|
||||||
|
|
||||||
|
game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -462,6 +518,58 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async addResource(_, target) {
|
||||||
|
const { actorType } = target.dataset;
|
||||||
|
const content = new foundry.data.fields.StringField({
|
||||||
|
label: game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.resources.resourceIdentifier'),
|
||||||
|
required: true
|
||||||
|
}).toFormGroup({}, { name: 'identifier', localize: true }).outerHTML;
|
||||||
|
|
||||||
|
async function callback(_, button) {
|
||||||
|
const identifier = button.form.elements.identifier.value;
|
||||||
|
if (!identifier) return;
|
||||||
|
|
||||||
|
const sluggedIdentifier = slugify(identifier);
|
||||||
|
|
||||||
|
await this.settings.updateSource({
|
||||||
|
[`resources.${actorType}.resources.${sluggedIdentifier}`]: Resource.getDefaultResourceData(identifier)
|
||||||
|
});
|
||||||
|
|
||||||
|
game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
await foundry.applications.api.DialogV2.prompt({
|
||||||
|
content: content,
|
||||||
|
rejectClose: false,
|
||||||
|
modal: true,
|
||||||
|
ok: { callback: callback.bind(this) },
|
||||||
|
window: {
|
||||||
|
title: game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.resources.setResourceIdentifier')
|
||||||
|
},
|
||||||
|
position: { width: 400 }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static async removeResource(_, target) {
|
||||||
|
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||||
|
window: {
|
||||||
|
title: game.i18n.localize(`DAGGERHEART.SETTINGS.Homebrew.deleteResourceTitle`)
|
||||||
|
},
|
||||||
|
content: game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.deleteResourceText')
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!confirmed) return;
|
||||||
|
|
||||||
|
const { actorType, resourceKey } = target.dataset;
|
||||||
|
await this.settings.updateSource({
|
||||||
|
[`resources.${actorType}.resources.-=${resourceKey}`]: null
|
||||||
|
});
|
||||||
|
|
||||||
|
game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
static async save() {
|
static async save() {
|
||||||
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
|
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
|
||||||
this.close();
|
this.close();
|
||||||
|
|
|
||||||
62
module/applications/settings/metagamingSettings.mjs
Normal file
62
module/applications/settings/metagamingSettings.mjs
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
import { DhMetagaming } from '../../data/settings/_module.mjs';
|
||||||
|
|
||||||
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
||||||
|
export default class DhMetagamingSettings extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
|
constructor() {
|
||||||
|
super({});
|
||||||
|
|
||||||
|
this.settings = new DhMetagaming(
|
||||||
|
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Metagaming).toObject()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get title() {
|
||||||
|
return game.i18n.localize('DAGGERHEART.SETTINGS.Menu.title');
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
tag: 'form',
|
||||||
|
id: 'daggerheart-metagaming-settings',
|
||||||
|
classes: ['daggerheart', 'dh-style', 'dialog', 'setting'],
|
||||||
|
position: { width: '600', height: 'auto' },
|
||||||
|
window: {
|
||||||
|
icon: 'fa-solid fa-eye-low-vision'
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
reset: this.reset,
|
||||||
|
save: this.save
|
||||||
|
},
|
||||||
|
form: { handler: this.updateData, submitOnChange: true }
|
||||||
|
};
|
||||||
|
|
||||||
|
static PARTS = {
|
||||||
|
header: { template: 'systems/daggerheart/templates/settings/metagaming-settings/header.hbs' },
|
||||||
|
general: { template: 'systems/daggerheart/templates/settings/metagaming-settings/general.hbs' },
|
||||||
|
footer: { template: 'systems/daggerheart/templates/settings/metagaming-settings/footer.hbs' }
|
||||||
|
};
|
||||||
|
|
||||||
|
async _prepareContext(_options) {
|
||||||
|
const context = await super._prepareContext(_options);
|
||||||
|
context.settingFields = this.settings;
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async updateData(_event, _element, formData) {
|
||||||
|
const updatedSettings = foundry.utils.expandObject(formData.object);
|
||||||
|
|
||||||
|
await this.settings.updateSource(updatedSettings);
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async reset() {
|
||||||
|
this.settings = new DhMetagaming();
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async save() {
|
||||||
|
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Metagaming, this.settings.toObject());
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -28,6 +28,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
removeEffect: this.removeEffect,
|
removeEffect: this.removeEffect,
|
||||||
addElement: this.addElement,
|
addElement: this.addElement,
|
||||||
removeElement: this.removeElement,
|
removeElement: this.removeElement,
|
||||||
|
removeTransformActor: this.removeTransformActor,
|
||||||
editEffect: this.editEffect,
|
editEffect: this.editEffect,
|
||||||
addDamage: this.addDamage,
|
addDamage: this.addDamage,
|
||||||
removeDamage: this.removeDamage,
|
removeDamage: this.removeDamage,
|
||||||
|
|
@ -41,7 +42,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
submitOnChange: true,
|
submitOnChange: true,
|
||||||
closeOnSubmit: false
|
closeOnSubmit: false
|
||||||
},
|
},
|
||||||
dragDrop: [{ dragSelector: null, dropSelector: '#summon-drop-zone', handlers: ['_onDrop'] }]
|
dragDrop: [{ dragSelector: null, dropSelector: '[data-is-drop-zone]', handlers: ['_onDrop'] }]
|
||||||
};
|
};
|
||||||
|
|
||||||
static PARTS = {
|
static PARTS = {
|
||||||
|
|
@ -120,6 +121,10 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
htmlElement.querySelectorAll('.summon-count-wrapper input').forEach(element => {
|
htmlElement.querySelectorAll('.summon-count-wrapper input').forEach(element => {
|
||||||
element.addEventListener('change', this.updateSummonCount.bind(this));
|
element.addEventListener('change', this.updateSummonCount.bind(this));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
htmlElement.querySelectorAll('.transform-resource input').forEach(element => {
|
||||||
|
element.addEventListener('change', this.updateTransformResource.bind(this));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async _prepareContext(_options) {
|
async _prepareContext(_options) {
|
||||||
|
|
@ -133,6 +138,18 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
context.summons.push({ actor, count: summon.count });
|
context.summons.push({ actor, count: summon.count });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (context.source.transform) {
|
||||||
|
const actor = await foundry.utils.fromUuid(context.source.transform.actorUUID);
|
||||||
|
context.transform = {
|
||||||
|
...context.source.transform,
|
||||||
|
actor:
|
||||||
|
actor ??
|
||||||
|
(context.source.transform.actorUUID && !actor
|
||||||
|
? { error: game.i18n.localize('DAGGERHEART.ACTIONS.Settings.transform.actorIsMissing') }
|
||||||
|
: null)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
context.openSection = this.openSection;
|
context.openSection = this.openSection;
|
||||||
context.tabs = this._getTabs(this.constructor.TABS);
|
context.tabs = this._getTabs(this.constructor.TABS);
|
||||||
context.config = CONFIG.DH;
|
context.config = CONFIG.DH;
|
||||||
|
|
@ -266,6 +283,12 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
if (doc) return doc.sheet.render({ force: true });
|
if (doc) return doc.sheet.render({ force: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async removeTransformActor() {
|
||||||
|
const data = this.action.toObject();
|
||||||
|
data.transform.actorUUID = null;
|
||||||
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
|
}
|
||||||
|
|
||||||
static addDamage(_event) {
|
static addDamage(_event) {
|
||||||
if (!this.action.damage.parts) return;
|
if (!this.action.damage.parts) return;
|
||||||
const data = this.action.toObject(),
|
const data = this.action.toObject(),
|
||||||
|
|
@ -346,6 +369,14 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateTransformResource(event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
const data = this.action.toObject();
|
||||||
|
data.transform.resourceRefresh[event.target.dataset.resource] = event.target.checked;
|
||||||
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
|
}
|
||||||
|
|
||||||
/** Specific implementation in extending classes **/
|
/** Specific implementation in extending classes **/
|
||||||
static async addEffect(_event) {}
|
static async addEffect(_event) {}
|
||||||
static removeEffect(_event, _button) {}
|
static removeEffect(_event, _button) {}
|
||||||
|
|
@ -364,6 +395,18 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const dropZone = event.target.closest('[data-is-drop-zone]');
|
||||||
|
if (!dropZone) return;
|
||||||
|
|
||||||
|
switch (dropZone.id) {
|
||||||
|
case 'summon-drop-zone':
|
||||||
|
return this.onSummonDrop(data);
|
||||||
|
case 'transform-drop-zone':
|
||||||
|
return this.onTransformDrop(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async onSummonDrop(data) {
|
||||||
const actionData = this.action.toObject();
|
const actionData = this.action.toObject();
|
||||||
let countvalue = 1;
|
let countvalue = 1;
|
||||||
for (const entry of actionData.summon) {
|
for (const entry of actionData.summon) {
|
||||||
|
|
@ -380,4 +423,10 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
actionData.summon.push({ actorUUID: data.uuid, count: countvalue });
|
actionData.summon.push({ actorUUID: data.uuid, count: countvalue });
|
||||||
await this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(actionData) });
|
await this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(actionData) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async onTransformDrop(data) {
|
||||||
|
const actionData = this.action.toObject();
|
||||||
|
actionData.transform.actorUUID = data.uuid;
|
||||||
|
await this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(actionData) });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,42 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
super(options);
|
super(options);
|
||||||
|
|
||||||
|
this.changeChoices = DhActiveEffectConfig.getChangeChoices();
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
classes: ['daggerheart', 'sheet', 'dh-style']
|
||||||
|
};
|
||||||
|
|
||||||
|
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'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get ChangeChoices for the changes autocomplete. Static for use in this class aswell as in settings-active-effect-config.mjs
|
||||||
|
* @returns {ChangeChoice { value: string, label: string, hint: string, group: string }[]}
|
||||||
|
*/
|
||||||
|
static getChangeChoices() {
|
||||||
const ignoredActorKeys = ['config', 'DhEnvironment', 'DhParty'];
|
const ignoredActorKeys = ['config', 'DhEnvironment', 'DhParty'];
|
||||||
|
|
||||||
const getAllLeaves = (root, group, parentPath = '') => {
|
const getAllLeaves = (root, group, parentPath = '') => {
|
||||||
|
|
@ -23,7 +59,7 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
||||||
|
|
||||||
return leaves;
|
return leaves;
|
||||||
};
|
};
|
||||||
this.changeChoices = Object.keys(game.system.api.models.actors).reduce((acc, key) => {
|
return Object.keys(game.system.api.models.actors).reduce((acc, key) => {
|
||||||
if (ignoredActorKeys.includes(key)) return acc;
|
if (ignoredActorKeys.includes(key)) return acc;
|
||||||
|
|
||||||
const model = game.system.api.models.actors[key];
|
const model = game.system.api.models.actors[key];
|
||||||
|
|
@ -62,34 +98,6 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
static DEFAULT_OPTIONS = {
|
|
||||||
classes: ['daggerheart', 'sheet', 'dh-style']
|
|
||||||
};
|
|
||||||
|
|
||||||
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'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
_attachPartListeners(partId, htmlElement, options) {
|
_attachPartListeners(partId, htmlElement, options) {
|
||||||
super._attachPartListeners(partId, htmlElement, options);
|
super._attachPartListeners(partId, htmlElement, options);
|
||||||
const changeChoices = this.changeChoices;
|
const changeChoices = this.changeChoices;
|
||||||
|
|
|
||||||
|
|
@ -7,19 +7,7 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi
|
||||||
super({});
|
super({});
|
||||||
|
|
||||||
this.effect = foundry.utils.deepClone(effect);
|
this.effect = foundry.utils.deepClone(effect);
|
||||||
const ignoredActorKeys = ['config', 'DhEnvironment'];
|
this.changeChoices = game.system.api.applications.sheetConfigs.ActiveEffectConfig.getChangeChoices();
|
||||||
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 = {
|
static DEFAULT_OPTIONS = {
|
||||||
|
|
|
||||||
|
|
@ -73,9 +73,11 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async updateData(event, element, formData) {
|
static async updateData(_event, _element, formData) {
|
||||||
const data = foundry.utils.expandObject(formData.object);
|
const data = foundry.utils.expandObject(formData.object);
|
||||||
foundry.utils.mergeObject(this.move, data);
|
await this.updateMove({
|
||||||
|
[`${this.movePath}`]: data
|
||||||
|
});
|
||||||
|
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
@ -135,9 +137,7 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
await this.settings.updateSource({ [`${this.actionsPath}.${action.id}`]: action });
|
await this.updateMove({ [`${this.actionsPath}.${action.id}`]: action });
|
||||||
this.move = foundry.utils.getProperty(this.settings, this.movePath);
|
|
||||||
|
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -150,13 +150,12 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App
|
||||||
await game.system.api.applications.sheetConfigs.SettingActiveEffectConfig.configure(effect);
|
await game.system.api.applications.sheetConfigs.SettingActiveEffectConfig.configure(effect);
|
||||||
if (!updatedEffect) return;
|
if (!updatedEffect) return;
|
||||||
|
|
||||||
await this.settings.updateSource({
|
await this.updateMove({
|
||||||
[`${this.movePath}.effects`]: this.move.effects.reduce((acc, effect, index) => {
|
[`${this.movePath}.effects`]: this.move.effects.reduce((acc, effect, index) => {
|
||||||
acc.push(index === effectIndex ? { ...updatedEffect, id: effect.id } : effect);
|
acc.push(index === effectIndex ? { ...updatedEffect, id: effect.id } : effect);
|
||||||
return acc;
|
return acc;
|
||||||
}, [])
|
}, [])
|
||||||
});
|
});
|
||||||
this.move = foundry.utils.getProperty(this.settings, this.movePath);
|
|
||||||
this.render();
|
this.render();
|
||||||
} else {
|
} else {
|
||||||
const action = this.move.actions.get(id);
|
const action = this.move.actions.get(id);
|
||||||
|
|
@ -171,13 +170,13 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App
|
||||||
: existingEffectIndex === -1
|
: existingEffectIndex === -1
|
||||||
? [...currentEffects, effectData]
|
? [...currentEffects, effectData]
|
||||||
: currentEffects.with(existingEffectIndex, effectData);
|
: currentEffects.with(existingEffectIndex, effectData);
|
||||||
await this.settings.updateSource({
|
await this.updateMove({
|
||||||
[`${this.movePath}.effects`]: updatedEffects
|
[`${this.movePath}.effects`]: updatedEffects
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.settings.updateSource({ [`${this.actionsPath}.${id}`]: updatedMove });
|
await this.updateMove({ [`${this.actionsPath}.${id}`]: updatedMove });
|
||||||
this.move = foundry.utils.getProperty(this.settings, this.movePath);
|
|
||||||
this.render();
|
this.render();
|
||||||
return updatedEffects;
|
return updatedEffects;
|
||||||
}).render(true);
|
}).render(true);
|
||||||
|
|
@ -199,33 +198,36 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await this.settings.updateSource({
|
await this.updateMove({
|
||||||
[this.movePath]: {
|
[this.movePath]: {
|
||||||
effects: move.effects.filter(x => x.id !== id),
|
effects: move.effects.filter(x => x.id !== id),
|
||||||
actions: move.actions
|
actions: move.actions
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await this.settings.updateSource({ [`${this.actionsPath}.-=${target.dataset.id}`]: null });
|
await this.updateMove({ [`${this.actionsPath}.-=${target.dataset.id}`]: null });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.move = foundry.utils.getProperty(this.settings, this.movePath);
|
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
static async addEffect(_, target) {
|
static async addEffect() {
|
||||||
const currentEffects = foundry.utils.getProperty(this.settings, `${this.movePath}.effects`);
|
const currentEffects = foundry.utils.getProperty(this.settings, `${this.movePath}.effects`);
|
||||||
await this.settings.updateSource({
|
|
||||||
|
await this.updateMove({
|
||||||
[`${this.movePath}.effects`]: [
|
[`${this.movePath}.effects`]: [
|
||||||
...currentEffects,
|
...currentEffects,
|
||||||
game.system.api.data.activeEffects.BaseEffect.getDefaultObject()
|
game.system.api.data.activeEffects.BaseEffect.getDefaultObject()
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
this.move = foundry.utils.getProperty(this.settings, this.movePath);
|
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updateMove(update) {
|
||||||
|
await this.settings.updateSource(update);
|
||||||
|
this.move = foundry.utils.getProperty(this.settings, this.movePath);
|
||||||
|
}
|
||||||
|
|
||||||
static resetMoves() {}
|
static resetMoves() {}
|
||||||
|
|
||||||
_filterTabs(tabs) {
|
_filterTabs(tabs) {
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
handleResourceDice: CharacterSheet.#handleResourceDice,
|
handleResourceDice: CharacterSheet.#handleResourceDice,
|
||||||
advanceResourceDie: CharacterSheet.#advanceResourceDie,
|
advanceResourceDie: CharacterSheet.#advanceResourceDie,
|
||||||
cancelBeastform: CharacterSheet.#cancelBeastform,
|
cancelBeastform: CharacterSheet.#cancelBeastform,
|
||||||
|
toggleResourceManagement: CharacterSheet.#toggleResourceManagement,
|
||||||
useDowntime: this.useDowntime,
|
useDowntime: this.useDowntime,
|
||||||
viewParty: CharacterSheet.#viewParty
|
viewParty: CharacterSheet.#viewParty
|
||||||
},
|
},
|
||||||
|
|
@ -225,6 +226,9 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
async _preparePartContext(partId, context, options) {
|
async _preparePartContext(partId, context, options) {
|
||||||
context = await super._preparePartContext(partId, context, options);
|
context = await super._preparePartContext(partId, context, options);
|
||||||
switch (partId) {
|
switch (partId) {
|
||||||
|
case 'header':
|
||||||
|
await this._prepareHeaderContext(context, options);
|
||||||
|
break;
|
||||||
case 'loadout':
|
case 'loadout':
|
||||||
await this._prepareLoadoutContext(context, options);
|
await this._prepareLoadoutContext(context, options);
|
||||||
break;
|
break;
|
||||||
|
|
@ -239,6 +243,12 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _prepareHeaderContext(context, _options) {
|
||||||
|
context.hasExtraResources = Object.keys(CONFIG.DH.RESOURCE.character.all).some(
|
||||||
|
key => !CONFIG.DH.RESOURCE.character.base[key]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare render context for the Loadout part.
|
* Prepare render context for the Loadout part.
|
||||||
* @param {ApplicationRenderContext} context
|
* @param {ApplicationRenderContext} context
|
||||||
|
|
@ -922,6 +932,78 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async #toggleResourceManagement(event, button) {
|
||||||
|
event.stopPropagation();
|
||||||
|
const existingTooltip = document.body.querySelector('.locked-tooltip .resource-management-container');
|
||||||
|
if (existingTooltip) {
|
||||||
|
game.tooltip.dismissLockedTooltips();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const extraResources = Object.values(CONFIG.DH.RESOURCE.character.all).reduce((acc, resource) => {
|
||||||
|
if (CONFIG.DH.RESOURCE.character.base[resource.id]) return acc;
|
||||||
|
|
||||||
|
const resourceData = this.document.system.resources[resource.id];
|
||||||
|
acc[resource.id] = {
|
||||||
|
id: resource.id,
|
||||||
|
label: game.i18n.localize(resource.label),
|
||||||
|
value: resourceData.value,
|
||||||
|
max: resourceData.max,
|
||||||
|
fullIcon: resource.images?.full ?? { value: 'fa-solid fa-circle', isIcon: true },
|
||||||
|
emptyIcon: resource.images?.empty ?? { value: 'fa-regular fa-circle', isIcon: true }
|
||||||
|
};
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const html = document.createElement('div');
|
||||||
|
html.innerHTML = await foundry.applications.handlebars.renderTemplate(
|
||||||
|
`systems/daggerheart/templates/ui/tooltip/resourceManagement.hbs`,
|
||||||
|
{
|
||||||
|
resources: extraResources
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const target = button.closest('.resource-section');
|
||||||
|
|
||||||
|
game.tooltip.dismissLockedTooltips();
|
||||||
|
game.tooltip.activate(target, {
|
||||||
|
html,
|
||||||
|
locked: true,
|
||||||
|
cssClass: 'bordered-tooltip',
|
||||||
|
direction: 'DOWN',
|
||||||
|
noOffset: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const resourceManager = target.querySelector('.resource-manager');
|
||||||
|
resourceManager.classList.toggle('inverted');
|
||||||
|
|
||||||
|
Hooks.once(CONFIG.DH.HOOKS.hooksConfig.lockedTooltipDismissed, () => {
|
||||||
|
resourceManager.classList.toggle('inverted');
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const element of html.querySelectorAll('.resource-value'))
|
||||||
|
element.addEventListener('click', this.onUpdateResource.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
async onUpdateResource(event) {
|
||||||
|
const target = event.target.closest('.resource-value');
|
||||||
|
const { resource, value: textValue } = target.dataset;
|
||||||
|
|
||||||
|
const inputValue = Number.parseInt(textValue);
|
||||||
|
const decreasing = inputValue <= this.document.system.resources[resource].value;
|
||||||
|
const value = decreasing ? inputValue - 1 : inputValue;
|
||||||
|
await this.document.update({ [`system.resources.${resource}.value`]: value }, { render: false });
|
||||||
|
|
||||||
|
/* Update resource symbols */
|
||||||
|
const section = target.closest('.resource-section');
|
||||||
|
for (const element of section.querySelectorAll('.resource-value')) {
|
||||||
|
const showFull = Number.parseInt(element.dataset.value) <= value;
|
||||||
|
element.querySelector('.full').classList.toggle('hidden', !showFull);
|
||||||
|
element.querySelector('.empty').classList.toggle('hidden', showFull);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open the downtime application.
|
* Open the downtime application.
|
||||||
* @type {ApplicationClickAction}
|
* @type {ApplicationClickAction}
|
||||||
|
|
|
||||||
|
|
@ -264,15 +264,6 @@ export default class Party extends DHBaseActorSheet {
|
||||||
).render({ force: true });
|
).render({ force: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the set of ContextMenu options for Consumable and Loot.
|
|
||||||
* @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance
|
|
||||||
* @this {CharacterSheet}
|
|
||||||
* @protected
|
|
||||||
*/
|
|
||||||
static #getItemContextOptions() {
|
|
||||||
return this._getContextMenuCommonOptions.call(this, { usable: true, toChat: true });
|
|
||||||
}
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
/* Filter Tracking */
|
/* Filter Tracking */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
|
||||||
|
|
@ -44,8 +44,32 @@ export default class DHBaseActorSettings extends DHApplicationMixin(DocumentShee
|
||||||
const context = await super._prepareContext(options);
|
const context = await super._prepareContext(options);
|
||||||
context.isNPC = this.actor.isNPC;
|
context.isNPC = this.actor.isNPC;
|
||||||
|
|
||||||
if (context.systemFields.attack)
|
if (context.systemFields.attack) {
|
||||||
context.systemFields.attack.fields = this.actor.system.attack.schema.fields;
|
context.systemFields.attack.fields = this.actor.system.attack.schema.fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create fake fields for actor configurable max resource value.
|
||||||
|
const resourceConfig = CONFIG.DH.RESOURCE[this.actor.type]?.all;
|
||||||
|
if (resourceConfig) {
|
||||||
|
const relevant = ['hitPoints', 'stress'].filter(r => r in resourceConfig);
|
||||||
|
context.resources = relevant.map(key => {
|
||||||
|
const data = this.actor._source.system.resources[key];
|
||||||
|
const config = resourceConfig[key];
|
||||||
|
return {
|
||||||
|
label: config.label,
|
||||||
|
name: `system.resources.${key}.max`,
|
||||||
|
value: data.max ?? config.max,
|
||||||
|
tooltip: key === 'hitPoints' ? game.i18n.localize('DAGGERHEART.UI.Tooltip.maxHPClassBound') : null,
|
||||||
|
field: new foundry.data.fields.NumberField({
|
||||||
|
initial: config.max,
|
||||||
|
integer: true,
|
||||||
|
label: game.i18n.format('DAGGERHEART.GENERAL.maxWithThing', {
|
||||||
|
thing: game.i18n.localize(config.label)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -691,6 +691,9 @@ export default function DHApplicationMixin(Base) {
|
||||||
case 'weapon':
|
case 'weapon':
|
||||||
presets.folder = 'equipments.folders.weapons';
|
presets.folder = 'equipments.folders.weapons';
|
||||||
break;
|
break;
|
||||||
|
case 'feature':
|
||||||
|
presets.folder = 'features';
|
||||||
|
break;
|
||||||
case 'domainCard':
|
case 'domainCard':
|
||||||
presets.folder = 'domains';
|
presets.folder = 'domains';
|
||||||
presets.filter = {
|
presets.filter = {
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
||||||
],
|
],
|
||||||
dragDrop: [
|
dragDrop: [
|
||||||
{ dragSelector: '.inventory-item[data-type="attack"]', dropSelector: null },
|
{ dragSelector: '.inventory-item[data-type="attack"]', dropSelector: null },
|
||||||
{ dragSelector: ".currency[data-currency] .drag-handle", dropSelector: null }
|
{ dragSelector: '.currency[data-currency] .drag-handle', dropSelector: null }
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -92,7 +92,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
||||||
value: context.source.system.gold[key]
|
value: context.source.system.gold[key]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
context.inventory.hasCurrency = Object.values(context.inventory.currencies).some((c) => c.enabled);
|
context.inventory.hasCurrency = Object.values(context.inventory.currencies).some(c => c.enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
|
|
@ -270,7 +270,9 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
||||||
currency
|
currency
|
||||||
});
|
});
|
||||||
if (quantity) {
|
if (quantity) {
|
||||||
originActor.update({ [`system.gold.${currency}`]: Math.max(0, originActor.system.gold[currency] - quantity) });
|
originActor.update({
|
||||||
|
[`system.gold.${currency}`]: Math.max(0, originActor.system.gold[currency] - quantity)
|
||||||
|
});
|
||||||
this.document.update({ [`system.gold.${currency}`]: this.document.system.gold[currency] + quantity });
|
this.document.update({ [`system.gold.${currency}`]: this.document.system.gold[currency] + quantity });
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
@ -292,6 +294,15 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
||||||
|
|
||||||
/* Handling transfer of inventoryItems */
|
/* Handling transfer of inventoryItems */
|
||||||
if (item.system.metadata.isInventoryItem) {
|
if (item.system.metadata.isInventoryItem) {
|
||||||
|
if (!this.document.testUserPermission(game.user, 'OWNER', { exact: true })) {
|
||||||
|
return ui.notifications.error(
|
||||||
|
game.i18n.format('DAGGERHEART.UI.Notifications.lackingItemTransferPermission', {
|
||||||
|
user: game.user.name,
|
||||||
|
target: this.document.name
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (item.system.metadata.isQuantifiable) {
|
if (item.system.metadata.isQuantifiable) {
|
||||||
const actorItem = originActor.items.get(data.originId);
|
const actorItem = originActor.items.get(data.originId);
|
||||||
const quantityTransfered = await game.system.api.applications.dialogs.ItemTransferDialog.configure({
|
const quantityTransfered = await game.system.api.applications.dialogs.ItemTransferDialog.configure({
|
||||||
|
|
@ -300,14 +311,6 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (quantityTransfered) {
|
if (quantityTransfered) {
|
||||||
if (quantityTransfered === actorItem.system.quantity) {
|
|
||||||
await originActor.deleteEmbeddedDocuments('Item', [data.originId]);
|
|
||||||
} else {
|
|
||||||
await actorItem.update({
|
|
||||||
'system.quantity': actorItem.system.quantity - quantityTransfered
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const existingItem = this.document.items.find(x => itemIsIdentical(x, item));
|
const existingItem = this.document.items.find(x => itemIsIdentical(x, item));
|
||||||
if (existingItem) {
|
if (existingItem) {
|
||||||
await existingItem.update({
|
await existingItem.update({
|
||||||
|
|
@ -325,10 +328,18 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (quantityTransfered === actorItem.system.quantity) {
|
||||||
|
await originActor.deleteEmbeddedDocuments('Item', [data.originId]);
|
||||||
|
} else {
|
||||||
|
await actorItem.update({
|
||||||
|
'system.quantity': actorItem.system.quantity - quantityTransfered
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await originActor.deleteEmbeddedDocuments('Item', [data.originId]);
|
|
||||||
await this.document.createEmbeddedDocuments('Item', [item.toObject()]);
|
await this.document.createEmbeddedDocuments('Item', [item.toObject()]);
|
||||||
|
await originActor.deleteEmbeddedDocuments('Item', [data.originId]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -339,7 +350,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
||||||
*/
|
*/
|
||||||
async _onDragStart(event) {
|
async _onDragStart(event) {
|
||||||
// Handle drag/dropping currencies
|
// Handle drag/dropping currencies
|
||||||
const currencyEl = event.currentTarget.closest(".currency[data-currency]");
|
const currencyEl = event.currentTarget.closest('.currency[data-currency]');
|
||||||
if (currencyEl) {
|
if (currencyEl) {
|
||||||
const currency = currencyEl.dataset.currency;
|
const currency = currencyEl.dataset.currency;
|
||||||
const data = { type: 'Currency', currency, originActor: this.document.uuid };
|
const data = { type: 'Currency', currency, originActor: this.document.uuid };
|
||||||
|
|
@ -359,8 +370,8 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
||||||
event.dataTransfer.setData('text/plain', JSON.stringify(attackData));
|
event.dataTransfer.setData('text/plain', JSON.stringify(attackData));
|
||||||
event.dataTransfer.setDragImage(attackItem.querySelector('img'), 60, 0);
|
event.dataTransfer.setDragImage(attackItem.querySelector('img'), 60, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const item = await getDocFromElement(event.target);
|
const item = await getDocFromElement(event.target);
|
||||||
if (item) {
|
if (item) {
|
||||||
const dragData = {
|
const dragData = {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { refreshIsAllowed } from '../../../helpers/utils.mjs';
|
import { RefreshFeatures } from '../../../helpers/utils.mjs';
|
||||||
|
|
||||||
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
||||||
const { AbstractSidebarTab } = foundry.applications.sidebar;
|
const { AbstractSidebarTab } = foundry.applications.sidebar;
|
||||||
|
|
@ -54,73 +54,6 @@ export default class DaggerheartMenu extends HandlebarsApplicationMixin(Abstract
|
||||||
return context;
|
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 && refreshIsAllowed(types, 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 (refreshIsAllowed(types, 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 */
|
/* Application Clicks Actions */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
@ -133,30 +66,9 @@ export default class DaggerheartMenu extends HandlebarsApplicationMixin(Abstract
|
||||||
|
|
||||||
static async #refreshActors() {
|
static async #refreshActors() {
|
||||||
const refreshKeys = Object.keys(this.refreshSelections).filter(key => this.refreshSelections[key].selected);
|
const refreshKeys = Object.keys(this.refreshSelections).filter(key => this.refreshSelections[key].selected);
|
||||||
await this.getRefreshables(refreshKeys);
|
await RefreshFeatures(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();
|
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();
|
this.render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -251,6 +251,12 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
|
|
||||||
/* If any noticeable slowdown occurs, consider replacing with enriching description on clicking to expand descriptions */
|
/* If any noticeable slowdown occurs, consider replacing with enriching description on clicking to expand descriptions */
|
||||||
for (const item of this.items) {
|
for (const item of this.items) {
|
||||||
|
if (["weapon", "armor"].includes(item.type)) {
|
||||||
|
item.system.enrichedTags = await foundry.applications.handlebars.renderTemplate(
|
||||||
|
'systems/daggerheart/templates/sheets/global/partials/item-tags.hbs',
|
||||||
|
item.system,
|
||||||
|
);
|
||||||
|
}
|
||||||
item.system.enrichedDescription =
|
item.system.enrichedDescription =
|
||||||
(await item.system.getEnrichedDescription?.()) ??
|
(await item.system.getEnrichedDescription?.()) ??
|
||||||
(await foundry.applications.ux.TextEditor.implementation.enrichHTML(item.description));
|
(await foundry.applications.ux.TextEditor.implementation.enrichHTML(item.description));
|
||||||
|
|
|
||||||
|
|
@ -11,3 +11,4 @@ export * as settingsConfig from './settingsConfig.mjs';
|
||||||
export * as systemConfig from './system.mjs';
|
export * as systemConfig from './system.mjs';
|
||||||
export * as itemBrowserConfig from './itemBrowserConfig.mjs';
|
export * as itemBrowserConfig from './itemBrowserConfig.mjs';
|
||||||
export * as triggerConfig from './triggerConfig.mjs';
|
export * as triggerConfig from './triggerConfig.mjs';
|
||||||
|
export * as resourceConfig from './resourceConfig.mjs';
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,12 @@ export const actionTypes = {
|
||||||
icon: 'fa-ghost',
|
icon: 'fa-ghost',
|
||||||
tooltip: 'DAGGERHEART.ACTIONS.TYPES.summon.tooltip'
|
tooltip: 'DAGGERHEART.ACTIONS.TYPES.summon.tooltip'
|
||||||
},
|
},
|
||||||
|
transform: {
|
||||||
|
id: 'transform',
|
||||||
|
name: 'DAGGERHEART.ACTIONS.TYPES.transform.name',
|
||||||
|
icon: 'fa-dragon',
|
||||||
|
tooltip: 'DAGGERHEART.ACTIONS.TYPES.transform.tooltip'
|
||||||
|
},
|
||||||
effect: {
|
effect: {
|
||||||
id: 'effect',
|
id: 'effect',
|
||||||
name: 'DAGGERHEART.ACTIONS.TYPES.effect.name',
|
name: 'DAGGERHEART.ACTIONS.TYPES.effect.name',
|
||||||
|
|
|
||||||
|
|
@ -55,24 +55,6 @@ export const abilities = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const scrollingTextResource = {
|
|
||||||
hitPoints: {
|
|
||||||
label: 'DAGGERHEART.GENERAL.HitPoints.plural',
|
|
||||||
reversed: true
|
|
||||||
},
|
|
||||||
stress: {
|
|
||||||
label: 'DAGGERHEART.GENERAL.stress',
|
|
||||||
reversed: true
|
|
||||||
},
|
|
||||||
hope: {
|
|
||||||
label: 'DAGGERHEART.GENERAL.hope'
|
|
||||||
},
|
|
||||||
armor: {
|
|
||||||
label: 'DAGGERHEART.GENERAL.armor',
|
|
||||||
reversed: true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const featureProperties = {
|
export const featureProperties = {
|
||||||
agility: {
|
agility: {
|
||||||
name: 'DAGGERHEART.CONFIG.Traits.agility.name',
|
name: 'DAGGERHEART.CONFIG.Traits.agility.name',
|
||||||
|
|
@ -506,8 +488,8 @@ export const subclassFeatureLabels = {
|
||||||
* @property {number[]} damage
|
* @property {number[]} damage
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {Record<string, Record<2 | 3 | 4, TierData>}
|
* @type {Record<string, Record<2 | 3 | 4, TierData>}
|
||||||
* Scaling data used to change an adversary's tier. Each rank is applied incrementally.
|
* Scaling data used to change an adversary's tier. Each rank is applied incrementally.
|
||||||
*/
|
*/
|
||||||
export const adversaryScalingData = {
|
export const adversaryScalingData = {
|
||||||
|
|
@ -518,7 +500,7 @@ export const adversaryScalingData = {
|
||||||
severeThreshold: 10,
|
severeThreshold: 10,
|
||||||
hp: 1,
|
hp: 1,
|
||||||
stress: 2,
|
stress: 2,
|
||||||
attack: 2,
|
attack: 2
|
||||||
},
|
},
|
||||||
3: {
|
3: {
|
||||||
difficulty: 2,
|
difficulty: 2,
|
||||||
|
|
@ -526,7 +508,7 @@ export const adversaryScalingData = {
|
||||||
severeThreshold: 15,
|
severeThreshold: 15,
|
||||||
hp: 1,
|
hp: 1,
|
||||||
stress: 0,
|
stress: 0,
|
||||||
attack: 2,
|
attack: 2
|
||||||
},
|
},
|
||||||
4: {
|
4: {
|
||||||
difficulty: 2,
|
difficulty: 2,
|
||||||
|
|
@ -534,7 +516,7 @@ export const adversaryScalingData = {
|
||||||
severeThreshold: 25,
|
severeThreshold: 25,
|
||||||
hp: 1,
|
hp: 1,
|
||||||
stress: 0,
|
stress: 0,
|
||||||
attack: 2,
|
attack: 2
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
horde: {
|
horde: {
|
||||||
|
|
@ -544,7 +526,7 @@ export const adversaryScalingData = {
|
||||||
severeThreshold: 8,
|
severeThreshold: 8,
|
||||||
hp: 2,
|
hp: 2,
|
||||||
stress: 0,
|
stress: 0,
|
||||||
attack: 0,
|
attack: 0
|
||||||
},
|
},
|
||||||
3: {
|
3: {
|
||||||
difficulty: 2,
|
difficulty: 2,
|
||||||
|
|
@ -552,7 +534,7 @@ export const adversaryScalingData = {
|
||||||
severeThreshold: 12,
|
severeThreshold: 12,
|
||||||
hp: 0,
|
hp: 0,
|
||||||
stress: 1,
|
stress: 1,
|
||||||
attack: 1,
|
attack: 1
|
||||||
},
|
},
|
||||||
4: {
|
4: {
|
||||||
difficulty: 2,
|
difficulty: 2,
|
||||||
|
|
@ -560,7 +542,7 @@ export const adversaryScalingData = {
|
||||||
severeThreshold: 15,
|
severeThreshold: 15,
|
||||||
hp: 2,
|
hp: 2,
|
||||||
stress: 0,
|
stress: 0,
|
||||||
attack: 0,
|
attack: 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
leader: {
|
leader: {
|
||||||
|
|
@ -570,7 +552,7 @@ export const adversaryScalingData = {
|
||||||
severeThreshold: 10,
|
severeThreshold: 10,
|
||||||
hp: 0,
|
hp: 0,
|
||||||
stress: 0,
|
stress: 0,
|
||||||
attack: 1,
|
attack: 1
|
||||||
},
|
},
|
||||||
3: {
|
3: {
|
||||||
difficulty: 2,
|
difficulty: 2,
|
||||||
|
|
@ -578,7 +560,7 @@ export const adversaryScalingData = {
|
||||||
severeThreshold: 15,
|
severeThreshold: 15,
|
||||||
hp: 1,
|
hp: 1,
|
||||||
stress: 0,
|
stress: 0,
|
||||||
attack: 2,
|
attack: 2
|
||||||
},
|
},
|
||||||
4: {
|
4: {
|
||||||
difficulty: 2,
|
difficulty: 2,
|
||||||
|
|
@ -586,7 +568,7 @@ export const adversaryScalingData = {
|
||||||
severeThreshold: 25,
|
severeThreshold: 25,
|
||||||
hp: 1,
|
hp: 1,
|
||||||
stress: 1,
|
stress: 1,
|
||||||
attack: 3,
|
attack: 3
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
minion: {
|
minion: {
|
||||||
|
|
@ -596,7 +578,7 @@ export const adversaryScalingData = {
|
||||||
severeThreshold: 0,
|
severeThreshold: 0,
|
||||||
hp: 0,
|
hp: 0,
|
||||||
stress: 0,
|
stress: 0,
|
||||||
attack: 1,
|
attack: 1
|
||||||
},
|
},
|
||||||
3: {
|
3: {
|
||||||
difficulty: 2,
|
difficulty: 2,
|
||||||
|
|
@ -604,7 +586,7 @@ export const adversaryScalingData = {
|
||||||
severeThreshold: 0,
|
severeThreshold: 0,
|
||||||
hp: 0,
|
hp: 0,
|
||||||
stress: 1,
|
stress: 1,
|
||||||
attack: 1,
|
attack: 1
|
||||||
},
|
},
|
||||||
4: {
|
4: {
|
||||||
difficulty: 2,
|
difficulty: 2,
|
||||||
|
|
@ -612,7 +594,7 @@ export const adversaryScalingData = {
|
||||||
severeThreshold: 0,
|
severeThreshold: 0,
|
||||||
hp: 0,
|
hp: 0,
|
||||||
stress: 0,
|
stress: 0,
|
||||||
attack: 1,
|
attack: 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ranged: {
|
ranged: {
|
||||||
|
|
@ -622,7 +604,7 @@ export const adversaryScalingData = {
|
||||||
severeThreshold: 6,
|
severeThreshold: 6,
|
||||||
hp: 1,
|
hp: 1,
|
||||||
stress: 0,
|
stress: 0,
|
||||||
attack: 1,
|
attack: 1
|
||||||
},
|
},
|
||||||
3: {
|
3: {
|
||||||
difficulty: 2,
|
difficulty: 2,
|
||||||
|
|
@ -630,7 +612,7 @@ export const adversaryScalingData = {
|
||||||
severeThreshold: 14,
|
severeThreshold: 14,
|
||||||
hp: 1,
|
hp: 1,
|
||||||
stress: 1,
|
stress: 1,
|
||||||
attack: 2,
|
attack: 2
|
||||||
},
|
},
|
||||||
4: {
|
4: {
|
||||||
difficulty: 2,
|
difficulty: 2,
|
||||||
|
|
@ -638,7 +620,7 @@ export const adversaryScalingData = {
|
||||||
severeThreshold: 10,
|
severeThreshold: 10,
|
||||||
hp: 1,
|
hp: 1,
|
||||||
stress: 1,
|
stress: 1,
|
||||||
attack: 1,
|
attack: 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
skulk: {
|
skulk: {
|
||||||
|
|
@ -648,7 +630,7 @@ export const adversaryScalingData = {
|
||||||
severeThreshold: 8,
|
severeThreshold: 8,
|
||||||
hp: 1,
|
hp: 1,
|
||||||
stress: 1,
|
stress: 1,
|
||||||
attack: 1,
|
attack: 1
|
||||||
},
|
},
|
||||||
3: {
|
3: {
|
||||||
difficulty: 2,
|
difficulty: 2,
|
||||||
|
|
@ -656,7 +638,7 @@ export const adversaryScalingData = {
|
||||||
severeThreshold: 12,
|
severeThreshold: 12,
|
||||||
hp: 1,
|
hp: 1,
|
||||||
stress: 1,
|
stress: 1,
|
||||||
attack: 1,
|
attack: 1
|
||||||
},
|
},
|
||||||
4: {
|
4: {
|
||||||
difficulty: 2,
|
difficulty: 2,
|
||||||
|
|
@ -664,7 +646,7 @@ export const adversaryScalingData = {
|
||||||
severeThreshold: 10,
|
severeThreshold: 10,
|
||||||
hp: 1,
|
hp: 1,
|
||||||
stress: 1,
|
stress: 1,
|
||||||
attack: 1,
|
attack: 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
solo: {
|
solo: {
|
||||||
|
|
@ -674,7 +656,7 @@ export const adversaryScalingData = {
|
||||||
severeThreshold: 10,
|
severeThreshold: 10,
|
||||||
hp: 0,
|
hp: 0,
|
||||||
stress: 1,
|
stress: 1,
|
||||||
attack: 2,
|
attack: 2
|
||||||
},
|
},
|
||||||
3: {
|
3: {
|
||||||
difficulty: 2,
|
difficulty: 2,
|
||||||
|
|
@ -682,7 +664,7 @@ export const adversaryScalingData = {
|
||||||
severeThreshold: 15,
|
severeThreshold: 15,
|
||||||
hp: 2,
|
hp: 2,
|
||||||
stress: 1,
|
stress: 1,
|
||||||
attack: 2,
|
attack: 2
|
||||||
},
|
},
|
||||||
4: {
|
4: {
|
||||||
difficulty: 2,
|
difficulty: 2,
|
||||||
|
|
@ -690,7 +672,7 @@ export const adversaryScalingData = {
|
||||||
severeThreshold: 25,
|
severeThreshold: 25,
|
||||||
hp: 0,
|
hp: 0,
|
||||||
stress: 1,
|
stress: 1,
|
||||||
attack: 3,
|
attack: 3
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
standard: {
|
standard: {
|
||||||
|
|
@ -700,7 +682,7 @@ export const adversaryScalingData = {
|
||||||
severeThreshold: 8,
|
severeThreshold: 8,
|
||||||
hp: 0,
|
hp: 0,
|
||||||
stress: 0,
|
stress: 0,
|
||||||
attack: 1,
|
attack: 1
|
||||||
},
|
},
|
||||||
3: {
|
3: {
|
||||||
difficulty: 2,
|
difficulty: 2,
|
||||||
|
|
@ -708,7 +690,7 @@ export const adversaryScalingData = {
|
||||||
severeThreshold: 15,
|
severeThreshold: 15,
|
||||||
hp: 1,
|
hp: 1,
|
||||||
stress: 1,
|
stress: 1,
|
||||||
attack: 1,
|
attack: 1
|
||||||
},
|
},
|
||||||
4: {
|
4: {
|
||||||
difficulty: 2,
|
difficulty: 2,
|
||||||
|
|
@ -716,7 +698,7 @@ export const adversaryScalingData = {
|
||||||
severeThreshold: 15,
|
severeThreshold: 15,
|
||||||
hp: 0,
|
hp: 0,
|
||||||
stress: 1,
|
stress: 1,
|
||||||
attack: 1,
|
attack: 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
support: {
|
support: {
|
||||||
|
|
@ -726,7 +708,7 @@ export const adversaryScalingData = {
|
||||||
severeThreshold: 8,
|
severeThreshold: 8,
|
||||||
hp: 1,
|
hp: 1,
|
||||||
stress: 1,
|
stress: 1,
|
||||||
attack: 1,
|
attack: 1
|
||||||
},
|
},
|
||||||
3: {
|
3: {
|
||||||
difficulty: 2,
|
difficulty: 2,
|
||||||
|
|
@ -734,7 +716,7 @@ export const adversaryScalingData = {
|
||||||
severeThreshold: 12,
|
severeThreshold: 12,
|
||||||
hp: 0,
|
hp: 0,
|
||||||
stress: 0,
|
stress: 0,
|
||||||
attack: 1,
|
attack: 1
|
||||||
},
|
},
|
||||||
4: {
|
4: {
|
||||||
difficulty: 2,
|
difficulty: 2,
|
||||||
|
|
@ -742,27 +724,27 @@ export const adversaryScalingData = {
|
||||||
severeThreshold: 10,
|
severeThreshold: 10,
|
||||||
hp: 1,
|
hp: 1,
|
||||||
stress: 1,
|
stress: 1,
|
||||||
attack: 1,
|
attack: 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scaling data used for an adversary's damage.
|
* Scaling data used for an adversary's damage.
|
||||||
* Tier 4 is missing certain adversary types and therefore skews upwards.
|
* Tier 4 is missing certain adversary types and therefore skews upwards.
|
||||||
* We manually set tier 4 data to hopefully lead to better results
|
* We manually set tier 4 data to hopefully lead to better results
|
||||||
*/
|
*/
|
||||||
export const adversaryExpectedDamage = {
|
export const adversaryExpectedDamage = {
|
||||||
basic: {
|
basic: {
|
||||||
1: { mean: 7.321428571428571, deviation: 1.962519002770912 },
|
1: { mean: 7.321428571428571, deviation: 1.962519002770912 },
|
||||||
2: { mean: 12.444444444444445, deviation: 2.0631069425529676 },
|
2: { mean: 12.444444444444445, deviation: 2.0631069425529676 },
|
||||||
3: { mean: 15.722222222222221, deviation: 2.486565208464823 },
|
3: { mean: 15.722222222222221, deviation: 2.486565208464823 },
|
||||||
4: { mean: 26, deviation: 5.2 }
|
4: { mean: 26, deviation: 5.2 }
|
||||||
},
|
},
|
||||||
minion: {
|
minion: {
|
||||||
1: { mean: 2.142857142857143, deviation: 1.0690449676496976 },
|
1: { mean: 2.142857142857143, deviation: 1.0690449676496976 },
|
||||||
2: { mean: 5, deviation: 0.816496580927726 },
|
2: { mean: 5, deviation: 0.816496580927726 },
|
||||||
3: { mean: 6.5, deviation: 2.1213203435596424 },
|
3: { mean: 6.5, deviation: 2.1213203435596424 },
|
||||||
4: { mean: 11, deviation: 1 }
|
4: { mean: 11, deviation: 1 }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -202,7 +202,8 @@ export const conditions = () => ({
|
||||||
id: 'vulnerable',
|
id: 'vulnerable',
|
||||||
name: 'DAGGERHEART.CONFIG.Condition.vulnerable.name',
|
name: 'DAGGERHEART.CONFIG.Condition.vulnerable.name',
|
||||||
img: 'icons/magic/control/silhouette-fall-slip-prone.webp',
|
img: 'icons/magic/control/silhouette-fall-slip-prone.webp',
|
||||||
description: 'DAGGERHEART.CONFIG.Condition.vulnerable.description'
|
description: 'DAGGERHEART.CONFIG.Condition.vulnerable.description',
|
||||||
|
autoApplyFlagId: 'auto-vulnerable'
|
||||||
},
|
},
|
||||||
hidden: {
|
hidden: {
|
||||||
id: 'hidden',
|
id: 'hidden',
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
export const hooksConfig = {
|
export const hooksConfig = {
|
||||||
effectDisplayToggle: 'DHEffectDisplayToggle'
|
effectDisplayToggle: 'DHEffectDisplayToggle',
|
||||||
|
lockedTooltipDismissed: 'DHLockedTooltipDismissed'
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -467,9 +467,7 @@ export const allArmorFeatures = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const orderedArmorFeatures = () => {
|
export const orderedArmorFeatures = () => {
|
||||||
const homebrewFeatures = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).itemFeatures
|
const allFeatures = allArmorFeatures();
|
||||||
.armorFeatures;
|
|
||||||
const allFeatures = { ...armorFeatures, ...homebrewFeatures };
|
|
||||||
const all = Object.keys(allFeatures).map(key => {
|
const all = Object.keys(allFeatures).map(key => {
|
||||||
const feature = allFeatures[key];
|
const feature = allFeatures[key];
|
||||||
return {
|
return {
|
||||||
|
|
@ -1404,9 +1402,7 @@ export const allWeaponFeatures = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const orderedWeaponFeatures = () => {
|
export const orderedWeaponFeatures = () => {
|
||||||
const homebrewFeatures = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).itemFeatures
|
const allFeatures = allWeaponFeatures();
|
||||||
.weaponFeatures;
|
|
||||||
const allFeatures = { ...weaponFeatures, ...homebrewFeatures };
|
|
||||||
const all = Object.keys(allFeatures).map(key => {
|
const all = Object.keys(allFeatures).map(key => {
|
||||||
const feature = allFeatures[key];
|
const feature = allFeatures[key];
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
88
module/config/resourceConfig.mjs
Normal file
88
module/config/resourceConfig.mjs
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
/**
|
||||||
|
* Full custom typing:
|
||||||
|
* id
|
||||||
|
* initial
|
||||||
|
* max
|
||||||
|
* reverse
|
||||||
|
* label
|
||||||
|
* images {
|
||||||
|
* full { value, isIcon, noColorFilter }
|
||||||
|
* empty { value, isIcon noColorFilter }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
|
const characterBaseResources = Object.freeze({
|
||||||
|
hitPoints: {
|
||||||
|
id: 'hitPoints',
|
||||||
|
initial: 0,
|
||||||
|
max: 0,
|
||||||
|
reverse: true,
|
||||||
|
label: 'DAGGERHEART.GENERAL.HitPoints.plural',
|
||||||
|
maxLabel: 'DAGGERHEART.ACTORS.Character.maxHPBonus'
|
||||||
|
},
|
||||||
|
stress: {
|
||||||
|
id: 'stress',
|
||||||
|
initial: 0,
|
||||||
|
max: 6,
|
||||||
|
reverse: true,
|
||||||
|
label: 'DAGGERHEART.GENERAL.stress'
|
||||||
|
},
|
||||||
|
hope: {
|
||||||
|
id: 'hope',
|
||||||
|
initial: 2,
|
||||||
|
reverse: false,
|
||||||
|
label: 'DAGGERHEART.GENERAL.hope'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const adversaryBaseResources = Object.freeze({
|
||||||
|
hitPoints: {
|
||||||
|
id: 'hitPoints',
|
||||||
|
initial: 0,
|
||||||
|
max: 0,
|
||||||
|
reverse: true,
|
||||||
|
label: 'DAGGERHEART.GENERAL.HitPoints.plural',
|
||||||
|
maxLabel: 'DAGGERHEART.ACTORS.Character.maxHPBonus'
|
||||||
|
},
|
||||||
|
stress: {
|
||||||
|
id: 'stress',
|
||||||
|
initial: 0,
|
||||||
|
max: 0,
|
||||||
|
reverse: true,
|
||||||
|
label: 'DAGGERHEART.GENERAL.stress'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const companionBaseResources = Object.freeze({
|
||||||
|
stress: {
|
||||||
|
id: 'stress',
|
||||||
|
initial: 0,
|
||||||
|
max: 0,
|
||||||
|
reverse: true,
|
||||||
|
label: 'DAGGERHEART.GENERAL.stress'
|
||||||
|
},
|
||||||
|
hope: {
|
||||||
|
id: 'hope',
|
||||||
|
initial: 0,
|
||||||
|
reverse: false,
|
||||||
|
label: 'DAGGERHEART.GENERAL.hope'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const character = {
|
||||||
|
base: characterBaseResources,
|
||||||
|
custom: {}, // module stuff goes here
|
||||||
|
all: { ...characterBaseResources },
|
||||||
|
};
|
||||||
|
|
||||||
|
export const adversary = {
|
||||||
|
base: adversaryBaseResources,
|
||||||
|
custom: {}, // module stuff goes here
|
||||||
|
all: { ...adversaryBaseResources },
|
||||||
|
};
|
||||||
|
|
||||||
|
export const companion = {
|
||||||
|
base: companionBaseResources,
|
||||||
|
custom: {}, // module stuff goes here
|
||||||
|
all: { ...companionBaseResources },
|
||||||
|
};
|
||||||
|
|
@ -3,6 +3,10 @@ export const menu = {
|
||||||
Name: 'GameSettingsAutomation',
|
Name: 'GameSettingsAutomation',
|
||||||
Icon: 'fa-solid fa-robot'
|
Icon: 'fa-solid fa-robot'
|
||||||
},
|
},
|
||||||
|
Metagaming: {
|
||||||
|
Name: 'GameSettingsMetagaming',
|
||||||
|
Icon: 'fa-solid fa-eye-low-vision'
|
||||||
|
},
|
||||||
Homebrew: {
|
Homebrew: {
|
||||||
Name: 'GameSettingsHomebrew',
|
Name: 'GameSettingsHomebrew',
|
||||||
Icon: 'fa-solid fa-flask-vial'
|
Icon: 'fa-solid fa-flask-vial'
|
||||||
|
|
@ -19,6 +23,7 @@ export const menu = {
|
||||||
|
|
||||||
export const gameSettings = {
|
export const gameSettings = {
|
||||||
Automation: 'Automation',
|
Automation: 'Automation',
|
||||||
|
Metagaming: 'Metagaming',
|
||||||
Homebrew: 'Homebrew',
|
Homebrew: 'Homebrew',
|
||||||
appearance: 'Appearance',
|
appearance: 'Appearance',
|
||||||
variantRules: 'VariantRules',
|
variantRules: 'VariantRules',
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import * as GENERAL from './generalConfig.mjs';
|
||||||
import * as DOMAIN from './domainConfig.mjs';
|
import * as DOMAIN from './domainConfig.mjs';
|
||||||
import * as ENCOUNTER from './encounterConfig.mjs';
|
import * as ENCOUNTER from './encounterConfig.mjs';
|
||||||
import * as ACTOR from './actorConfig.mjs';
|
import * as ACTOR from './actorConfig.mjs';
|
||||||
|
import * as RESOURCE from './resourceConfig.mjs';
|
||||||
import * as ITEM from './itemConfig.mjs';
|
import * as ITEM from './itemConfig.mjs';
|
||||||
import * as SETTINGS from './settingsConfig.mjs';
|
import * as SETTINGS from './settingsConfig.mjs';
|
||||||
import * as EFFECTS from './effectConfig.mjs';
|
import * as EFFECTS from './effectConfig.mjs';
|
||||||
|
|
@ -19,6 +20,7 @@ export const SYSTEM = {
|
||||||
GENERAL,
|
GENERAL,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
ACTOR,
|
ACTOR,
|
||||||
|
RESOURCE,
|
||||||
ITEM,
|
ITEM,
|
||||||
SETTINGS,
|
SETTINGS,
|
||||||
EFFECTS,
|
EFFECTS,
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import EffectAction from './effectAction.mjs';
|
||||||
import HealingAction from './healingAction.mjs';
|
import HealingAction from './healingAction.mjs';
|
||||||
import MacroAction from './macroAction.mjs';
|
import MacroAction from './macroAction.mjs';
|
||||||
import SummonAction from './summonAction.mjs';
|
import SummonAction from './summonAction.mjs';
|
||||||
|
import TransformAction from './transformAction.mjs';
|
||||||
|
|
||||||
export const actionsTypes = {
|
export const actionsTypes = {
|
||||||
base: BaseAction,
|
base: BaseAction,
|
||||||
|
|
@ -17,5 +18,6 @@ export const actionsTypes = {
|
||||||
summon: SummonAction,
|
summon: SummonAction,
|
||||||
effect: EffectAction,
|
effect: EffectAction,
|
||||||
macro: MacroAction,
|
macro: MacroAction,
|
||||||
beastform: BeastformAction
|
beastform: BeastformAction,
|
||||||
|
transform: TransformAction
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -197,7 +197,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
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 false;
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -224,7 +224,9 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute the Action Worflow in order based of schema fields
|
// Execute the Action Worflow in order based of schema fields
|
||||||
await this.executeWorkflow(config);
|
const result = await this.executeWorkflow(config);
|
||||||
|
if (result === false) return;
|
||||||
|
|
||||||
await config.resourceUpdates.updateResources();
|
await config.resourceUpdates.updateResources();
|
||||||
|
|
||||||
if (Hooks.call(`${CONFIG.DH.id}.postUseAction`, this, config) === false) return;
|
if (Hooks.call(`${CONFIG.DH.id}.postUseAction`, this, config) === false) return;
|
||||||
|
|
|
||||||
5
module/data/action/transformAction.mjs
Normal file
5
module/data/action/transformAction.mjs
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import DHBaseAction from './baseAction.mjs';
|
||||||
|
|
||||||
|
export default class DHTransformAction extends DHBaseAction {
|
||||||
|
static extraSchemas = [...super.extraSchemas, 'transform'];
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,7 @@ import DHAdversarySettings from '../../applications/sheets-configs/adversary-set
|
||||||
import { ActionField } from '../fields/actionField.mjs';
|
import { ActionField } from '../fields/actionField.mjs';
|
||||||
import { commonActorRules } from './base.mjs';
|
import { commonActorRules } from './base.mjs';
|
||||||
import DhCreature from './creature.mjs';
|
import DhCreature from './creature.mjs';
|
||||||
import { resourceField, bonusField } from '../fields/actorField.mjs';
|
import { bonusField } from '../fields/actorField.mjs';
|
||||||
import { calculateExpectedValue, parseTermsFromSimpleFormula } from '../../helpers/utils.mjs';
|
import { calculateExpectedValue, parseTermsFromSimpleFormula } from '../../helpers/utils.mjs';
|
||||||
import { adversaryExpectedDamage, adversaryScalingData } from '../../config/actorConfig.mjs';
|
import { adversaryExpectedDamage, adversaryScalingData } from '../../config/actorConfig.mjs';
|
||||||
|
|
||||||
|
|
@ -65,10 +65,6 @@ export default class DhpAdversary extends DhCreature {
|
||||||
label: 'DAGGERHEART.GENERAL.DamageThresholds.severeThreshold'
|
label: 'DAGGERHEART.GENERAL.DamageThresholds.severeThreshold'
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
resources: new fields.SchemaField({
|
|
||||||
hitPoints: resourceField(0, 0, 'DAGGERHEART.GENERAL.HitPoints.plural', true),
|
|
||||||
stress: resourceField(0, 0, 'DAGGERHEART.GENERAL.stress', true)
|
|
||||||
}),
|
|
||||||
rules: new fields.SchemaField({
|
rules: new fields.SchemaField({
|
||||||
...commonActorRules()
|
...commonActorRules()
|
||||||
}),
|
}),
|
||||||
|
|
@ -191,6 +187,7 @@ export default class DhpAdversary extends DhCreature {
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareDerivedData() {
|
prepareDerivedData() {
|
||||||
|
super.prepareDerivedData();
|
||||||
this.attack.roll.isStandardAttack = true;
|
this.attack.roll.isStandardAttack = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -213,7 +213,7 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
|
||||||
const textData = Object.keys(changes.system.resources).reduce((acc, key) => {
|
const textData = Object.keys(changes.system.resources).reduce((acc, key) => {
|
||||||
const resource = changes.system.resources[key];
|
const resource = changes.system.resources[key];
|
||||||
if (resource.value !== undefined && resource.value !== this.resources[key].value) {
|
if (resource.value !== undefined && resource.value !== this.resources[key].value) {
|
||||||
acc.push(getScrollTextData(this.resources, resource, key));
|
acc.push(getScrollTextData(this.parent, resource, key));
|
||||||
}
|
}
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
||||||
import DhLevelData from '../levelData.mjs';
|
import DhLevelData from '../levelData.mjs';
|
||||||
import { commonActorRules } from './base.mjs';
|
import { commonActorRules } from './base.mjs';
|
||||||
import DhCreature from './creature.mjs';
|
import DhCreature from './creature.mjs';
|
||||||
import { attributeField, resourceField, stressDamageReductionRule, bonusField } from '../fields/actorField.mjs';
|
import { attributeField, stressDamageReductionRule, bonusField } from '../fields/actorField.mjs';
|
||||||
import { ActionField } from '../fields/actionField.mjs';
|
import { ActionField } from '../fields/actionField.mjs';
|
||||||
import DHCharacterSettings from '../../applications/sheets-configs/character-settings.mjs';
|
import DHCharacterSettings from '../../applications/sheets-configs/character-settings.mjs';
|
||||||
|
|
||||||
|
|
@ -27,28 +27,6 @@ export default class DhCharacter extends DhCreature {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...super.defineSchema(),
|
...super.defineSchema(),
|
||||||
resources: new fields.SchemaField({
|
|
||||||
hitPoints: resourceField(
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
'DAGGERHEART.GENERAL.HitPoints.plural',
|
|
||||||
true,
|
|
||||||
'DAGGERHEART.ACTORS.Character.maxHPBonus'
|
|
||||||
),
|
|
||||||
stress: resourceField(6, 0, 'DAGGERHEART.GENERAL.stress', true),
|
|
||||||
hope: new fields.SchemaField(
|
|
||||||
{
|
|
||||||
value: new fields.NumberField({
|
|
||||||
initial: 2,
|
|
||||||
min: 0,
|
|
||||||
integer: true,
|
|
||||||
label: 'DAGGERHEART.GENERAL.hope'
|
|
||||||
}),
|
|
||||||
isReversed: new fields.BooleanField({ initial: false })
|
|
||||||
},
|
|
||||||
{ label: 'DAGGERHEART.GENERAL.hope' }
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
traits: new fields.SchemaField({
|
traits: new fields.SchemaField({
|
||||||
agility: attributeField('DAGGERHEART.CONFIG.Traits.agility.name'),
|
agility: attributeField('DAGGERHEART.CONFIG.Traits.agility.name'),
|
||||||
strength: attributeField('DAGGERHEART.CONFIG.Traits.strength.name'),
|
strength: attributeField('DAGGERHEART.CONFIG.Traits.strength.name'),
|
||||||
|
|
@ -609,6 +587,7 @@ export default class DhCharacter extends DhCreature {
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareBaseData() {
|
prepareBaseData() {
|
||||||
|
super.prepareBaseData();
|
||||||
this.evasion += this.class.value?.system?.evasion ?? 0;
|
this.evasion += this.class.value?.system?.evasion ?? 0;
|
||||||
|
|
||||||
const currentLevel = this.levelData.level.current;
|
const currentLevel = this.levelData.level.current;
|
||||||
|
|
@ -680,6 +659,7 @@ export default class DhCharacter extends DhCreature {
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareDerivedData() {
|
prepareDerivedData() {
|
||||||
|
super.prepareDerivedData();
|
||||||
let baseHope = this.resources.hope.value;
|
let baseHope = this.resources.hope.value;
|
||||||
if (this.companion) {
|
if (this.companion) {
|
||||||
for (let levelKey in this.companion.system.levelData.levelups) {
|
for (let levelKey in this.companion.system.levelData.levelups) {
|
||||||
|
|
@ -699,6 +679,7 @@ export default class DhCharacter extends DhCreature {
|
||||||
this.attack.roll.trait = this.rules.attack.roll.trait ?? this.attack.roll.trait;
|
this.attack.roll.trait = this.rules.attack.roll.trait ?? this.attack.roll.trait;
|
||||||
|
|
||||||
this.resources.armor = {
|
this.resources.armor = {
|
||||||
|
label: 'DAGGERHEART.GENERAL.armor',
|
||||||
value: this.armor?.system?.marks?.value ?? 0,
|
value: this.armor?.system?.marks?.value ?? 0,
|
||||||
max: this.armorScore,
|
max: this.armorScore,
|
||||||
isReversed: true
|
isReversed: true
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
||||||
import { ActionField } from '../fields/actionField.mjs';
|
import { ActionField } from '../fields/actionField.mjs';
|
||||||
import { adjustDice, adjustRange } from '../../helpers/utils.mjs';
|
import { adjustDice, adjustRange } from '../../helpers/utils.mjs';
|
||||||
import DHCompanionSettings from '../../applications/sheets-configs/companion-settings.mjs';
|
import DHCompanionSettings from '../../applications/sheets-configs/companion-settings.mjs';
|
||||||
import { resourceField, bonusField } from '../fields/actorField.mjs';
|
import { bonusField } from '../fields/actorField.mjs';
|
||||||
|
|
||||||
export default class DhCompanion extends DhCreature {
|
export default class DhCompanion extends DhCreature {
|
||||||
static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Companion'];
|
static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Companion'];
|
||||||
|
|
@ -26,10 +26,6 @@ export default class DhCompanion extends DhCreature {
|
||||||
return {
|
return {
|
||||||
...super.defineSchema(),
|
...super.defineSchema(),
|
||||||
partner: new ForeignDocumentUUIDField({ type: 'Actor' }),
|
partner: new ForeignDocumentUUIDField({ type: 'Actor' }),
|
||||||
resources: new fields.SchemaField({
|
|
||||||
stress: resourceField(3, 0, 'DAGGERHEART.GENERAL.stress', true),
|
|
||||||
hope: new fields.NumberField({ initial: 0, integer: true, label: 'DAGGERHEART.GENERAL.hope' })
|
|
||||||
}),
|
|
||||||
evasion: new fields.NumberField({
|
evasion: new fields.NumberField({
|
||||||
required: true,
|
required: true,
|
||||||
min: 1,
|
min: 1,
|
||||||
|
|
@ -127,6 +123,7 @@ export default class DhCompanion extends DhCreature {
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareBaseData() {
|
prepareBaseData() {
|
||||||
|
super.prepareBaseData();
|
||||||
this.attack.roll.bonus = this.partner?.system?.spellcastModifier ?? 0;
|
this.attack.roll.bonus = this.partner?.system?.spellcastModifier ?? 0;
|
||||||
|
|
||||||
for (let levelKey in this.levelData.levelups) {
|
for (let levelKey in this.levelData.levelups) {
|
||||||
|
|
@ -161,6 +158,7 @@ export default class DhCompanion extends DhCreature {
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareDerivedData() {
|
prepareDerivedData() {
|
||||||
|
super.prepareDerivedData();
|
||||||
/* Partner Related Setup */
|
/* Partner Related Setup */
|
||||||
if (this.partner) {
|
if (this.partner) {
|
||||||
this.levelData.level.changed = this.partner.system.levelData.level.current;
|
this.levelData.level.changed = this.partner.system.levelData.level.current;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { ResourcesField } from '../fields/actorField.mjs';
|
||||||
import BaseDataActor from './base.mjs';
|
import BaseDataActor from './base.mjs';
|
||||||
|
|
||||||
export default class DhCreature extends BaseDataActor {
|
export default class DhCreature extends BaseDataActor {
|
||||||
|
|
@ -7,6 +8,7 @@ export default class DhCreature extends BaseDataActor {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...super.defineSchema(),
|
...super.defineSchema(),
|
||||||
|
resources: new ResourcesField(this.metadata.type),
|
||||||
advantageSources: new fields.ArrayField(new fields.StringField(), {
|
advantageSources: new fields.ArrayField(new fields.StringField(), {
|
||||||
label: 'DAGGERHEART.ACTORS.Character.advantageSources.label',
|
label: 'DAGGERHEART.ACTORS.Character.advantageSources.label',
|
||||||
hint: 'DAGGERHEART.ACTORS.Character.advantageSources.hint'
|
hint: 'DAGGERHEART.ACTORS.Character.advantageSources.hint'
|
||||||
|
|
@ -17,4 +19,45 @@ export default class DhCreature extends BaseDataActor {
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isAutoVulnerableActive() {
|
||||||
|
const vulnerableAppliedByOther = this.parent.effects.some(
|
||||||
|
x => x.statuses.has('vulnerable') && !x.flags.daggerheart?.autoApplyFlagId
|
||||||
|
);
|
||||||
|
return !vulnerableAppliedByOther;
|
||||||
|
}
|
||||||
|
|
||||||
|
async _preUpdate(changes, options, userId) {
|
||||||
|
const allowed = await super._preUpdate(changes, options, userId);
|
||||||
|
if (allowed === false) return;
|
||||||
|
|
||||||
|
const automationSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation);
|
||||||
|
if (
|
||||||
|
automationSettings.vulnerableAutomation &&
|
||||||
|
this.parent.type !== 'companion' &&
|
||||||
|
changes.system?.resources?.stress?.value
|
||||||
|
) {
|
||||||
|
const { name, description, img, autoApplyFlagId } = CONFIG.DH.GENERAL.conditions().vulnerable;
|
||||||
|
const autoEffects = this.parent.effects.filter(
|
||||||
|
x => x.flags.daggerheart?.autoApplyFlagId === autoApplyFlagId
|
||||||
|
);
|
||||||
|
if (changes.system.resources.stress.value >= this.resources.stress.max) {
|
||||||
|
if (!autoEffects.length)
|
||||||
|
this.parent.createEmbeddedDocuments('ActiveEffect', [
|
||||||
|
{
|
||||||
|
name: game.i18n.localize(name),
|
||||||
|
description: game.i18n.localize(description),
|
||||||
|
img: img,
|
||||||
|
statuses: ['vulnerable'],
|
||||||
|
flags: { daggerheart: { autoApplyFlagId } }
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
} else if (this.resources.stress.value >= this.resources.stress.max) {
|
||||||
|
this.parent.deleteEmbeddedDocuments(
|
||||||
|
'ActiveEffect',
|
||||||
|
autoEffects.map(x => x.id)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,3 +10,4 @@ export { default as DamageField } from './damageField.mjs';
|
||||||
export { default as RollField } from './rollField.mjs';
|
export { default as RollField } from './rollField.mjs';
|
||||||
export { default as MacroField } from './macroField.mjs';
|
export { default as MacroField } from './macroField.mjs';
|
||||||
export { default as SummonField } from './summonField.mjs';
|
export { default as SummonField } from './summonField.mjs';
|
||||||
|
export { default as TransformField } from './transformField.mjs';
|
||||||
|
|
|
||||||
103
module/data/fields/action/transformField.mjs
Normal file
103
module/data/fields/action/transformField.mjs
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
|
export default class DHSummonField extends fields.SchemaField {
|
||||||
|
/**
|
||||||
|
* Action Workflow order
|
||||||
|
*/
|
||||||
|
static order = 130;
|
||||||
|
|
||||||
|
constructor(options = {}, context = {}) {
|
||||||
|
const transformFields = {
|
||||||
|
actorUUID: new fields.DocumentUUIDField({
|
||||||
|
type: 'Actor',
|
||||||
|
required: true
|
||||||
|
}),
|
||||||
|
resourceRefresh: new fields.SchemaField({
|
||||||
|
hitPoints: new fields.BooleanField({ initial: true }),
|
||||||
|
stress: new fields.BooleanField({ initial: true })
|
||||||
|
})
|
||||||
|
};
|
||||||
|
super(transformFields, options, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async execute() {
|
||||||
|
if (!this.transform.actorUUID) {
|
||||||
|
ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.transform.noTransformActor'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseActor = await foundry.utils.fromUuid(this.transform.actorUUID);
|
||||||
|
if (!baseActor) {
|
||||||
|
ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.transform.transformActorMissing'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!canvas.scene) {
|
||||||
|
ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.transform.canvasError'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.actor.prototypeToken.actorLink) {
|
||||||
|
ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.transform.actorLinkError'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.actor.token) {
|
||||||
|
ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.transform.prototypeError'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const actor = await DHSummonField.getWorldActor(baseActor);
|
||||||
|
const tokenSizes = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes;
|
||||||
|
const tokenSize = actor?.system.metadata.usesSize ? tokenSizes[actor.system.size] : actor.prototypeToken.width;
|
||||||
|
|
||||||
|
await this.actor.token.update(
|
||||||
|
{ ...actor.prototypeToken.toJSON(), actorId: actor.id, width: tokenSize, height: tokenSize },
|
||||||
|
{ diff: false, recursive: false, noHook: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this.actor.token.combatant) {
|
||||||
|
this.actor.token.combatant.update({ actorId: actor.id, img: actor.prototypeToken.texture.src });
|
||||||
|
}
|
||||||
|
|
||||||
|
const marks = { hitPoints: 0, stress: 0 };
|
||||||
|
if (!this.transform.resourceRefresh.hitPoints) {
|
||||||
|
marks.hitPoints = Math.min(
|
||||||
|
this.actor.system.resources.hitPoints.value,
|
||||||
|
this.actor.token.actor.system.resources.hitPoints.max - 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!this.transform.resourceRefresh.stress) {
|
||||||
|
marks.stress = Math.min(
|
||||||
|
this.actor.system.resources.stress.value,
|
||||||
|
this.actor.token.actor.system.resources.stress.max - 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (marks.hitPoints || marks.stress) {
|
||||||
|
this.actor.token.actor.update({
|
||||||
|
'system.resources': {
|
||||||
|
hitPoints: { value: marks.hitPoints },
|
||||||
|
stress: { value: marks.stress }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const prevPosition = { ...this.actor.sheet.position };
|
||||||
|
this.actor.sheet.close();
|
||||||
|
this.actor.token.actor.sheet.render({ force: true, position: prevPosition });
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for any available instances of the actor present in the world, or create a world actor based on compendium */
|
||||||
|
static async getWorldActor(baseActor) {
|
||||||
|
if (!baseActor.inCompendium) return baseActor;
|
||||||
|
|
||||||
|
const dataType = game.system.api.data.actors[`Dh${baseActor.type.capitalize()}`];
|
||||||
|
if (dataType && baseActor.img === dataType.DEFAULT_ICON) {
|
||||||
|
const worldActorCopy = game.actors.find(x => x.name === baseActor.name);
|
||||||
|
if (worldActorCopy) return worldActorCopy;
|
||||||
|
}
|
||||||
|
|
||||||
|
const worldActor = await game.system.api.documents.DhpActor.create(baseActor.toObject());
|
||||||
|
return worldActor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,22 +6,6 @@ const attributeField = label =>
|
||||||
tierMarked: new fields.BooleanField({ initial: false })
|
tierMarked: new fields.BooleanField({ initial: false })
|
||||||
});
|
});
|
||||||
|
|
||||||
const resourceField = (max = 0, initial = 0, label, reverse = false, maxLabel) =>
|
|
||||||
new fields.SchemaField(
|
|
||||||
{
|
|
||||||
value: new fields.NumberField({ initial: initial, min: 0, integer: true, label }),
|
|
||||||
max: new fields.NumberField({
|
|
||||||
initial: max,
|
|
||||||
integer: true,
|
|
||||||
label:
|
|
||||||
maxLabel ??
|
|
||||||
game.i18n.format('DAGGERHEART.GENERAL.maxWithThing', { thing: game.i18n.localize(label) })
|
|
||||||
}),
|
|
||||||
isReversed: new fields.BooleanField({ initial: reverse })
|
|
||||||
},
|
|
||||||
{ label }
|
|
||||||
);
|
|
||||||
|
|
||||||
const stressDamageReductionRule = localizationPath =>
|
const stressDamageReductionRule = localizationPath =>
|
||||||
new fields.SchemaField({
|
new fields.SchemaField({
|
||||||
cost: new fields.NumberField({
|
cost: new fields.NumberField({
|
||||||
|
|
@ -37,4 +21,67 @@ const bonusField = label =>
|
||||||
dice: new fields.ArrayField(new fields.StringField(), { label: `${game.i18n.localize(label)} Dice` })
|
dice: new fields.ArrayField(new fields.StringField(), { label: `${game.i18n.localize(label)} Dice` })
|
||||||
});
|
});
|
||||||
|
|
||||||
export { attributeField, resourceField, stressDamageReductionRule, bonusField };
|
/**
|
||||||
|
* Field used for actor resources. It is a resource that validates dynamically based on the config.
|
||||||
|
* Because "max" may be defined during runtime, we don't attempt to clamp the maximum value.
|
||||||
|
*/
|
||||||
|
class ResourcesField extends fields.TypedObjectField {
|
||||||
|
constructor(actorType) {
|
||||||
|
super(
|
||||||
|
new fields.SchemaField({
|
||||||
|
value: new fields.NumberField({ min: 0, initial: 0, integer: true }),
|
||||||
|
// Some resources allow changing max. A null max means its the default
|
||||||
|
max: new fields.NumberField({ initial: null, integer: true, nullable: true })
|
||||||
|
})
|
||||||
|
);
|
||||||
|
this.actorType = actorType;
|
||||||
|
}
|
||||||
|
|
||||||
|
getInitialValue() {
|
||||||
|
const resources = CONFIG.DH.RESOURCE[this.actorType].all;
|
||||||
|
return Object.values(resources).reduce((result, resource) => {
|
||||||
|
result[resource.id] = {
|
||||||
|
value: resource.initial,
|
||||||
|
max: null
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
_validateKey(key) {
|
||||||
|
return key in CONFIG.DH.RESOURCE[this.actorType].all;
|
||||||
|
}
|
||||||
|
|
||||||
|
_cleanType(value, options) {
|
||||||
|
value = super._cleanType(value, options);
|
||||||
|
|
||||||
|
// If not partial, ensure all data exists
|
||||||
|
if (!options.partial) {
|
||||||
|
value = foundry.utils.mergeObject(this.getInitialValue(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Initializes the original source data, returning prepared data */
|
||||||
|
initialize(...args) {
|
||||||
|
const data = super.initialize(...args);
|
||||||
|
const resources = CONFIG.DH.RESOURCE[this.actorType].all;
|
||||||
|
for (const [key, value] of Object.entries(data)) {
|
||||||
|
// TypedObjectField only calls _validateKey when persisting, so we also call it here
|
||||||
|
if (!this._validateKey(key)) {
|
||||||
|
delete value[key];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add basic prepared data.
|
||||||
|
const resource = resources[key];
|
||||||
|
value.label = resource.label;
|
||||||
|
value.isReversed = resources[key].reverse;
|
||||||
|
value.max = typeof resource.max === 'number' ? value.max ?? resource.max : null;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { attributeField, ResourcesField, stressDamageReductionRule, bonusField };
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,7 @@ export default class DHArmor extends AttachableItem {
|
||||||
armorFeatures: new fields.ArrayField(
|
armorFeatures: new fields.ArrayField(
|
||||||
new fields.SchemaField({
|
new fields.SchemaField({
|
||||||
value: new fields.StringField({
|
value: new fields.StringField({
|
||||||
required: true,
|
required: true
|
||||||
choices: CONFIG.DH.ITEM.allArmorFeatures,
|
|
||||||
blank: true
|
|
||||||
}),
|
}),
|
||||||
effectIds: new fields.ArrayField(new fields.StringField({ required: true })),
|
effectIds: new fields.ArrayField(new fields.StringField({ required: true })),
|
||||||
actionIds: new fields.ArrayField(new fields.StringField({ required: true }))
|
actionIds: new fields.ArrayField(new fields.StringField({ required: true }))
|
||||||
|
|
@ -58,7 +56,7 @@ export default class DHArmor extends AttachableItem {
|
||||||
async getDescriptionData() {
|
async getDescriptionData() {
|
||||||
const baseDescription = this.description;
|
const baseDescription = this.description;
|
||||||
const allFeatures = CONFIG.DH.ITEM.allArmorFeatures();
|
const allFeatures = CONFIG.DH.ITEM.allArmorFeatures();
|
||||||
const features = this.armorFeatures.map(x => allFeatures[x.value]);
|
const features = this.armorFeatures.map(x => allFeatures[x.value]).filter(x => x);
|
||||||
|
|
||||||
const prefix = await foundry.applications.handlebars.renderTemplate(
|
const prefix = await foundry.applications.handlebars.renderTemplate(
|
||||||
'systems/daggerheart/templates/sheets/items/armor/description.hbs',
|
'systems/daggerheart/templates/sheets/items/armor/description.hbs',
|
||||||
|
|
|
||||||
|
|
@ -224,7 +224,7 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
||||||
const armorChanged =
|
const armorChanged =
|
||||||
changed.system?.marks?.value !== undefined && changed.system.marks.value !== this.marks.value;
|
changed.system?.marks?.value !== undefined && changed.system.marks.value !== this.marks.value;
|
||||||
if (armorChanged && autoSettings.resourceScrollTexts && this.parent.parent?.type === 'character') {
|
if (armorChanged && autoSettings.resourceScrollTexts && this.parent.parent?.type === 'character') {
|
||||||
const armorData = getScrollTextData(this.parent.parent.system.resources, changed.system.marks, 'armor');
|
const armorData = getScrollTextData(this.parent.parent, changed.system.marks, 'armor');
|
||||||
options.scrollingTextData = [armorData];
|
options.scrollingTextData = [armorData];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,9 +38,7 @@ export default class DHWeapon extends AttachableItem {
|
||||||
weaponFeatures: new fields.ArrayField(
|
weaponFeatures: new fields.ArrayField(
|
||||||
new fields.SchemaField({
|
new fields.SchemaField({
|
||||||
value: new fields.StringField({
|
value: new fields.StringField({
|
||||||
required: true,
|
required: true
|
||||||
choices: CONFIG.DH.ITEM.allWeaponFeatures,
|
|
||||||
blank: true
|
|
||||||
}),
|
}),
|
||||||
effectIds: new fields.ArrayField(new fields.StringField({ required: true })),
|
effectIds: new fields.ArrayField(new fields.StringField({ required: true })),
|
||||||
actionIds: new fields.ArrayField(new fields.StringField({ required: true }))
|
actionIds: new fields.ArrayField(new fields.StringField({ required: true }))
|
||||||
|
|
@ -114,24 +112,14 @@ export default class DHWeapon extends AttachableItem {
|
||||||
async getDescriptionData() {
|
async getDescriptionData() {
|
||||||
const baseDescription = this.description;
|
const baseDescription = this.description;
|
||||||
|
|
||||||
const tier = game.i18n.localize(`DAGGERHEART.GENERAL.Tiers.${this.tier}`);
|
|
||||||
const trait = game.i18n.localize(CONFIG.DH.ACTOR.abilities[this.attack.roll.trait].label);
|
|
||||||
const range = game.i18n.localize(`DAGGERHEART.CONFIG.Range.${this.attack.range}.name`);
|
|
||||||
const damage = Roll.replaceFormulaData(this.attack.damageFormula, this.parent.parent ?? this.parent);
|
|
||||||
const burden = game.i18n.localize(CONFIG.DH.GENERAL.burden[this.burden].label);
|
|
||||||
|
|
||||||
const allFeatures = CONFIG.DH.ITEM.allWeaponFeatures();
|
const allFeatures = CONFIG.DH.ITEM.allWeaponFeatures();
|
||||||
const features = this.weaponFeatures.map(x => allFeatures[x.value]);
|
const features = this.weaponFeatures.map(x => allFeatures[x.value]).filter(x => x);
|
||||||
|
|
||||||
const prefix = await foundry.applications.handlebars.renderTemplate(
|
const prefix = await foundry.applications.handlebars.renderTemplate(
|
||||||
'systems/daggerheart/templates/sheets/items/weapon/description.hbs',
|
'systems/daggerheart/templates/sheets/items/weapon/description.hbs',
|
||||||
{
|
{
|
||||||
features,
|
item: this,
|
||||||
tier,
|
features
|
||||||
trait,
|
|
||||||
range,
|
|
||||||
damage,
|
|
||||||
burden
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,10 @@ export default class DhAutomation extends foundry.abstract.DataModel {
|
||||||
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.hopeFear.players.label'
|
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.hopeFear.players.label'
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
|
vulnerableAutomation: new fields.BooleanField({
|
||||||
|
initial: true,
|
||||||
|
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.vulnerableAutomation.label'
|
||||||
|
}),
|
||||||
countdownAutomation: new fields.BooleanField({
|
countdownAutomation: new fields.BooleanField({
|
||||||
required: true,
|
required: true,
|
||||||
initial: true,
|
initial: true,
|
||||||
|
|
|
||||||
|
|
@ -145,6 +145,16 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
|
||||||
description: new fields.StringField()
|
description: new fields.StringField()
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
|
resources: new fields.TypedObjectField(
|
||||||
|
new fields.SchemaField({
|
||||||
|
resources: new fields.TypedObjectField(new fields.EmbeddedDataField(Resource))
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
initial: {
|
||||||
|
character: { resources: {} }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
itemFeatures: new fields.SchemaField({
|
itemFeatures: new fields.SchemaField({
|
||||||
weaponFeatures: new fields.TypedObjectField(
|
weaponFeatures: new fields.TypedObjectField(
|
||||||
new fields.SchemaField({
|
new fields.SchemaField({
|
||||||
|
|
@ -185,4 +195,117 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
|
||||||
}
|
}
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Invoked by the setting when data changes */
|
||||||
|
handleChange() {
|
||||||
|
if (this.maxFear) {
|
||||||
|
if (ui.resources) ui.resources.render({ force: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.refreshConfig();
|
||||||
|
this.#resetActors();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Update config values based on homebrew data. Make sure the references don't change */
|
||||||
|
refreshConfig() {
|
||||||
|
for (const [actorType, actorData] of Object.entries(this.resources)) {
|
||||||
|
const config = CONFIG.DH.RESOURCE[actorType];
|
||||||
|
for (const key of Object.keys(config.all)) {
|
||||||
|
delete config.all[key];
|
||||||
|
}
|
||||||
|
Object.assign(config.all, {
|
||||||
|
...Object.entries(actorData.resources).reduce((result, [key, value]) => {
|
||||||
|
result[key] = value.toObject();
|
||||||
|
result[key].id = key;
|
||||||
|
return result;
|
||||||
|
}, {}),
|
||||||
|
...config.custom,
|
||||||
|
...config.base,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers a reset and non-forced re-render on all given actors (if given)
|
||||||
|
* or all world actors and actors in all scenes to show immediate results for a changed setting.
|
||||||
|
*/
|
||||||
|
#resetActors() {
|
||||||
|
const actors = new Set(
|
||||||
|
[
|
||||||
|
game.actors.contents,
|
||||||
|
game.scenes.contents.flatMap(s => s.tokens.contents).flatMap(t => t.actor ?? [])
|
||||||
|
].flat()
|
||||||
|
);
|
||||||
|
for (const actor of actors) {
|
||||||
|
for (const app of Object.values(actor.apps)) {
|
||||||
|
for (const element of app.element?.querySelectorAll('prose-mirror.active')) {
|
||||||
|
element.open = false; // This triggers a save
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
actor.reset();
|
||||||
|
actor.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class Resource extends foundry.abstract.DataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
return {
|
||||||
|
initial: new fields.NumberField({
|
||||||
|
required: true,
|
||||||
|
integer: true,
|
||||||
|
initial: 0,
|
||||||
|
min: 0,
|
||||||
|
label: 'DAGGERHEART.GENERAL.initial'
|
||||||
|
}),
|
||||||
|
max: new fields.NumberField({
|
||||||
|
nullable: true,
|
||||||
|
initial: null,
|
||||||
|
min: 0,
|
||||||
|
label: 'DAGGERHEART.GENERAL.max'
|
||||||
|
}),
|
||||||
|
label: new fields.StringField({ label: 'DAGGERHEART.GENERAL.label' }),
|
||||||
|
images: new fields.SchemaField({
|
||||||
|
full: imageIconField('fa solid fa-circle'),
|
||||||
|
empty: imageIconField('fa-regular fa-circle')
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static getDefaultResourceData = label => {
|
||||||
|
const images = Resource.schema.fields.images.getInitialValue();
|
||||||
|
return {
|
||||||
|
initial: 0,
|
||||||
|
max: 0,
|
||||||
|
label: label ?? '',
|
||||||
|
images
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
static getDefaultImageData = imageKey => {
|
||||||
|
return Resource.schema.fields.images.fields[imageKey].getInitialValue();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const imageIconField = defaultValue =>
|
||||||
|
new foundry.data.fields.SchemaField(
|
||||||
|
{
|
||||||
|
value: new foundry.data.fields.StringField({
|
||||||
|
initial: defaultValue,
|
||||||
|
label: 'DAGGERHEART.SETTINGS.Homebrew.FIELDS.resources.resources.value.label'
|
||||||
|
}),
|
||||||
|
isIcon: new foundry.data.fields.BooleanField({
|
||||||
|
required: true,
|
||||||
|
initial: true,
|
||||||
|
label: 'DAGGERHEART.SETTINGS.Homebrew.FIELDS.resources.resources.isIcon.label'
|
||||||
|
}),
|
||||||
|
noColorFilter: new foundry.data.fields.BooleanField({
|
||||||
|
required: true,
|
||||||
|
initial: false,
|
||||||
|
label: 'DAGGERHEART.SETTINGS.Homebrew.FIELDS.resources.resources.noColorFilter.label'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{ required: true }
|
||||||
|
);
|
||||||
|
|
|
||||||
12
module/data/settings/Metagaming.mjs
Normal file
12
module/data/settings/Metagaming.mjs
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
export default class DhMetagaming extends foundry.abstract.DataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
return {
|
||||||
|
hideObserverPermissionInChat: new fields.BooleanField({
|
||||||
|
initial: false,
|
||||||
|
label: 'DAGGERHEART.SETTINGS.Metagaming.FIELDS.hideObserverPermissionInChat.label',
|
||||||
|
hint: 'DAGGERHEART.SETTINGS.Metagaming.FIELDS.hideObserverPermissionInChat.hint'
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
export { default as DhAppearance } from './Appearance.mjs';
|
export { default as DhAppearance } from './Appearance.mjs';
|
||||||
export { default as DhAutomation } from './Automation.mjs';
|
export { default as DhAutomation } from './Automation.mjs';
|
||||||
export { default as DhHomebrew } from './Homebrew.mjs';
|
export { default as DhHomebrew } from './Homebrew.mjs';
|
||||||
|
export { default as DhMetagaming } from './Metagaming.mjs';
|
||||||
export { default as DhVariantRules } from './VariantRules.mjs';
|
export { default as DhVariantRules } from './VariantRules.mjs';
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import DamageDialog from '../applications/dialogs/damageDialog.mjs';
|
import DamageDialog from '../applications/dialogs/damageDialog.mjs';
|
||||||
|
import { parseRallyDice } from '../helpers/utils.mjs';
|
||||||
import { RefreshType, socketEvent } from '../systemRegistration/socket.mjs';
|
import { RefreshType, socketEvent } from '../systemRegistration/socket.mjs';
|
||||||
import DHRoll from './dhRoll.mjs';
|
import DHRoll from './dhRoll.mjs';
|
||||||
|
|
||||||
|
|
@ -33,7 +34,7 @@ export default class DamageRoll extends DHRoll {
|
||||||
static async buildPost(roll, config, message) {
|
static async buildPost(roll, config, message) {
|
||||||
const chatMessage = config.source?.message
|
const chatMessage = config.source?.message
|
||||||
? ui.chat.collection.get(config.source.message)
|
? ui.chat.collection.get(config.source.message)
|
||||||
: getDocumentClass('ChatMessage').applyRollMode({}, config.rollMode);
|
: getDocumentClass('ChatMessage').applyRollMode({}, config.rollMode ?? CONST.DICE_ROLL_MODES.PUBLIC);
|
||||||
if (game.modules.get('dice-so-nice')?.active) {
|
if (game.modules.get('dice-so-nice')?.active) {
|
||||||
const pool = foundry.dice.terms.PoolTerm.fromRolls(
|
const pool = foundry.dice.terms.PoolTerm.fromRolls(
|
||||||
Object.values(config.damage).flatMap(r => r.parts.map(p => p.roll))
|
Object.values(config.damage).flatMap(r => r.parts.map(p => p.roll))
|
||||||
|
|
@ -46,9 +47,14 @@ export default class DamageRoll extends DHRoll {
|
||||||
chatMessage.whisper?.length > 0 ? chatMessage.whisper : null,
|
chatMessage.whisper?.length > 0 ? chatMessage.whisper : null,
|
||||||
chatMessage.blind
|
chatMessage.blind
|
||||||
);
|
);
|
||||||
|
config.mute = true;
|
||||||
}
|
}
|
||||||
await super.buildPost(roll, config, message);
|
await super.buildPost(roll, config, message);
|
||||||
if (config.source?.message) chatMessage.update({ 'system.damage': config.damage });
|
if (config.source?.message) {
|
||||||
|
chatMessage.update({ 'system.damage': config.damage });
|
||||||
|
|
||||||
|
if (!game.modules.get('dice-so-nice')?.active) foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static unifyDamageRoll(rolls) {
|
static unifyDamageRoll(rolls) {
|
||||||
|
|
@ -192,7 +198,7 @@ export default class DamageRoll extends DHRoll {
|
||||||
// Bardic Rally
|
// Bardic Rally
|
||||||
const rallyChoices = config.data?.parent?.appliedEffects.reduce((a, c) => {
|
const rallyChoices = config.data?.parent?.appliedEffects.reduce((a, c) => {
|
||||||
const change = c.changes.find(ch => ch.key === 'system.bonuses.rally');
|
const change = c.changes.find(ch => ch.key === 'system.bonuses.rally');
|
||||||
if (change) a.push({ value: c.id, label: change.value });
|
if (change) a.push({ value: c.id, label: parseRallyDice(change.value, c) });
|
||||||
return a;
|
return a;
|
||||||
}, []);
|
}, []);
|
||||||
if (rallyChoices.length) {
|
if (rallyChoices.length) {
|
||||||
|
|
|
||||||
|
|
@ -143,8 +143,10 @@ export default class DHRoll extends Roll {
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
async render({ flavor, template = this.constructor.CHAT_TEMPLATE, isPrivate = false, ...options } = {}) {
|
async render({ flavor, template = this.constructor.CHAT_TEMPLATE, isPrivate = false, ...options } = {}) {
|
||||||
if (!this._evaluated) return;
|
if (!this._evaluated) return;
|
||||||
|
|
||||||
|
const metagamingSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Metagaming);
|
||||||
const chatData = await this._prepareChatRenderContext({ flavor, isPrivate, ...options });
|
const chatData = await this._prepareChatRenderContext({ flavor, isPrivate, ...options });
|
||||||
return foundry.applications.handlebars.renderTemplate(template, chatData);
|
return foundry.applications.handlebars.renderTemplate(template, { ...chatData, metagamingSettings });
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs';
|
import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs';
|
||||||
import D20Roll from './d20Roll.mjs';
|
import D20Roll from './d20Roll.mjs';
|
||||||
import { setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs';
|
import { parseRallyDice, setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs';
|
||||||
import { getDiceSoNicePresets } from '../config/generalConfig.mjs';
|
import { getDiceSoNicePresets } from '../config/generalConfig.mjs';
|
||||||
import { ResourceUpdateMap } from '../data/action/baseAction.mjs';
|
import { ResourceUpdateMap } from '../data/action/baseAction.mjs';
|
||||||
|
|
||||||
|
|
@ -68,7 +68,7 @@ export default class DualityRoll extends D20Roll {
|
||||||
setRallyChoices() {
|
setRallyChoices() {
|
||||||
return this.data?.parent?.appliedEffects.reduce((a, c) => {
|
return this.data?.parent?.appliedEffects.reduce((a, c) => {
|
||||||
const change = c.changes.find(ch => ch.key === 'system.bonuses.rally');
|
const change = c.changes.find(ch => ch.key === 'system.bonuses.rally');
|
||||||
if (change) a.push({ value: c.id, label: change.value });
|
if (change) a.push({ value: c.id, label: parseRallyDice(change.value, c) });
|
||||||
return a;
|
return a;
|
||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -959,10 +959,23 @@ export default class DhpActor extends Actor {
|
||||||
|
|
||||||
/** Get active effects */
|
/** Get active effects */
|
||||||
getActiveEffects() {
|
getActiveEffects() {
|
||||||
|
const conditions = CONFIG.DH.GENERAL.conditions();
|
||||||
const statusMap = new Map(foundry.CONFIG.statusEffects.map(status => [status.id, status]));
|
const statusMap = new Map(foundry.CONFIG.statusEffects.map(status => [status.id, status]));
|
||||||
|
const autoVulnerableActive = this.system.isAutoVulnerableActive;
|
||||||
return this.effects
|
return this.effects
|
||||||
.filter(x => !x.disabled)
|
.filter(x => !x.disabled)
|
||||||
.reduce((acc, effect) => {
|
.reduce((acc, effect) => {
|
||||||
|
/* Could be generalized if needed. Currently just related to Vulnerable */
|
||||||
|
const isAutoVulnerableEffect =
|
||||||
|
effect.flags.daggerheart?.autoApplyFlagId === conditions.vulnerable.autoApplyFlagId;
|
||||||
|
if (isAutoVulnerableEffect) {
|
||||||
|
if (!autoVulnerableActive) return acc;
|
||||||
|
|
||||||
|
effect.appliedBy = game.i18n.localize('DAGGERHEART.CONFIG.Condition.vulnerable.autoAppliedByLabel');
|
||||||
|
effect.isLockedCondition = true;
|
||||||
|
effect.condition = 'vulnerable';
|
||||||
|
}
|
||||||
|
|
||||||
acc.push(effect);
|
acc.push(effect);
|
||||||
|
|
||||||
const currentStatusActiveEffects = acc.filter(
|
const currentStatusActiveEffects = acc.filter(
|
||||||
|
|
|
||||||
|
|
@ -68,8 +68,11 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
||||||
document = fromUuidSync(uuid);
|
document = fromUuidSync(uuid);
|
||||||
if (!document) return;
|
if (!document) return;
|
||||||
|
|
||||||
e.setAttribute('data-view-perm', document.testUserPermission(game.user, 'OBSERVER'));
|
|
||||||
e.setAttribute('data-use-perm', document.testUserPermission(game.user, 'OWNER'));
|
e.setAttribute('data-use-perm', document.testUserPermission(game.user, 'OWNER'));
|
||||||
|
|
||||||
|
const settings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Metagaming);
|
||||||
|
if (settings.hideObserverPermissionInChat)
|
||||||
|
e.setAttribute('data-view-perm', document.testUserPermission(game.user, 'OBSERVER'));
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.isContentVisible) {
|
if (this.isContentVisible) {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { AdversaryBPPerEncounter, BaseBPPerEncounter } from '../config/encounter
|
||||||
export default class DhTooltipManager extends foundry.helpers.interaction.TooltipManager {
|
export default class DhTooltipManager extends foundry.helpers.interaction.TooltipManager {
|
||||||
#wide = false;
|
#wide = false;
|
||||||
#bordered = false;
|
#bordered = false;
|
||||||
|
#active = false;
|
||||||
|
|
||||||
async activate(element, options = {}) {
|
async activate(element, options = {}) {
|
||||||
const { TextEditor } = foundry.applications.ux;
|
const { TextEditor } = foundry.applications.ux;
|
||||||
|
|
@ -168,7 +169,100 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
super.activate(element, { ...options, html: html });
|
this.baseActivate(element, { ...options, html: html });
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Need to pass more options to _setAnchor, so have to copy whole foundry method >_< */
|
||||||
|
async baseActivate(element, options) {
|
||||||
|
let { text, direction, cssClass, locked = false, html, content } = options;
|
||||||
|
if (content && !html) {
|
||||||
|
foundry.utils.logCompatibilityWarning(
|
||||||
|
'The content option has been deprecated in favor of the html option',
|
||||||
|
{ since: 13, until: 15, once: true }
|
||||||
|
);
|
||||||
|
html = content;
|
||||||
|
}
|
||||||
|
if (text && html) throw new Error('Cannot provide both text and html options to TooltipManager#activate.');
|
||||||
|
// Deactivate currently active element
|
||||||
|
this.deactivate();
|
||||||
|
// Check if the element still exists in the DOM.
|
||||||
|
if (!document.body.contains(element)) return;
|
||||||
|
// Mark the new element as active
|
||||||
|
this.#active = true;
|
||||||
|
this.element = element;
|
||||||
|
element.setAttribute('aria-describedby', 'tooltip');
|
||||||
|
html ||= element.dataset.tooltipHtml;
|
||||||
|
if (html) {
|
||||||
|
if (typeof html === 'string') this.tooltip.innerHTML = foundry.utils.cleanHTML(html);
|
||||||
|
else {
|
||||||
|
this.tooltip.innerHTML = ''; // Clear existing HTML
|
||||||
|
this.tooltip.appendChild(html);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
text ||= element.dataset.tooltipText;
|
||||||
|
if (text) this.tooltip.textContent = text;
|
||||||
|
else {
|
||||||
|
text = element.dataset.tooltip;
|
||||||
|
// Localized message should be safe
|
||||||
|
if (game.i18n.has(text)) this.tooltip.innerHTML = game.i18n.localize(text);
|
||||||
|
else this.tooltip.innerHTML = foundry.utils.cleanHTML(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Activate display of the tooltip
|
||||||
|
this.tooltip.removeAttribute('class');
|
||||||
|
this.tooltip.classList.add('active', 'themed', 'theme-dark');
|
||||||
|
this.tooltip.showPopover();
|
||||||
|
cssClass ??= element.closest('[data-tooltip-class]')?.dataset.tooltipClass;
|
||||||
|
if (cssClass) this.tooltip.classList.add(...cssClass.split(' '));
|
||||||
|
|
||||||
|
// Set tooltip position
|
||||||
|
direction ??= element.closest('[data-tooltip-direction]')?.dataset.tooltipDirection;
|
||||||
|
if (!direction) direction = this._determineDirection();
|
||||||
|
this._setAnchor(direction, options);
|
||||||
|
|
||||||
|
if (locked || element.dataset.hasOwnProperty('locked')) this.lockTooltip();
|
||||||
|
}
|
||||||
|
|
||||||
|
_setAnchor(direction, options) {
|
||||||
|
const directions = this.constructor.TOOLTIP_DIRECTIONS;
|
||||||
|
const pad = this.constructor.TOOLTIP_MARGIN_PX;
|
||||||
|
const pos = this.element.getBoundingClientRect();
|
||||||
|
|
||||||
|
const { innerHeight, innerWidth } = this.tooltip.ownerDocument.defaultView;
|
||||||
|
const tooltipPadding = 16;
|
||||||
|
const horizontalOffset = options.noOffset ? tooltipPadding : this.tooltip.offsetWidth / 2 - pos.width / 2;
|
||||||
|
const verticalOffset = options.noOffset ? tooltipPadding : this.tooltip.offsetHeight / 2 - pos.height / 2;
|
||||||
|
|
||||||
|
const style = {};
|
||||||
|
switch (direction) {
|
||||||
|
case directions.DOWN:
|
||||||
|
style.textAlign = 'center';
|
||||||
|
style.left = pos.left - horizontalOffset;
|
||||||
|
style.top = pos.bottom + pad;
|
||||||
|
break;
|
||||||
|
case directions.LEFT:
|
||||||
|
style.textAlign = 'left';
|
||||||
|
style.right = innerWidth - pos.left + pad;
|
||||||
|
style.top = pos.top - verticalOffset;
|
||||||
|
break;
|
||||||
|
case directions.RIGHT:
|
||||||
|
style.textAlign = 'right';
|
||||||
|
style.left = pos.right + pad;
|
||||||
|
style.top = pos.top - verticalOffset;
|
||||||
|
break;
|
||||||
|
case directions.UP:
|
||||||
|
style.textAlign = 'center';
|
||||||
|
style.left = pos.left - horizontalOffset;
|
||||||
|
style.bottom = innerHeight - pos.top + pad;
|
||||||
|
break;
|
||||||
|
case directions.CENTER:
|
||||||
|
style.textAlign = 'center';
|
||||||
|
style.left = pos.left - horizontalOffset;
|
||||||
|
style.top = pos.top - verticalOffset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return this._setStyle(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
_determineItemTooltipDirection(element, prefered = this.constructor.TOOLTIP_DIRECTIONS.LEFT) {
|
_determineItemTooltipDirection(element, prefered = this.constructor.TOOLTIP_DIRECTIONS.LEFT) {
|
||||||
|
|
@ -270,6 +364,12 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**@inheritdoc */
|
||||||
|
dismissLockedTooltips() {
|
||||||
|
super.dismissLockedTooltips();
|
||||||
|
Hooks.callAll(CONFIG.DH.HOOKS.hooksConfig.lockedTooltipDismissed);
|
||||||
|
}
|
||||||
|
|
||||||
/** Get HTML for Battlepoints tooltip */
|
/** Get HTML for Battlepoints tooltip */
|
||||||
async getBattlepointHTML(combatId) {
|
async getBattlepointHTML(combatId) {
|
||||||
const combat = game.combats.get(combatId);
|
const combat = game.combats.get(combatId);
|
||||||
|
|
|
||||||
|
|
@ -119,8 +119,8 @@ export const tagifyElement = (element, baseOptions, onChange, tagifyOptions = {}
|
||||||
}),
|
}),
|
||||||
maxTags: typeof maxTags === 'function' ? maxTags() : maxTags,
|
maxTags: typeof maxTags === 'function' ? maxTags() : maxTags,
|
||||||
dropdown: {
|
dropdown: {
|
||||||
|
searchKeys: ['value', 'name'],
|
||||||
mapValueTo: 'name',
|
mapValueTo: 'name',
|
||||||
searchKeys: ['value'],
|
|
||||||
enabled: 0,
|
enabled: 0,
|
||||||
maxItems: 100,
|
maxItems: 100,
|
||||||
closeOnSelect: true,
|
closeOnSelect: true,
|
||||||
|
|
@ -378,17 +378,18 @@ export const arraysEqual = (a, b) =>
|
||||||
|
|
||||||
export const setsEqual = (a, b) => a.size === b.size && [...a].every(value => b.has(value));
|
export const setsEqual = (a, b) => a.size === b.size && [...a].every(value => b.has(value));
|
||||||
|
|
||||||
export function getScrollTextData(resources, resource, key) {
|
export function getScrollTextData(actor, resource, key) {
|
||||||
const { reversed, label } = CONFIG.DH.ACTOR.scrollingTextResource[key];
|
|
||||||
const { BOTTOM, TOP } = CONST.TEXT_ANCHOR_POINTS;
|
const { BOTTOM, TOP } = CONST.TEXT_ANCHOR_POINTS;
|
||||||
|
|
||||||
|
const resources = actor.system.resources;
|
||||||
const increased = resources[key].value < resource.value;
|
const increased = resources[key].value < resource.value;
|
||||||
const value = -1 * (resources[key].value - resource.value);
|
const value = -1 * (resources[key].value - resource.value);
|
||||||
|
const { label, isReversed } = resources[key];
|
||||||
|
|
||||||
const text = `${game.i18n.localize(label)} ${value.signedString()}`;
|
const text = `${game.i18n.localize(label)} ${value.signedString()}`;
|
||||||
|
const stroke = increased ? (isReversed ? 0xffffff : 0x000000) : isReversed ? 0x000000 : 0xffffff;
|
||||||
const stroke = increased ? (reversed ? 0xffffff : 0x000000) : reversed ? 0x000000 : 0xffffff;
|
const fill = increased ? (isReversed ? 0x0032b1 : 0xffe760) : isReversed ? 0xffe760 : 0x0032b1;
|
||||||
const fill = increased ? (reversed ? 0x0032b1 : 0xffe760) : reversed ? 0xffe760 : 0x0032b1;
|
const direction = increased ? (isReversed ? BOTTOM : TOP) : isReversed ? TOP : BOTTOM;
|
||||||
const direction = increased ? (reversed ? BOTTOM : TOP) : reversed ? TOP : BOTTOM;
|
|
||||||
|
|
||||||
return { text, stroke, fill, direction };
|
return { text, stroke, fill, direction };
|
||||||
}
|
}
|
||||||
|
|
@ -472,7 +473,7 @@ export function refreshIsAllowed(allowedTypes, typeToCheck) {
|
||||||
case CONFIG.DH.GENERAL.refreshTypes.scene.id:
|
case CONFIG.DH.GENERAL.refreshTypes.scene.id:
|
||||||
case CONFIG.DH.GENERAL.refreshTypes.session.id:
|
case CONFIG.DH.GENERAL.refreshTypes.session.id:
|
||||||
case CONFIG.DH.GENERAL.refreshTypes.longRest.id:
|
case CONFIG.DH.GENERAL.refreshTypes.longRest.id:
|
||||||
return allowedTypes.includes(typeToCheck);
|
return allowedTypes.includes?.(typeToCheck) ?? allowedTypes.has(typeToCheck);
|
||||||
case CONFIG.DH.GENERAL.refreshTypes.shortRest.id:
|
case CONFIG.DH.GENERAL.refreshTypes.shortRest.id:
|
||||||
return allowedTypes.some(
|
return allowedTypes.some(
|
||||||
x =>
|
x =>
|
||||||
|
|
@ -557,3 +558,121 @@ export function calculateExpectedValue(formulaOrTerms) {
|
||||||
: [formulaOrTerms];
|
: [formulaOrTerms];
|
||||||
return terms.reduce((r, t) => r + (t.bonus ?? 0) + (t.diceQuantity ? (t.diceQuantity * (t.faces + 1)) / 2 : 0), 0);
|
return terms.reduce((r, t) => r + (t.bonus ?? 0) + (t.diceQuantity ? (t.diceQuantity * (t.faces + 1)) / 2 : 0), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function parseRallyDice(value, effect) {
|
||||||
|
const legacyStartsWithPrefix = value.toLowerCase().startsWith('d');
|
||||||
|
const workingValue = legacyStartsWithPrefix ? value.slice(1) : value;
|
||||||
|
const dataParsedValue = itemAbleRollParse(workingValue, effect.parent);
|
||||||
|
|
||||||
|
return `d${game.system.api.documents.DhActiveEffect.effectSafeEval(dataParsedValue)}`;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Refreshes character and/or adversary resources.
|
||||||
|
* @param { string[] } refreshTypes Which type of features to refresh using IDs from CONFIG.DH.GENERAL.refreshTypes
|
||||||
|
* @param { string[] = ['character', 'adversary'] } actorTypes Which actor types should refresh their features. Defaults to character and adversary.
|
||||||
|
* @param { boolean = true } sendRefreshMessage If a chat message should be created detailing the refresh
|
||||||
|
* @return { Actor[] } The actors that had their features refreshed
|
||||||
|
*/
|
||||||
|
export async function RefreshFeatures(
|
||||||
|
refreshTypes = [],
|
||||||
|
actorTypes = ['character', 'adversary'],
|
||||||
|
sendNotificationMessage = true,
|
||||||
|
sendRefreshMessage = true
|
||||||
|
) {
|
||||||
|
const refreshedActors = {};
|
||||||
|
for (let actor of game.actors) {
|
||||||
|
if (actorTypes.includes(actor.type) && actor.prototypeToken.actorLink) {
|
||||||
|
const updates = {};
|
||||||
|
for (let item of actor.items) {
|
||||||
|
if (
|
||||||
|
item.system.metadata?.hasResource &&
|
||||||
|
refreshIsAllowed(refreshTypes, 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
|
||||||
|
: game.system.api.documents.DhActiveEffect.effectSafeEval(
|
||||||
|
Roll.replaceFormulaData(item.system.resource.max, actor.getRollData())
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (item.system.metadata?.hasActions) {
|
||||||
|
const usedTypes = new Set();
|
||||||
|
const actions = item.system.actions.filter(action => {
|
||||||
|
if (refreshIsAllowed(refreshTypes, action.uses.recovery)) {
|
||||||
|
usedTypes.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(
|
||||||
|
...usedTypes.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const types = refreshTypes.map(x => game.i18n.localize(CONFIG.DH.GENERAL.refreshTypes[x].label)).join(', ');
|
||||||
|
|
||||||
|
if (sendNotificationMessage) {
|
||||||
|
ui.notifications.info(
|
||||||
|
game.i18n.format('DAGGERHEART.UI.Notifications.gmMenuRefresh', {
|
||||||
|
types: `[${types}]`
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sendRefreshMessage) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
return refreshedActors;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,10 @@ export const preloadHandlebarsTemplates = async function () {
|
||||||
'systems/daggerheart/templates/sheets/global/partials/resource-section/dice-value.hbs',
|
'systems/daggerheart/templates/sheets/global/partials/resource-section/dice-value.hbs',
|
||||||
'systems/daggerheart/templates/sheets/global/partials/resource-section/die.hbs',
|
'systems/daggerheart/templates/sheets/global/partials/resource-section/die.hbs',
|
||||||
'systems/daggerheart/templates/sheets/global/partials/resource-bar.hbs',
|
'systems/daggerheart/templates/sheets/global/partials/resource-bar.hbs',
|
||||||
|
'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs',
|
||||||
|
'systems/daggerheart/templates/sheets/global/partials/item-tags.hbs',
|
||||||
'systems/daggerheart/templates/components/card-preview.hbs',
|
'systems/daggerheart/templates/components/card-preview.hbs',
|
||||||
'systems/daggerheart/templates/levelup/parts/selectable-card-preview.hbs',
|
'systems/daggerheart/templates/levelup/parts/selectable-card-preview.hbs',
|
||||||
'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs',
|
|
||||||
'systems/daggerheart/templates/ui/combatTracker/combatTrackerSection.hbs',
|
'systems/daggerheart/templates/ui/combatTracker/combatTrackerSection.hbs',
|
||||||
'systems/daggerheart/templates/actionTypes/damage.hbs',
|
'systems/daggerheart/templates/actionTypes/damage.hbs',
|
||||||
'systems/daggerheart/templates/actionTypes/resource.hbs',
|
'systems/daggerheart/templates/actionTypes/resource.hbs',
|
||||||
|
|
@ -33,6 +34,7 @@ export const preloadHandlebarsTemplates = async function () {
|
||||||
'systems/daggerheart/templates/actionTypes/beastform.hbs',
|
'systems/daggerheart/templates/actionTypes/beastform.hbs',
|
||||||
'systems/daggerheart/templates/actionTypes/countdown.hbs',
|
'systems/daggerheart/templates/actionTypes/countdown.hbs',
|
||||||
'systems/daggerheart/templates/actionTypes/summon.hbs',
|
'systems/daggerheart/templates/actionTypes/summon.hbs',
|
||||||
|
'systems/daggerheart/templates/actionTypes/transform.hbs',
|
||||||
'systems/daggerheart/templates/settings/components/settings-item-line.hbs',
|
'systems/daggerheart/templates/settings/components/settings-item-line.hbs',
|
||||||
'systems/daggerheart/templates/ui/tooltip/parts/tooltipChips.hbs',
|
'systems/daggerheart/templates/ui/tooltip/parts/tooltipChips.hbs',
|
||||||
'systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs',
|
'systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs',
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import { defaultLevelTiers, DhLevelTiers } from '../data/levelTier.mjs';
|
import { defaultLevelTiers, DhLevelTiers } from '../data/levelTier.mjs';
|
||||||
import DhCountdowns from '../data/countdowns.mjs';
|
import DhCountdowns from '../data/countdowns.mjs';
|
||||||
import { DhAppearance, DhAutomation, DhHomebrew, DhVariantRules } from '../data/settings/_module.mjs';
|
import { DhAppearance, DhAutomation, DhHomebrew, DhMetagaming, DhVariantRules } from '../data/settings/_module.mjs';
|
||||||
import {
|
import {
|
||||||
DhAppearanceSettings,
|
DhAppearanceSettings,
|
||||||
DhAutomationSettings,
|
DhAutomationSettings,
|
||||||
DhHomebrewSettings,
|
DhHomebrewSettings,
|
||||||
|
DhMetagamingSettings,
|
||||||
DhVariantRuleSettings
|
DhVariantRuleSettings
|
||||||
} from '../applications/settings/_module.mjs';
|
} from '../applications/settings/_module.mjs';
|
||||||
import { CompendiumBrowserSettings, DhTagTeamRoll } from '../data/_module.mjs';
|
import { CompendiumBrowserSettings, DhTagTeamRoll } from '../data/_module.mjs';
|
||||||
|
|
@ -38,17 +39,18 @@ const registerMenuSettings = () => {
|
||||||
type: DhAutomation
|
type: DhAutomation
|
||||||
});
|
});
|
||||||
|
|
||||||
|
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Metagaming, {
|
||||||
|
scope: 'world',
|
||||||
|
config: false,
|
||||||
|
type: DhMetagaming
|
||||||
|
});
|
||||||
|
|
||||||
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, {
|
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, {
|
||||||
scope: 'world',
|
scope: 'world',
|
||||||
config: false,
|
config: false,
|
||||||
type: DhHomebrew,
|
type: DhHomebrew,
|
||||||
onChange: value => {
|
onChange: value => {
|
||||||
if (value.maxFear) {
|
value.handleChange();
|
||||||
if (ui.resources) ui.resources.render({ force: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some homebrew settings may change sheets in various ways, so trigger a re-render
|
|
||||||
resetActors();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -76,6 +78,16 @@ const registerMenus = () => {
|
||||||
type: DhAutomationSettings,
|
type: DhAutomationSettings,
|
||||||
restricted: true
|
restricted: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
game.settings.registerMenu(CONFIG.DH.id, CONFIG.DH.SETTINGS.menu.Metagaming.Name, {
|
||||||
|
name: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.metagaming.name'),
|
||||||
|
label: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.metagaming.label'),
|
||||||
|
hint: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.metagaming.hint'),
|
||||||
|
icon: CONFIG.DH.SETTINGS.menu.Metagaming.Icon,
|
||||||
|
type: DhMetagamingSettings,
|
||||||
|
restricted: true
|
||||||
|
});
|
||||||
|
|
||||||
game.settings.registerMenu(CONFIG.DH.id, CONFIG.DH.SETTINGS.menu.Homebrew.Name, {
|
game.settings.registerMenu(CONFIG.DH.id, CONFIG.DH.SETTINGS.menu.Homebrew.Name, {
|
||||||
name: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.homebrew.name'),
|
name: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.homebrew.name'),
|
||||||
label: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.homebrew.label'),
|
label: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.homebrew.label'),
|
||||||
|
|
@ -144,30 +156,8 @@ const registerNonConfigSettings = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.CompendiumBrowserSettings, {
|
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.CompendiumBrowserSettings, {
|
||||||
scope: 'client',
|
scope: 'world',
|
||||||
config: false,
|
config: false,
|
||||||
type: CompendiumBrowserSettings
|
type: CompendiumBrowserSettings
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggers a reset and non-forced re-render on all given actors (if given)
|
|
||||||
* or all world actors and actors in all scenes to show immediate results for a changed setting.
|
|
||||||
*/
|
|
||||||
function resetActors(actors) {
|
|
||||||
actors ??= [
|
|
||||||
game.actors.contents,
|
|
||||||
game.scenes.contents.flatMap(s => s.tokens.contents).flatMap(t => t.actor ?? [])
|
|
||||||
].flat();
|
|
||||||
actors = new Set(actors);
|
|
||||||
for (const actor of actors) {
|
|
||||||
for (const app of Object.values(actor.apps)) {
|
|
||||||
for (const element of app.element?.querySelectorAll('prose-mirror.active')) {
|
|
||||||
element.open = false; // This triggers a save
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
actor.reset();
|
|
||||||
actor.render();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -717,7 +717,35 @@
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p>When the @Lookup[@name] marks their last HP, replace them with the @UUID[Compendium.daggerheart.adversaries.Actor.RXkZTwBRi4dJ3JE5]{Fallen Warlord: Undefeated Champion} and immediately spotlight them.</p>",
|
"description": "<p>When the @Lookup[@name] marks their last HP, replace them with the @UUID[Compendium.daggerheart.adversaries.Actor.RXkZTwBRi4dJ3JE5]{Fallen Warlord: Undefeated Champion} and immediately spotlight them.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {},
|
"actions": {
|
||||||
|
"gP426WmWbtrZEWCD": {
|
||||||
|
"type": "transform",
|
||||||
|
"_id": "gP426WmWbtrZEWCD",
|
||||||
|
"systemPath": "actions",
|
||||||
|
"baseAction": false,
|
||||||
|
"description": "",
|
||||||
|
"chatDisplay": true,
|
||||||
|
"originItem": {
|
||||||
|
"type": "itemCollection"
|
||||||
|
},
|
||||||
|
"actionType": "action",
|
||||||
|
"triggers": [],
|
||||||
|
"cost": [],
|
||||||
|
"uses": {
|
||||||
|
"value": null,
|
||||||
|
"max": null,
|
||||||
|
"recovery": null,
|
||||||
|
"consumeOnSuccess": false
|
||||||
|
},
|
||||||
|
"transform": {
|
||||||
|
"actorUUID": "Compendium.daggerheart.adversaries.Actor.RXkZTwBRi4dJ3JE5",
|
||||||
|
"resourceRefresh": {
|
||||||
|
"hitPoints": true,
|
||||||
|
"stress": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
"originId": null,
|
"originId": null,
|
||||||
"featureForm": "reaction"
|
"featureForm": "reaction"
|
||||||
|
|
|
||||||
|
|
@ -846,7 +846,37 @@
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p>When the @Lookup[@name] marks their last HP, replace them with the @UUID[Compendium.daggerheart.adversaries.Actor.pMuXGCSOQaxpi5tb]{Ashen Tyrant} and immediately spotlight them.</p>",
|
"description": "<p>When the @Lookup[@name] marks their last HP, replace them with the @UUID[Compendium.daggerheart.adversaries.Actor.pMuXGCSOQaxpi5tb]{Ashen Tyrant} and immediately spotlight them.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {},
|
"actions": {
|
||||||
|
"cFqFjemAfAjB0OB0": {
|
||||||
|
"type": "transform",
|
||||||
|
"_id": "cFqFjemAfAjB0OB0",
|
||||||
|
"systemPath": "actions",
|
||||||
|
"baseAction": false,
|
||||||
|
"description": "",
|
||||||
|
"chatDisplay": true,
|
||||||
|
"originItem": {
|
||||||
|
"type": "itemCollection"
|
||||||
|
},
|
||||||
|
"actionType": "action",
|
||||||
|
"triggers": [],
|
||||||
|
"cost": [],
|
||||||
|
"uses": {
|
||||||
|
"value": null,
|
||||||
|
"max": "",
|
||||||
|
"recovery": null,
|
||||||
|
"consumeOnSuccess": false
|
||||||
|
},
|
||||||
|
"transform": {
|
||||||
|
"actorUUID": "Compendium.daggerheart.adversaries.Actor.pMuXGCSOQaxpi5tb",
|
||||||
|
"resourceRefresh": {
|
||||||
|
"hitPoints": true,
|
||||||
|
"stress": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": "Transform",
|
||||||
|
"range": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
"originId": null,
|
"originId": null,
|
||||||
"featureForm": "reaction"
|
"featureForm": "reaction"
|
||||||
|
|
|
||||||
|
|
@ -742,7 +742,37 @@
|
||||||
"system": {
|
"system": {
|
||||||
"description": "<p>When the @Lookup[@name] marks their last HP, replace them with the @UUID[Compendium.daggerheart.adversaries.Actor.eArAPuB38CNR0ZIM]{Molten Scourge} and immediately spotlight them.</p>",
|
"description": "<p>When the @Lookup[@name] marks their last HP, replace them with the @UUID[Compendium.daggerheart.adversaries.Actor.eArAPuB38CNR0ZIM]{Molten Scourge} and immediately spotlight them.</p>",
|
||||||
"resource": null,
|
"resource": null,
|
||||||
"actions": {},
|
"actions": {
|
||||||
|
"OxGkCGgIl4vGFufD": {
|
||||||
|
"type": "transform",
|
||||||
|
"_id": "OxGkCGgIl4vGFufD",
|
||||||
|
"systemPath": "actions",
|
||||||
|
"baseAction": false,
|
||||||
|
"description": "",
|
||||||
|
"chatDisplay": true,
|
||||||
|
"originItem": {
|
||||||
|
"type": "itemCollection"
|
||||||
|
},
|
||||||
|
"actionType": "action",
|
||||||
|
"triggers": [],
|
||||||
|
"cost": [],
|
||||||
|
"uses": {
|
||||||
|
"value": null,
|
||||||
|
"max": "",
|
||||||
|
"recovery": null,
|
||||||
|
"consumeOnSuccess": false
|
||||||
|
},
|
||||||
|
"transform": {
|
||||||
|
"actorUUID": "Compendium.daggerheart.adversaries.Actor.eArAPuB38CNR0ZIM",
|
||||||
|
"resourceRefresh": {
|
||||||
|
"hitPoints": true,
|
||||||
|
"stress": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": "Transform",
|
||||||
|
"range": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
"originItemType": null,
|
"originItemType": null,
|
||||||
"originId": null,
|
"originId": null,
|
||||||
"featureForm": "reaction"
|
"featureForm": "reaction"
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,6 @@
|
||||||
{
|
{
|
||||||
"type": "class",
|
"type": "class",
|
||||||
"item": "Compendium.daggerheart.classes.Item.PydiMnNCKpd44SGS"
|
"item": "Compendium.daggerheart.classes.Item.PydiMnNCKpd44SGS"
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "class",
|
|
||||||
"item": "Compendium.daggerheart.classes.Item.TVeEyqmPPiRa2r3i"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"subclasses": [
|
"subclasses": [
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@
|
||||||
{
|
{
|
||||||
"key": "system.bonuses.rally",
|
"key": "system.bonuses.rally",
|
||||||
"mode": 2,
|
"mode": 2,
|
||||||
"value": "d6",
|
"value": "6 + min((floor(@system.levelData.level.current / 5)*2), 2)",
|
||||||
"priority": null
|
"priority": null
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
{
|
|
||||||
"folder": "C9y59fIkq50d3SyD",
|
|
||||||
"name": "Rally (Level 5)",
|
|
||||||
"type": "feature",
|
|
||||||
"img": "icons/tools/instruments/drum-hand-tan.webp",
|
|
||||||
"system": {
|
|
||||||
"description": "<p>Once per session, describe how you rally the party and give yourself and each of your allies a Rally Die. At level 1, your Rally Die is a d6. A PC can spend their Rally Die to roll it, adding the result to their action roll, reaction roll, damage roll, or to clear a number of Stress equal to the result. At the end of each session, clear all unspent Rally Dice. At level 5, your Rally Die increases to a d8.</p>",
|
|
||||||
"resource": null,
|
|
||||||
"actions": {
|
|
||||||
"Z1KWFrpXOqZWuZD1": {
|
|
||||||
"type": "effect",
|
|
||||||
"_id": "Z1KWFrpXOqZWuZD1",
|
|
||||||
"systemPath": "actions",
|
|
||||||
"description": "",
|
|
||||||
"chatDisplay": true,
|
|
||||||
"actionType": "action",
|
|
||||||
"cost": [],
|
|
||||||
"uses": {
|
|
||||||
"value": null,
|
|
||||||
"max": "1",
|
|
||||||
"recovery": "session"
|
|
||||||
},
|
|
||||||
"effects": [
|
|
||||||
{
|
|
||||||
"_id": "8CFxYJV8zE6Wabwj",
|
|
||||||
"onSave": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"target": {
|
|
||||||
"type": "any",
|
|
||||||
"amount": null
|
|
||||||
},
|
|
||||||
"name": "Rally your Allies",
|
|
||||||
"img": "icons/tools/instruments/drum-hand-tan.webp",
|
|
||||||
"range": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"originItemType": null,
|
|
||||||
"originId": null,
|
|
||||||
"attribution": {
|
|
||||||
"source": "Daggerheart SRD",
|
|
||||||
"page": 9,
|
|
||||||
"artist": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"effects": [
|
|
||||||
{
|
|
||||||
"name": "Rally (Level 5)",
|
|
||||||
"img": "icons/tools/instruments/drum-hand-tan.webp",
|
|
||||||
"origin": "Compendium.daggerheart.classes.Item.oxv0m8AFUQVFKtZ4",
|
|
||||||
"transfer": false,
|
|
||||||
"_id": "8CFxYJV8zE6Wabwj",
|
|
||||||
"type": "base",
|
|
||||||
"system": {
|
|
||||||
"rangeDependence": {
|
|
||||||
"enabled": false,
|
|
||||||
"type": "withinRange",
|
|
||||||
"target": "hostile",
|
|
||||||
"range": "melee"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"key": "system.bonuses.rally",
|
|
||||||
"mode": 2,
|
|
||||||
"value": "d8",
|
|
||||||
"priority": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"disabled": false,
|
|
||||||
"duration": {
|
|
||||||
"startTime": null,
|
|
||||||
"combat": null,
|
|
||||||
"seconds": null,
|
|
||||||
"rounds": null,
|
|
||||||
"turns": null,
|
|
||||||
"startRound": null,
|
|
||||||
"startTurn": null
|
|
||||||
},
|
|
||||||
"description": "",
|
|
||||||
"tint": "#ffffff",
|
|
||||||
"statuses": [],
|
|
||||||
"sort": 0,
|
|
||||||
"flags": {},
|
|
||||||
"_stats": {
|
|
||||||
"compendiumSource": null
|
|
||||||
},
|
|
||||||
"_key": "!items.effects!TVeEyqmPPiRa2r3i.8CFxYJV8zE6Wabwj"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"flags": {},
|
|
||||||
"ownership": {
|
|
||||||
"default": 0,
|
|
||||||
"LgnbNMLaxandgMQq": 3
|
|
||||||
},
|
|
||||||
"_id": "TVeEyqmPPiRa2r3i",
|
|
||||||
"sort": 300000,
|
|
||||||
"_key": "!items!TVeEyqmPPiRa2r3i"
|
|
||||||
}
|
|
||||||
|
|
@ -85,7 +85,7 @@
|
||||||
{
|
{
|
||||||
"trigger": "dualityRoll",
|
"trigger": "dualityRoll",
|
||||||
"triggeringActorType": "self",
|
"triggeringActorType": "self",
|
||||||
"command": "/* Ignore if it's a TagTeam roll */\nconst tagTeam = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll);\nif (tagTeam.members[actor.id]) return;\n\n/* Check if there's a Strange Pattern match */\nconst dice = [roll.dFear.total, roll.dHope.total];\nconst resource = this.parent.resource?.diceStates ? Object.values(this.parent.resource.diceStates).map(x => x.value)[0] : null;\nconst nrMatches = dice.filter(x => x === resource).length;\n\nif (!nrMatches) return;\n\n/* Create a dialog to choose Hope or Stress - or to cancel*/\nconst content = `\n <div><div>${game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerTexts.strangePatternsContentTitle', { nr: nrMatches })}</div>\n <div>${game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerTexts.strangePatternsContentSubTitle', { nr: nrMatches })}</div>\n <div class=\"flexrow\" style=\"gap: 8px;\">\n <button type=\"button\" id=\"hopeButton\">\n <i class=\"fa-solid fa-hands-holding\"></i>\n <label>0</label>\n </button>\n <button type=\"button\" id=\"stressButton\">\n <i class=\"fa-solid fa-bolt-lightning\"></i>\n <label>0</label>\n </button>\n </div>\n</div>`;\n\nconst result = await foundry.applications.api.DialogV2.input({\n classes: ['dh-style', 'two-big-buttons'],\n window: { title: this.item.name },\n content: content,\n render: (_, dialog) => {\n const hopeButton = dialog.element.querySelector('#hopeButton');\n const stressButton = dialog.element.querySelector('#stressButton');\ndialog.element.querySelector('button[type=\"submit\"]').disabled = true;\n \n const updateFunc = (event, selector, adding, clamp) => {\n const button = event.target.closest(`#${selector}Button`);\n const parent = event.target.closest('.flexrow');\n const hope = Number.parseInt(parent.querySelector('#hopeButton label').innerHTML);\n const stress = Number.parseInt(parent.querySelector('#stressButton label').innerHTML);\n const currentTotal = (Number.isNumeric(hope) ? hope : 0) + (Number.isNumeric(stress) ? stress : 0);\n if (adding && currentTotal === nrMatches) return;\n \n const current = Number.parseInt(button.querySelector('label').innerHTML);\n if (!adding && current === 0) return;\n \n const value = Number.isNumeric(current) ? adding ? current+1 : current-1 : 1;\n if (!dialog.data) dialog.data = {};\n dialog.data[selector] = clamp(value);\n button.querySelector('label').innerHTML = dialog.data[selector];\n\n event.target.closest('.dialog-form').querySelector('button[type=\"submit\"]').disabled = !adding || currentTotal < (nrMatches-1);\n \n };\n hopeButton.addEventListener('click', event => updateFunc(event, 'hope', true, x => Math.min(x, nrMatches)));\n hopeButton.addEventListener('contextmenu', event => updateFunc(event, 'hope', false, x => Math.max(x, 0)));\n stressButton.addEventListener('click', event => updateFunc(event, 'stress', true, x => Math.min(x, nrMatches)));\n stressButton.addEventListener('contextmenu', event => updateFunc(event, 'stress', false, x => Math.max(x, 0)));\n },\n ok: { callback: (_event, _result, dialog) => {\n const hope = dialog.data.hope ?? 0;\n const stress = dialog.data.stress ?? 0;\n if (!hope && !stress) return;\n\n /* Return resource update according to choices */\n const hopeUpdate = hope ? { key: 'hope', value: hope, total: -hope, enabled: true } : null;\n const stressUpdate = stress ? { key: 'stress', value: -stress, total: stress, enabled: true } : null;\n return { updates: [hopeUpdate, stressUpdate].filter(x => x) };\n }}\n});\n\nreturn result;"
|
"command": "/* Ignore if it's a TagTeam roll */\nconst tagTeam = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll);\nif (tagTeam.members[actor.id]) return;\n\n/* Check if there's a Strange Pattern match */\nconst dice = [roll.dFear.total, roll.dHope.total];\nconst resource = this.parent.resource?.diceStates ? Object.values(this.parent.resource.diceStates).map(x => x.value)[0] : null;\nconst nrMatches = dice.filter(x => x === resource).length;\n\nif (!nrMatches) return;\n\n/* Create a dialog to choose Hope or Stress - or to cancel*/\nconst content = `\n <div><div>${game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerTexts.strangePatternsContentTitle', { nr: nrMatches })}</div>\n <div>${game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerTexts.strangePatternsContentSubTitle', { nr: nrMatches })}</div>\n<div>${game.i18n.localize('DAGGERHEART.CONFIG.Triggers.triggerTexts.strangePatternsActionExplanation')}</div>\n <div class=\"flexrow\" style=\"gap: 8px;\">\n <button type=\"button\" id=\"hopeButton\">\n <i class=\"fa-solid fa-hands-holding\"></i>\n <label>0</label>\n </button>\n <button type=\"button\" id=\"stressButton\">\n <i class=\"fa-solid fa-bolt-lightning\"></i>\n <label>0</label>\n </button>\n </div>\n</div>`;\n\nconst result = await foundry.applications.api.DialogV2.input({\n classes: ['dh-style', 'two-big-buttons'],\n window: { title: this.item.name },\n content: content,\n render: (_, dialog) => {\n const hopeButton = dialog.element.querySelector('#hopeButton');\n const stressButton = dialog.element.querySelector('#stressButton');\ndialog.element.querySelector('button[type=\"submit\"]').disabled = true;\n \n const updateFunc = (event, selector, adding, clamp) => {\n const button = event.target.closest(`#${selector}Button`);\n const parent = event.target.closest('.flexrow');\n const hope = Number.parseInt(parent.querySelector('#hopeButton label').innerHTML);\n const stress = Number.parseInt(parent.querySelector('#stressButton label').innerHTML);\n const currentTotal = (Number.isNumeric(hope) ? hope : 0) + (Number.isNumeric(stress) ? stress : 0);\n if (adding && currentTotal === nrMatches) return;\n \n const current = Number.parseInt(button.querySelector('label').innerHTML);\n if (!adding && current === 0) return;\n \n const value = Number.isNumeric(current) ? adding ? current+1 : current-1 : 1;\n if (!dialog.data) dialog.data = {};\n dialog.data[selector] = clamp(value);\n button.querySelector('label').innerHTML = dialog.data[selector];\n\n event.target.closest('.dialog-form').querySelector('button[type=\"submit\"]').disabled = !adding || currentTotal < (nrMatches-1);\n \n };\n hopeButton.addEventListener('click', event => updateFunc(event, 'hope', true, x => Math.min(x, nrMatches)));\n hopeButton.addEventListener('contextmenu', event => updateFunc(event, 'hope', false, x => Math.max(x, 0)));\n stressButton.addEventListener('click', event => updateFunc(event, 'stress', true, x => Math.min(x, nrMatches)));\n stressButton.addEventListener('contextmenu', event => updateFunc(event, 'stress', false, x => Math.max(x, 0)));\n },\n ok: { callback: (_event, _result, dialog) => {\n const hope = dialog.data.hope ?? 0;\n const stress = dialog.data.stress ?? 0;\n if (!hope && !stress) return;\n\n /* Return resource update according to choices */\n const hopeUpdate = hope ? { key: 'hope', value: hope, total: -hope, enabled: true } : null;\n const stressUpdate = stress ? { key: 'stress', value: -stress, total: stress, enabled: true } : null;\n return { updates: [hopeUpdate, stressUpdate].filter(x => x) };\n }}\n});\n\nreturn result;"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@
|
||||||
"difficulty": null,
|
"difficulty": null,
|
||||||
"damageMod": "none"
|
"damageMod": "none"
|
||||||
},
|
},
|
||||||
"name": "Agility Check",
|
"name": "Agility Roll",
|
||||||
"img": "icons/skills/melee/sword-engraved-glow-purple.webp",
|
"img": "icons/skills/melee/sword-engraved-glow-purple.webp",
|
||||||
"range": "close"
|
"range": "close"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,24 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.item-tags {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.tag {
|
||||||
|
align-items: center;
|
||||||
|
background: light-dark(@dark-15, @beige-15);
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid light-dark(@dark, @beige);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
font-size: var(--font-size-12);
|
||||||
|
justify-content: start;
|
||||||
|
padding: 3px 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: Remove me when this issue is resolved https://github.com/foundryvtt/foundryvtt/issues/13734 */
|
/* TODO: Remove me when this issue is resolved https://github.com/foundryvtt/foundryvtt/issues/13734 */
|
||||||
|
|
|
||||||
|
|
@ -103,10 +103,9 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: end;
|
justify-content: end;
|
||||||
gap: 8px;
|
|
||||||
|
|
||||||
a {
|
a {
|
||||||
width: 15px;
|
width: 20px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -139,24 +138,6 @@
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-tags {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
|
|
||||||
.tag {
|
|
||||||
align-items: center;
|
|
||||||
background: light-dark(@dark-15, @beige-15);
|
|
||||||
border-radius: 3px;
|
|
||||||
border: 1px solid light-dark(@dark, @beige);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
font-size: var(--font-size-12);
|
|
||||||
justify-content: start;
|
|
||||||
padding: 3px 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-resource {
|
.item-resource {
|
||||||
|
|
@ -275,8 +256,10 @@
|
||||||
grid-area: controls;
|
grid-area: controls;
|
||||||
align-self: start;
|
align-self: start;
|
||||||
padding-top: 0.3125rem;
|
padding-top: 0.3125rem;
|
||||||
gap: 4px;
|
|
||||||
margin-bottom: -1px;
|
margin-bottom: -1px;
|
||||||
|
a {
|
||||||
|
width: 18px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
> .item-labels {
|
> .item-labels {
|
||||||
align-self: start;
|
align-self: start;
|
||||||
|
|
@ -334,6 +317,27 @@
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.recall-cost {
|
||||||
|
position: absolute;
|
||||||
|
right: 4px;
|
||||||
|
top: 4px;
|
||||||
|
width: 1.75em;
|
||||||
|
height: 1.75em;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
background: @dark-blue;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 1px solid @golden;
|
||||||
|
color: @golden;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 0.1em; // compensate for font
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 0.68em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.card-label {
|
.card-label {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
|
||||||
|
|
@ -4,48 +4,78 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.actor-summon-line {
|
.transform-container {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.transform-resources {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.transform-resource {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
|
||||||
|
.resource-title {
|
||||||
|
font-size: var(--font-size-18);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.actor-drop-line {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
border-radius: 3px;
|
||||||
|
|
||||||
|
.actor-drop-name {
|
||||||
|
flex: 2;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
border-radius: 3px;
|
|
||||||
|
|
||||||
.actor-summon-name {
|
img {
|
||||||
flex: 2;
|
height: 40px;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 5px;
|
|
||||||
|
|
||||||
img {
|
|
||||||
height: 40px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.actor-summon-controls {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 5px;
|
|
||||||
|
|
||||||
.controls {
|
|
||||||
display: flex;
|
|
||||||
gap: 5px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.summon-dragger {
|
.actor-drop-controls {
|
||||||
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
gap: 5px;
|
||||||
box-sizing: border-box;
|
|
||||||
height: 40px;
|
&.transform {
|
||||||
margin-top: 10px;
|
justify-content: flex-end;
|
||||||
border: 1px dashed light-dark(@dark-blue-50, @beige-50);
|
}
|
||||||
border-radius: 3px;
|
|
||||||
color: light-dark(@dark-blue-50, @beige-50);
|
.controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.actor-drop-hint {
|
||||||
|
flex: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.drop-dragger {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 40px;
|
||||||
|
margin-top: 10px;
|
||||||
|
border: 1px dashed light-dark(@dark-blue-50, @beige-50);
|
||||||
|
border-radius: 3px;
|
||||||
|
color: light-dark(@dark-blue-50, @beige-50);
|
||||||
}
|
}
|
||||||
|
|
||||||
.trigger-data {
|
.trigger-data {
|
||||||
|
|
|
||||||
|
|
@ -183,6 +183,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.domain-details {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
.level-details {
|
.level-details {
|
||||||
align-self: center;
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -133,8 +133,19 @@
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
|
|
||||||
.hope-section {
|
.resource-section {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
|
|
||||||
|
.resource-manager {
|
||||||
|
transition: all 0.1s ease;
|
||||||
|
|
||||||
|
&.inverted {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.downtime-section {
|
.downtime-section {
|
||||||
|
|
|
||||||
|
|
@ -158,7 +158,7 @@
|
||||||
.daggerheart,
|
.daggerheart,
|
||||||
#chat-notifications {
|
#chat-notifications {
|
||||||
.chat-message {
|
.chat-message {
|
||||||
--text-color: @golden;
|
--text-color: light-dark(@dark-blue, @golden);
|
||||||
--bg-color: @golden-40;
|
--bg-color: @golden-40;
|
||||||
|
|
||||||
[data-use-perm='false'] {
|
[data-use-perm='false'] {
|
||||||
|
|
@ -233,7 +233,7 @@
|
||||||
font-family: @font-subtitle;
|
font-family: @font-subtitle;
|
||||||
font-size: var(--font-size-18);
|
font-size: var(--font-size-18);
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: light-dark(@dark-blue, var(--text-color));
|
color: var(--text-color);
|
||||||
margin-bottom: -2px;
|
margin-bottom: -2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -450,6 +450,10 @@
|
||||||
|
|
||||||
.target-data {
|
.target-data {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
||||||
|
.target-name {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.target-save {
|
.target-save {
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@
|
||||||
@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 './settings/homebrew-settings/resources.less';
|
||||||
|
|
||||||
@import './sidebar/tabs.less';
|
@import './sidebar/tabs.less';
|
||||||
@import './sidebar/daggerheartMenu.less';
|
@import './sidebar/daggerheartMenu.less';
|
||||||
|
|
|
||||||
|
|
@ -304,7 +304,15 @@
|
||||||
padding: 0 0 0 50px;
|
padding: 0 0 0 50px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 5px;
|
|
||||||
|
.item-description-outer-container:has(div, p) {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Some items don't include an outer container, so we attempt a catch-all */
|
||||||
|
> *:last-child {
|
||||||
|
padding-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: var(--font-size-32);
|
font-size: var(--font-size-32);
|
||||||
|
|
@ -350,6 +358,7 @@
|
||||||
.filter-content,
|
.filter-content,
|
||||||
.item-desc {
|
.item-desc {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
opacity: 0;
|
||||||
grid-template-rows: 0fr;
|
grid-template-rows: 0fr;
|
||||||
transition: all 0.3s ease-in-out;
|
transition: all 0.3s ease-in-out;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
@ -378,8 +387,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.expanded + .extensible {
|
.expanded + .extensible {
|
||||||
|
opacity: 1;
|
||||||
grid-template-rows: 1fr;
|
grid-template-rows: 1fr;
|
||||||
padding-top: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.welcome-message {
|
.welcome-message {
|
||||||
|
|
|
||||||
87
styles/less/ui/settings/homebrew-settings/resources.less
Normal file
87
styles/less/ui/settings/homebrew-settings/resources.less
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
.daggerheart.dh-style.setting.homebrew-settings .resources.tab {
|
||||||
|
.resource-types-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
overflow: auto;
|
||||||
|
max-height: 570px;
|
||||||
|
|
||||||
|
fieldset legend {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resource-type-container {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.resources-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
.resource-container {
|
||||||
|
.resource-icons-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.resource-icon-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.resource-icon-title-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
&::before,
|
||||||
|
&::after {
|
||||||
|
color: @dark-blue;
|
||||||
|
content: '';
|
||||||
|
flex: 1;
|
||||||
|
height: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
background: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, @golden 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
background: linear-gradient(90deg, @golden 0%, rgba(0, 0, 0, 0) 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.resource-icon-title {
|
||||||
|
font-size: var(--font-size-16);
|
||||||
|
white-space: nowrap;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
color: light-dark(@dark-blue, @golden);
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.two-columns {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group.vertical {
|
||||||
|
> * {
|
||||||
|
flex: 0 0 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,3 +4,5 @@
|
||||||
@import './tooltip/domain-cards.less';
|
@import './tooltip/domain-cards.less';
|
||||||
|
|
||||||
@import './autocomplete/autocomplete.less';
|
@import './autocomplete/autocomplete.less';
|
||||||
|
|
||||||
|
@import './tooltip/resource-management.less';
|
||||||
|
|
|
||||||
56
styles/less/ux/tooltip/resource-management.less
Normal file
56
styles/less/ux/tooltip/resource-management.less
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
.bordered-tooltip.locked-tooltip .daggerheart.resource-management-container,
|
||||||
|
#tooltip .daggerheart.resource-management-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
.resource-section {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
background-color: light-dark(transparent, @dark-blue);
|
||||||
|
color: light-dark(@dark-blue, @golden);
|
||||||
|
padding: 5px 10px;
|
||||||
|
border: 1px solid light-dark(@dark-blue, @golden);
|
||||||
|
border-radius: 6px;
|
||||||
|
align-items: center;
|
||||||
|
width: fit-content;
|
||||||
|
height: 30px;
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-family: var(--dh-font-body, 'Montserrat'), sans-serif;
|
||||||
|
font-size: var(--font-size-14);
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: light-dark(@dark-blue, @golden);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resource-value {
|
||||||
|
display: flex;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
|
||||||
|
&.empty {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.filter {
|
||||||
|
filter: @golden-filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.non-transparent {
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 1px solid @golden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "daggerheart",
|
"id": "daggerheart",
|
||||||
"title": "Daggerheart",
|
"title": "Daggerheart",
|
||||||
"description": "An unofficial implementation of the Daggerheart system",
|
"description": "An unofficial implementation of the Daggerheart system",
|
||||||
"version": "1.7.2",
|
"version": "1.9.0",
|
||||||
"compatibility": {
|
"compatibility": {
|
||||||
"minimum": "13.346",
|
"minimum": "13.346",
|
||||||
"verified": "13.351",
|
"verified": "13.351",
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
<fieldset class="one-column" id="summon-drop-zone" data-key="summon">
|
<fieldset class="one-column" id="summon-drop-zone" data-is-drop-zone="true" data-key="summon">
|
||||||
<legend>
|
<legend>
|
||||||
{{localize "DAGGERHEART.ACTIONS.TYPES.summon.name"}}
|
{{localize "DAGGERHEART.ACTIONS.TYPES.summon.name"}}
|
||||||
</legend>
|
</legend>
|
||||||
|
|
||||||
<ul class="actor-summon-items">
|
<ul class="actor-summon-items">
|
||||||
{{#each @root.summons as |summon index|}}
|
{{#each @root.summons as |summon index|}}
|
||||||
<li class="actor-summon-line">
|
<li class="actor-drop-line">
|
||||||
<div class="actor-summon-name">
|
<div class="actor-drop-name">
|
||||||
<img class="image" src="{{summon.actor.img}}" />
|
<img class="image" src="{{summon.actor.img}}" />
|
||||||
<h4 class="h4">
|
<h4 class="h4">
|
||||||
{{summon.actor.name}}
|
{{summon.actor.name}}
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="actor-summon-controls">
|
<div class="actor-drop-controls">
|
||||||
<div class="form-group summon-count-wrapper" data-index="{{index}}">
|
<div class="form-group summon-count-wrapper" data-index="{{index}}">
|
||||||
<div class="form-fields">
|
<div class="form-fields">
|
||||||
<input type="text" value="{{summon.count}}" />
|
<input type="text" value="{{summon.count}}" />
|
||||||
|
|
@ -43,7 +43,7 @@
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
<div class="summon-dragger">
|
<div class="drop-dragger">
|
||||||
<span>{{localize "DAGGERHEART.ACTIONS.Settings.summon.dropSummonsHere"}}</span>
|
<span>{{localize "DAGGERHEART.ACTIONS.Settings.summon.dropSummonsHere"}}</span>
|
||||||
</div>
|
</div>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
||||||
62
templates/actionTypes/transform.hbs
Normal file
62
templates/actionTypes/transform.hbs
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
<fieldset class="one-column" id="transform-drop-zone" data-is-drop-zone="true" data-key="transform">
|
||||||
|
<legend>
|
||||||
|
{{localize "DAGGERHEART.ACTIONS.TYPES.transform.name"}}
|
||||||
|
</legend>
|
||||||
|
|
||||||
|
<div class="transform-container">
|
||||||
|
{{#if transform.actor}}
|
||||||
|
<div class="actor-drop-line">
|
||||||
|
{{#if transform.actor.error}}
|
||||||
|
<div class="hint actor-drop-hint">{{transform.actor.error}}</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="actor-drop-name">
|
||||||
|
<img class="image" src="{{transform.actor.img}}" />
|
||||||
|
<h4 class="h4">
|
||||||
|
{{transform.actor.name}}
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<div class="actor-drop-controls transform">
|
||||||
|
<div class="controls">
|
||||||
|
{{#unless transform.actor.error}}
|
||||||
|
<a
|
||||||
|
class='effect-control'
|
||||||
|
data-action='editDoc'
|
||||||
|
data-item-uuid="{{transform.actor.uuid}}"
|
||||||
|
data-tooltip='{{localize "DAGGERHEART.UI.Tooltip.openItemWorld"}}'
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-globe"></i>
|
||||||
|
</a>
|
||||||
|
{{/unless}}
|
||||||
|
<a
|
||||||
|
class='effect-control'
|
||||||
|
data-action='removeTransformActor'
|
||||||
|
data-tooltip='{{localize "CONTROLS.CommonDelete"}}'
|
||||||
|
>
|
||||||
|
<i class='fas fa-trash'></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<line-div></line-div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#unless transform.actor}}
|
||||||
|
<div class="drop-dragger">
|
||||||
|
<span>{{localize "DAGGERHEART.ACTIONS.Settings.transform.dropTransformHere"}}</span>
|
||||||
|
</div>
|
||||||
|
{{/unless}}
|
||||||
|
|
||||||
|
<div class="transform-resources">
|
||||||
|
<div class="transform-resource">
|
||||||
|
<input type="checkbox" data-resource="hitPoints" {{checked transform.resourceRefresh.hitPoints}}/>
|
||||||
|
<span class="resource-title">{{localize "DAGGERHEART.ACTIONS.Settings.transform.clearHitPoints"}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="transform-resource">
|
||||||
|
<input type="checkbox" data-resource="stress" {{checked transform.resourceRefresh.stress}}/>
|
||||||
|
<span class="resource-title">{{localize "DAGGERHEART.ACTIONS.Settings.transform.clearStress"}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
{{formGroup settingFields.schema.fields.summaryMessages.fields.effects value=settingFields._source.summaryMessages.effects localize=true}}
|
{{formGroup settingFields.schema.fields.summaryMessages.fields.effects value=settingFields._source.summaryMessages.effects localize=true}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{formGroup settingFields.schema.fields.vulnerableAutomation value=settingFields._source.vulnerableAutomation localize=true}}
|
||||||
{{formGroup settingFields.schema.fields.countdownAutomation value=settingFields._source.countdownAutomation localize=true}}
|
{{formGroup settingFields.schema.fields.countdownAutomation value=settingFields._source.countdownAutomation localize=true}}
|
||||||
{{formGroup settingFields.schema.fields.actionPoints value=settingFields._source.actionPoints localize=true}}
|
{{formGroup settingFields.schema.fields.actionPoints value=settingFields._source.actionPoints localize=true}}
|
||||||
{{formGroup settingFields.schema.fields.hordeDamage value=settingFields._source.hordeDamage localize=true}}
|
{{formGroup settingFields.schema.fields.hordeDamage value=settingFields._source.hordeDamage localize=true}}
|
||||||
|
|
|
||||||
78
templates/settings/homebrew-settings/resources.hbs
Normal file
78
templates/settings/homebrew-settings/resources.hbs
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
<section
|
||||||
|
class="tab {{tabs.resources.cssClass}} {{tabs.resources.id}} scrollable"
|
||||||
|
data-tab="{{tabs.resources.id}}"
|
||||||
|
data-group="{{tabs.resources.group}}"
|
||||||
|
>
|
||||||
|
<div class="resource-types-container">
|
||||||
|
{{#each settingFields.resources as |type key|}}
|
||||||
|
<fieldset>
|
||||||
|
<legend>
|
||||||
|
{{localize "DAGGERHEART.SETTINGS.Homebrew.resources.typeTitle" type=(localize (concat "TYPES.Actor." key))}}
|
||||||
|
<a data-action="addResource" data-actor-type="{{key}}"><i class="fa-solid fa-plus"></i></a>
|
||||||
|
</legend>
|
||||||
|
|
||||||
|
<div class="resource-type-container">
|
||||||
|
<div class="resources-container">
|
||||||
|
{{#each type.resources as |resource key|}}
|
||||||
|
<fieldset class="resource-container">
|
||||||
|
<legend>{{resource.label}}<a data-action="removeResource" data-actor-type="{{@../key}}" data-resource-key="{{key}}"><i class="fa-solid fa-trash"></i></a></legend>
|
||||||
|
|
||||||
|
{{formField @root.schemaFields.resources.element.fields.resources.element.fields.label value=resource.label name=(concat "resources." @../key ".resources." key ".label") classes="vertical" localize=true }}
|
||||||
|
|
||||||
|
<div class="two-columns even">
|
||||||
|
{{formField @root.schemaFields.resources.element.fields.resources.element.fields.initial value=resource.initial name=(concat "resources." @../key ".resources." key ".initial") classes="vertical" localize=true }}
|
||||||
|
{{formField @root.schemaFields.resources.element.fields.resources.element.fields.max value=resource.max name=(concat "resources." @../key ".resources." key ".max") classes="vertical" localize=true }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="resource-icons-container">
|
||||||
|
<div class="resource-icon-container" data-actor-type="{{@../key}}" data-resource-key="{{key}}" data-image-key="full">
|
||||||
|
{{#with @root.schemaFields.resources.element.fields.resources.element.fields.images.fields.full.fields}}
|
||||||
|
<div class="resource-icon-title-container">
|
||||||
|
<div class="resource-icon-title">
|
||||||
|
<span>{{localize "DAGGERHEART.SETTINGS.Homebrew.resources.filledIcon"}}</span>
|
||||||
|
<a data-action="resetResourceImage"><i class="fa-solid fa-arrow-rotate-left"></i></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="resource-icon-content">
|
||||||
|
{{#if ../images.full.isIcon}}
|
||||||
|
{{formGroup this.value value=../images.full.value name=(concat "resources." @../key ".resources." key ".images.full.value") localize=true }}
|
||||||
|
{{else}}
|
||||||
|
<div class="form-fields">
|
||||||
|
<file-picker name="{{concat "resources." @../key ".resources." key ".images.full.value"}}" value="{{../images.full.value}}" type="image"></file-picker>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{formGroup this.isIcon value=../images.full.isIcon name="" classes="path-field" localize=true }}
|
||||||
|
{{formGroup this.noColorFilter value=../images.full.noColorFilter name=(concat "resources." @../key ".resources." key ".images.full.noColorFilter") localize=true }}
|
||||||
|
</div>
|
||||||
|
{{/with}}
|
||||||
|
</div>
|
||||||
|
<div class="resource-icon-container" data-actor-type="{{@../key}}" data-resource-key="{{key}}" data-image-key="empty">
|
||||||
|
{{#with @root.schemaFields.resources.element.fields.resources.element.fields.images.fields.empty.fields}}
|
||||||
|
<div class="resource-icon-title-container">
|
||||||
|
<div class="resource-icon-title">
|
||||||
|
<span>{{localize "DAGGERHEART.SETTINGS.Homebrew.resources.emptyIcon"}}</span>
|
||||||
|
<a data-action="resetResourceImage"><i class="fa-solid fa-arrow-rotate-left"></i></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="resource-icon-content">
|
||||||
|
{{#if ../images.empty.isIcon}}
|
||||||
|
{{formGroup this.value value=../images.empty.value name=(concat "resources." @../key ".resources." key ".images.empty.value") localize=true }}
|
||||||
|
{{else}}
|
||||||
|
<div class="form-fields">
|
||||||
|
<file-picker name="{{concat "resources." @../key ".resources." key ".images.empty.value"}}" value="{{../images.empty.value}}" type="image"></file-picker>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{formGroup this.isIcon value=resource.images.empty.isIcon name="" classes="path-field" localize=true }}
|
||||||
|
{{formGroup this.noColorFilter value=resource.images.empty.noColorFilter name=(concat "resources." @../key ".resources." key ".images.empty.noColorFilter") localize=true }}
|
||||||
|
</div>
|
||||||
|
{{/with}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
10
templates/settings/metagaming-settings/footer.hbs
Normal file
10
templates/settings/metagaming-settings/footer.hbs
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<footer class="form-footer">
|
||||||
|
<button data-action="reset">
|
||||||
|
<i class="fa-solid fa-arrow-rotate-left"></i>
|
||||||
|
<span>{{localize "Reset"}}</span>
|
||||||
|
</button>
|
||||||
|
<button data-action="save" >
|
||||||
|
<i class="fa-solid fa-floppy-disk"></i>
|
||||||
|
<span>{{localize "Save Changes"}}</span>
|
||||||
|
</button>
|
||||||
|
</footer>
|
||||||
3
templates/settings/metagaming-settings/general.hbs
Normal file
3
templates/settings/metagaming-settings/general.hbs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<div>
|
||||||
|
{{formGroup settingFields.schema.fields.hideObserverPermissionInChat value=settingFields._source.hideObserverPermissionInChat localize=true}}
|
||||||
|
</div>
|
||||||
3
templates/settings/metagaming-settings/header.hbs
Normal file
3
templates/settings/metagaming-settings/header.hbs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<header class="dialog-header">
|
||||||
|
<h1>{{localize 'DAGGERHEART.SETTINGS.Menu.metagaming.name'}}</h1>
|
||||||
|
</header>
|
||||||
|
|
@ -11,4 +11,5 @@
|
||||||
{{#if fields.beastform}}{{> 'systems/daggerheart/templates/actionTypes/beastform.hbs' fields=fields.beastform.fields source=source.beastform}}{{/if}}
|
{{#if fields.beastform}}{{> 'systems/daggerheart/templates/actionTypes/beastform.hbs' fields=fields.beastform.fields source=source.beastform}}{{/if}}
|
||||||
{{#if fields.summon}}{{> 'systems/daggerheart/templates/actionTypes/summon.hbs' fields=fields.summon.element.fields source=source.summon}}{{/if}}
|
{{#if fields.summon}}{{> 'systems/daggerheart/templates/actionTypes/summon.hbs' fields=fields.summon.element.fields source=source.summon}}{{/if}}
|
||||||
{{#if fields.countdown}}{{> 'systems/daggerheart/templates/actionTypes/countdown.hbs' fields=fields.countdown.element.fields source=source.countdown}}{{/if}}
|
{{#if fields.countdown}}{{> 'systems/daggerheart/templates/actionTypes/countdown.hbs' fields=fields.countdown.element.fields source=source.countdown}}{{/if}}
|
||||||
|
{{#if fields.transform}}{{> 'systems/daggerheart/templates/actionTypes/transform.hbs' fields=fields.transform.fields source=source.transform}}{{/if}}
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -20,15 +20,11 @@
|
||||||
|
|
||||||
<div class="fieldsets-section">
|
<div class="fieldsets-section">
|
||||||
<fieldset class="flex">
|
<fieldset class="flex">
|
||||||
<legend>{{localize "DAGGERHEART.GENERAL.HitPoints.plural"}}</legend>
|
<legend>{{localize "DAGGERHEART.GENERAL.Resource.plural"}}</legend>
|
||||||
{{formGroup systemFields.resources.fields.hitPoints.fields.value value=document._source.system.resources.hitPoints.value label=(localize "DAGGERHEART.ACTORS.Adversary.FIELDS.resources.hitPoints.value.label")}}
|
{{#each resources as |resource|}}
|
||||||
{{formGroup systemFields.resources.fields.hitPoints.fields.max value=document._source.system.resources.hitPoints.max label=(localize "DAGGERHEART.ACTORS.Adversary.FIELDS.resources.hitPoints.max.label")}}
|
{{formGroup resource.field value=resource.value name=resource.name}}
|
||||||
</fieldset>
|
{{/each}}
|
||||||
<fieldset class="flex">
|
</fieldset>
|
||||||
<legend>{{localize "DAGGERHEART.GENERAL.stress"}}</legend>
|
|
||||||
{{formGroup systemFields.resources.fields.stress.fields.value value=document._source.system.resources.stress.value label=(localize "DAGGERHEART.ACTORS.Adversary.FIELDS.resources.stress.value.label")}}
|
|
||||||
{{formGroup systemFields.resources.fields.stress.fields.max value=document._source.system.resources.stress.max label=(localize "DAGGERHEART.ACTORS.Adversary.FIELDS.resources.stress.max.label")}}
|
|
||||||
</fieldset>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<fieldset class="flex">
|
<fieldset class="flex">
|
||||||
|
|
@ -36,4 +32,4 @@
|
||||||
{{formGroup systemFields.damageThresholds.fields.major value=document._source.system.damageThresholds.major label=(localize "DAGGERHEART.GENERAL.DamageThresholds.majorThreshold")}}
|
{{formGroup systemFields.damageThresholds.fields.major value=document._source.system.damageThresholds.major label=(localize "DAGGERHEART.GENERAL.DamageThresholds.majorThreshold")}}
|
||||||
{{formGroup systemFields.damageThresholds.fields.severe value=document._source.system.damageThresholds.severe label=(localize "DAGGERHEART.GENERAL.DamageThresholds.severeThreshold")}}
|
{{formGroup systemFields.damageThresholds.fields.severe value=document._source.system.damageThresholds.severe label=(localize "DAGGERHEART.GENERAL.DamageThresholds.severeThreshold")}}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -22,15 +22,12 @@
|
||||||
<legend>{{localize 'DAGGERHEART.GENERAL.basics'}}</legend>
|
<legend>{{localize 'DAGGERHEART.GENERAL.basics'}}</legend>
|
||||||
|
|
||||||
<div class="two-columns even">
|
<div class="two-columns even">
|
||||||
{{formGroup systemFields.resources.fields.hitPoints.fields.value value=document._source.system.resources.hitPoints.value localize=true}}
|
{{#each resources as |resource|}}
|
||||||
<span data-tooltip-text="{{localize "DAGGERHEART.UI.Tooltip.maxHPClassBound"}}">
|
<span {{#if resource.tooltip}}data-tooltip-text="{{resource.tooltip}}"{{/if}}>
|
||||||
{{formGroup systemFields.resources.fields.hitPoints.fields.max value=document._source.system.resources.hitPoints.max localize=true}}
|
{{formGroup resource.field value=resource.value name=resource.name}}
|
||||||
</span>
|
</span>
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
{{formGroup systemFields.resources.fields.stress.fields.value value=document._source.system.resources.stress.value localize=true}}
|
|
||||||
{{formGroup systemFields.resources.fields.stress.fields.max value=document._source.system.resources.stress.max localize=true}}
|
|
||||||
|
|
||||||
{{formGroup systemFields.resources.fields.hope.fields.value value=document._source.system.resources.hope.value localize=true}}
|
|
||||||
{{formGroup systemFields.scars value=document._source.system.scars localize=true}}
|
{{formGroup systemFields.scars value=document._source.system.scars localize=true}}
|
||||||
|
|
||||||
{{formGroup systemFields.proficiency value=document._source.system.proficiency localize=true}}
|
{{formGroup systemFields.proficiency value=document._source.system.proficiency localize=true}}
|
||||||
|
|
@ -39,4 +36,4 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,9 @@
|
||||||
<legend>{{localize 'DAGGERHEART.GENERAL.basics'}}</legend>
|
<legend>{{localize 'DAGGERHEART.GENERAL.basics'}}</legend>
|
||||||
<div class="nest-inputs">
|
<div class="nest-inputs">
|
||||||
{{formGroup systemFields.evasion value=document._source.system.evasion localize=true}}
|
{{formGroup systemFields.evasion value=document._source.system.evasion localize=true}}
|
||||||
{{formGroup systemFields.resources.fields.stress.fields.value value=document._source.system.resources.stress.value label='DAGGERHEART.ACTORS.Companion.FIELDS.resources.stress.currentStress.label' localize=true}}
|
{{#each resources as |resource|}}
|
||||||
{{formGroup systemFields.resources.fields.stress.fields.max value=document._source.system.resources.stress.max label='DAGGERHEART.ACTORS.Companion.FIELDS.resources.stress.maxStress.label' localize=true}}
|
{{formGroup resource.field value=resource.value name=resource.name}}
|
||||||
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="form-fields">
|
<div class="form-fields">
|
||||||
|
|
@ -19,4 +20,4 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -4,19 +4,21 @@
|
||||||
{{#each document.system.sheetLists as |category|}}
|
{{#each document.system.sheetLists as |category|}}
|
||||||
{{#if (eq category.type 'feature' )}}
|
{{#if (eq category.type 'feature' )}}
|
||||||
{{> 'daggerheart.inventory-items'
|
{{> 'daggerheart.inventory-items'
|
||||||
title=category.title
|
title=category.title
|
||||||
type='feature'
|
type='feature'
|
||||||
collection=category.values
|
actorType='character'
|
||||||
canCreate=true
|
collection=category.values
|
||||||
showActions=true
|
canCreate=true
|
||||||
|
showActions=true
|
||||||
}}
|
}}
|
||||||
{{else if category.values}}
|
{{else if category.values}}
|
||||||
{{> 'daggerheart.inventory-items'
|
{{> 'daggerheart.inventory-items'
|
||||||
title=category.title
|
title=category.title
|
||||||
type='feature'
|
type='feature'
|
||||||
collection=category.values
|
actorType='character'
|
||||||
canCreate=false
|
collection=category.values
|
||||||
showActions=true
|
canCreate=false
|
||||||
|
showActions=true
|
||||||
}}
|
}}
|
||||||
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
||||||
|
|
@ -65,22 +65,25 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="character-row">
|
<div class="character-row">
|
||||||
<div class="hope-section">
|
<div class="resource-section">
|
||||||
<h4>{{localize "DAGGERHEART.GENERAL.hope"}}</h4>
|
<div class="hope-section">
|
||||||
{{#times document.system.resources.hope.max}}
|
<h4>{{localize "DAGGERHEART.GENERAL.hope"}}</h4>
|
||||||
<span class='hope-value' data-action='toggleHope' data-value="{{add this 1}}">
|
{{#times document.system.resources.hope.max}}
|
||||||
{{#if (gte ../document.system.resources.hope.value (add this 1))}}
|
<span class='hope-value' data-action='toggleHope' data-value="{{add this 1}}">
|
||||||
<i class='fa-solid fa-diamond'></i>
|
{{#if (gte ../document.system.resources.hope.value (add this 1))}}
|
||||||
{{else}}
|
<i class='fa-solid fa-diamond'></i>
|
||||||
<i class='fa-regular fa-circle'></i>
|
{{else}}
|
||||||
{{/if}}
|
<i class='fa-regular fa-circle'></i>
|
||||||
</span>
|
{{/if}}
|
||||||
{{/times}}
|
</span>
|
||||||
{{#times document.system.scars}}
|
{{/times}}
|
||||||
<span class='hope-value scar'>
|
{{#times document.system.scars}}
|
||||||
<i class='fa-regular fa-ban'></i>
|
<span class='hope-value scar'>
|
||||||
</span>
|
<i class='fa-regular fa-ban'></i>
|
||||||
{{/times}}
|
</span>
|
||||||
|
{{/times}}
|
||||||
|
{{#if hasExtraResources}}<a type="button" class="resource-manager" data-action="toggleResourceManagement"><i class="fa-solid fa-angle-down"></i></a>{{/if}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{#if document.system.class.value}}
|
{{#if document.system.class.value}}
|
||||||
<div class="domains-section">
|
<div class="domains-section">
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
<li class="card-item" data-item-uuid="{{item.uuid}}" data-type="domainCard">
|
<li class="card-item" data-item-uuid="{{item.uuid}}" data-type="domainCard">
|
||||||
<img src="{{item.img}}" data-action="useItem" class="card-img" />
|
<img src="{{item.img}}" data-action="useItem" class="card-img" />
|
||||||
|
<span class="item-icon recall-cost">
|
||||||
|
<span class="recall-value">{{item.system.recallCost}}</span>
|
||||||
|
<i class="fa-solid fa-bolt"></i>
|
||||||
|
</span>
|
||||||
<div class="card-label">
|
<div class="card-label">
|
||||||
<div
|
<div
|
||||||
class="menu {{#if item.system.resource}}resource-menu{{/if}} {{#if (eq item.system.resource.type 'diceValue')}}dice-menu{{/if}}">
|
class="menu {{#if item.system.resource}}resource-menu{{/if}} {{#if (eq item.system.resource.type 'diceValue')}}dice-menu{{/if}}">
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ Parameters:
|
||||||
<legend>
|
<legend>
|
||||||
{{localize title}}
|
{{localize title}}
|
||||||
{{#if canCreate}}
|
{{#if canCreate}}
|
||||||
<a data-action="{{ifThen (or (eq type 'effect') (eq type 'feature') (eq type 'action')) 'createDoc' 'addNewItem' }}" data-document-class="{{ifThen (eq type 'effect') 'ActiveEffect' 'Item' }}"
|
<a data-action="{{ifThen (or (eq type 'effect') (and (eq type 'feature') (not (eq actorType 'character'))) (eq type 'action')) 'createDoc' 'addNewItem' }}" data-document-class="{{ifThen (eq type 'effect') 'ActiveEffect' 'Item' }}"
|
||||||
data-type="{{ifThen (eq type 'effect') 'base' type}}"
|
data-type="{{ifThen (eq type 'effect') 'base' type}}"
|
||||||
{{#if inVault}}data-in-vault="{{inVault}}"{{/if}}
|
{{#if inVault}}data-in-vault="{{inVault}}"{{/if}}
|
||||||
{{#if disabled}} data-disabled="{{disabled}}"{{/if}}
|
{{#if disabled}} data-disabled="{{disabled}}"{{/if}}
|
||||||
|
|
|
||||||
|
|
@ -45,30 +45,20 @@ Parameters:
|
||||||
<span class="item-name">{{localize item.name}} {{#unless (or noExtensible (not item.system.description))}}<span class="expanded-icon"><i class="fa-solid fa-expand"></i></span>{{/unless}}</span>
|
<span class="item-name">{{localize item.name}} {{#unless (or noExtensible (not item.system.description))}}<span class="expanded-icon"><i class="fa-solid fa-expand"></i></span>{{/unless}}</span>
|
||||||
|
|
||||||
{{!-- Tags Start --}}
|
{{!-- Tags Start --}}
|
||||||
{{#with item}}
|
{{#if (not ../hideTags)}}
|
||||||
{{#if (not ../hideTags)}}
|
{{#> "systems/daggerheart/templates/sheets/global/partials/item-tags.hbs" item }}
|
||||||
<div class="item-tags">
|
{{#if (eq ../type 'feature')}}
|
||||||
{{#each this._getTags as |tag|}}
|
{{#if (or (eq @root.document.type 'adversary') (eq @root.document.type 'environment'))}}
|
||||||
<div class="tag">
|
{{#if system.featureForm}}
|
||||||
{{tag}}
|
<div class="tag feature-form">
|
||||||
</div>
|
<span class="recall-value">{{localize (concat "DAGGERHEART.CONFIG.FeatureForm." system.featureForm)}}</span>
|
||||||
{{/each}}
|
</div>
|
||||||
|
{{/if}}
|
||||||
{{!-- Feature Form Tag Start --}}
|
|
||||||
{{#if (eq ../type 'feature')}}
|
|
||||||
{{#if (or (eq @root.document.type 'adversary') (eq @root.document.type 'environment'))}}
|
|
||||||
{{#if system.featureForm}}
|
|
||||||
<div class="tag feature-form">
|
|
||||||
<span class="recall-value">{{localize (concat "DAGGERHEART.CONFIG.FeatureForm." system.featureForm)}}</span>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{/ "systems/daggerheart/templates/sheets/global/partials/item-tags.hbs"}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{!-- Feature Form Tag End --}}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{/with}}
|
|
||||||
{{!--Tags End --}}
|
{{!--Tags End --}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -109,7 +99,7 @@ Parameters:
|
||||||
{{else if (eq type 'armor')}}
|
{{else if (eq type 'armor')}}
|
||||||
<a class="{{#unless item.system.equipped}}unequipped{{/unless}}" data-action="toggleEquipItem"
|
<a class="{{#unless item.system.equipped}}unequipped{{/unless}}" data-action="toggleEquipItem"
|
||||||
data-tooltip="DAGGERHEART.UI.Tooltip.{{ifThen item.system.equipped 'unequip' 'equip' }}">
|
data-tooltip="DAGGERHEART.UI.Tooltip.{{ifThen item.system.equipped 'unequip' 'equip' }}">
|
||||||
<i class="fa-solid fa-shield"></i>
|
<i class="fa-solid fa-fw fa-shield"></i>
|
||||||
</a>
|
</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if (eq type 'domainCard')}}
|
{{#if (eq type 'domainCard')}}
|
||||||
|
|
@ -125,7 +115,7 @@ Parameters:
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if (hasProperty item "toChat")}}
|
{{#if (hasProperty item "toChat")}}
|
||||||
<a data-action="toChat" data-tooltip="DAGGERHEART.UI.Tooltip.sendToChat">
|
<a data-action="toChat" data-tooltip="DAGGERHEART.UI.Tooltip.sendToChat">
|
||||||
<i class="fa-regular fa-message"></i>
|
<i class="fa-regular fa-fw fa-message"></i>
|
||||||
</a>
|
</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{else}}
|
{{else}}
|
||||||
|
|
@ -138,7 +128,7 @@ Parameters:
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
{{#unless hideContextMenu}}
|
{{#unless hideContextMenu}}
|
||||||
<a data-action="triggerContextMenu" data-tooltip="DAGGERHEART.UI.Tooltip.moreOptions">
|
<a data-action="triggerContextMenu" data-tooltip="DAGGERHEART.UI.Tooltip.moreOptions">
|
||||||
<i class="fa-solid fa-ellipsis-vertical"></i>
|
<i class="fa-solid fa-fw fa-ellipsis-vertical"></i>
|
||||||
</a>
|
</a>
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
||||||
8
templates/sheets/global/partials/item-tags.hbs
Normal file
8
templates/sheets/global/partials/item-tags.hbs
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
<div class="item-tags">
|
||||||
|
{{#each _getTags as |tag|}}
|
||||||
|
<div class="tag">
|
||||||
|
{{tag}}
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
{{#if @partial-block}}{{> @partial-block}}{{/if}}
|
||||||
|
</div>
|
||||||
|
|
@ -1,19 +1,6 @@
|
||||||
<div class="item-description-outer-container">
|
<div class="item-description-outer-container">
|
||||||
<div class="two-columns">
|
|
||||||
<div class="item-description-container">
|
|
||||||
<h4>{{localize "DAGGERHEART.ITEMS.Armor.baseThresholds.base"}}</h4>
|
|
||||||
<span>{{item.system.baseThresholds.major}}/{{item.system.baseThresholds.severe}}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="item-description-container">
|
|
||||||
<h4>{{localize "DAGGERHEART.ITEMS.Armor.baseScore"}}</h4>
|
|
||||||
<span>{{item.system.baseScore}}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#if features.length}}
|
{{#if features.length}}
|
||||||
<div class="item-description-container">
|
<div class="item-description-container">
|
||||||
<h4>{{localize "DAGGERHEART.GENERAL.features"}}</h4>
|
|
||||||
{{#each features as | feature |}}
|
{{#each features as | feature |}}
|
||||||
<div><strong>{{localize feature.label}}</strong>: {{{localize feature.description}}}</div>
|
<div><strong>{{localize feature.label}}</strong>: {{{localize feature.description}}}</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,6 @@
|
||||||
<div class="item-description-outer-container">
|
<div class="item-description-outer-container">
|
||||||
<div class="three-columns">
|
|
||||||
<div class="item-description-container">
|
|
||||||
<h4>{{localize "DAGGERHEART.GENERAL.Tiers.singular"}}</h4>
|
|
||||||
<span>{{tier}}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="item-description-container">
|
|
||||||
<h4>{{localize "DAGGERHEART.GENERAL.Trait.single"}}</h4>
|
|
||||||
<span>{{trait}}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="item-description-container">
|
|
||||||
<h4>{{localize "DAGGERHEART.GENERAL.range"}}</h4>
|
|
||||||
<span>{{range}}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="three-columns">
|
|
||||||
<div class="item-description-container">
|
|
||||||
<h4>{{localize "DAGGERHEART.GENERAL.damage"}}</h4>
|
|
||||||
<span>{{damage}}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="item-description-container">
|
|
||||||
<h4>{{localize "DAGGERHEART.GENERAL.burden"}}</h4>
|
|
||||||
<span>{{burden}}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#if features.length}}
|
{{#if features.length}}
|
||||||
<div class="item-description-container">
|
<div class="item-description-container">
|
||||||
<h4>{{localize "DAGGERHEART.GENERAL.features"}}</h4>
|
|
||||||
{{#each features as | feature |}}
|
{{#each features as | feature |}}
|
||||||
<div><strong>{{localize feature.label}}</strong>: {{{localize feature.description}}}</div>
|
<div><strong>{{localize feature.label}}</strong>: {{{localize feature.description}}}</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<div class="roll-part target-section dice-roll" data-action="expandRoll">
|
<div class="roll-part target-section dice-roll" data-action="expandRoll">
|
||||||
<div class="roll-part-header"><div><span>{{pluralize currentTargets.length "DAGGERHEART.GENERAL.Target"}}</span></div></div>
|
<div class="roll-part-header"><div><span>{{pluralize currentTargets.length "DAGGERHEART.GENERAL.Target"}}</span></div></div>
|
||||||
{{#if isGM}}
|
{{#if (or isGM (not metagamingSettings.hideObserverPermissionInChat))}}
|
||||||
<div class="roll-part-extra on-reduced">
|
<div class="roll-part-extra on-reduced">
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
{{#if (or (gt targetShort.hit 0) (gt targetShort.miss 0))}}
|
{{#if (or (gt targetShort.hit 0) (gt targetShort.miss 0))}}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="item-desc extensible">
|
<div class="item-desc extensible">
|
||||||
<span class="wrapper">{{{system.enrichedDescription}}}</span>
|
<span class="wrapper">
|
||||||
|
{{{system.enrichedTags}}}
|
||||||
|
{{{system.enrichedDescription}}}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
<i class="fa-solid fa-bolt"></i>
|
<i class="fa-solid fa-bolt"></i>
|
||||||
</span>
|
</span>
|
||||||
<span class="item-icon">
|
<span class="item-icon">
|
||||||
{{#with (lookup config.DOMAIN.domains item.system.domain) as | domain |}}
|
{{#with (lookup allDomains item.system.domain) as | domain |}}
|
||||||
<img src="{{domain.src}}" alt="">
|
<img src="{{domain.src}}" alt="">
|
||||||
{{/with}}
|
{{/with}}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
||||||
21
templates/ui/tooltip/resourceManagement.hbs
Normal file
21
templates/ui/tooltip/resourceManagement.hbs
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
<div class="daggerheart resource-management-container">
|
||||||
|
{{#each resources as |resource|}}
|
||||||
|
<div class="resource-section {{resource.resourceClass}}">
|
||||||
|
<h4>{{resource.label}}</h4>
|
||||||
|
{{#times resource.max}}
|
||||||
|
<span class='resource-value' data-action='toggleResource' data-value="{{add this 1}}" data-resource="{{resource.id}}">
|
||||||
|
{{#if resource.fullIcon.isIcon}}
|
||||||
|
<i class='{{resource.fullIcon.value}} full {{#unless (gte ../value (add this 1))}}hidden{{/unless}}'></i>
|
||||||
|
{{else}}
|
||||||
|
<img src="{{resource.fullIcon.value}}" class="full {{#unless resource.fullIcon.noColorFilter}}filter{{else}}non-transparent{{/unless}} {{#unless (gte ../value (add this 1))}}hidden{{/unless}}" />
|
||||||
|
{{/if}}
|
||||||
|
{{#if resource.emptyIcon.isIcon}}
|
||||||
|
<i class='{{resource.emptyIcon.value}} empty {{#if (gte ../value (add this 1))}}hidden{{/if}}'></i>
|
||||||
|
{{else}}
|
||||||
|
<img src="{{resource.emptyIcon.value}}" class="empty {{#unless resource.fullIcon.noColorFilter}}filter{{else}}non-transparent{{/unless}} {{#if (gte ../value (add this 1))}}hidden{{/if}}" />
|
||||||
|
{{/if}}
|
||||||
|
</span>
|
||||||
|
{{/times}}
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue