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();
|
||||
});
|
||||
|
||||
Hooks.on('i18nInit', () => {
|
||||
// Setup homebrew resources
|
||||
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).refreshConfig();
|
||||
});
|
||||
|
||||
Hooks.on('setup', () => {
|
||||
CONFIG.statusEffects = [
|
||||
...CONFIG.statusEffects.filter(x => !['dead', 'unconscious'].includes(x.id)),
|
||||
|
|
|
|||
64
lang/en.json
64
lang/en.json
|
|
@ -74,6 +74,15 @@
|
|||
"invalidDrop": "You can only drop Actor entities to summon.",
|
||||
"chatMessageTitle": "Test2",
|
||||
"chatMessageHeaderTitle": "Summoning"
|
||||
},
|
||||
"transform": {
|
||||
"name": "Transform",
|
||||
"tooltip": "Transform one actor into another",
|
||||
"noTransformActor": "There is no assigned actor to transform into",
|
||||
"transformActorMissing": "The assigned actor to transform into does not exist. It was probably deleted or moved in/out of a compendium",
|
||||
"canvasError": "There is no active scene.",
|
||||
"prototypeError": "You can only use a transform action from a Token",
|
||||
"actorLinkError": "You cannot transform a token with Actor Link set to true"
|
||||
}
|
||||
},
|
||||
"Config": {
|
||||
|
|
@ -129,6 +138,12 @@
|
|||
},
|
||||
"summon": {
|
||||
"dropSummonsHere": "Drop Summons Here"
|
||||
},
|
||||
"transform": {
|
||||
"dropTransformHere": "Drop Transform Here",
|
||||
"actorIsMissing": "The linked actor is missing. You should delete this link.",
|
||||
"clearHitPoints": "Clear Hitpoints",
|
||||
"clearStress": "Clear Stress"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -1031,7 +1046,8 @@
|
|||
},
|
||||
"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": {
|
||||
|
|
@ -1166,12 +1182,12 @@
|
|||
},
|
||||
"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"
|
||||
},
|
||||
"veryFar": {
|
||||
"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"
|
||||
}
|
||||
},
|
||||
|
|
@ -1294,6 +1310,7 @@
|
|||
"triggerTexts": {
|
||||
"strangePatternsContentTitle": "Matched {nr} times.",
|
||||
"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?",
|
||||
"ferocityEffectDescription": "Your evasion is increased by {bonus}. This bonus lasts until after the next attack made against you."
|
||||
},
|
||||
|
|
@ -2268,6 +2285,7 @@
|
|||
"identify": "Identity",
|
||||
"imagePath": "Image Path",
|
||||
"inactiveEffects": "Inactive Effects",
|
||||
"initial": "Initial",
|
||||
"inventory": "Inventory",
|
||||
"itemResource": "Item Resource",
|
||||
"itemQuantity": "Item Quantity",
|
||||
|
|
@ -2559,6 +2577,10 @@
|
|||
"gm": { "label": "GM" },
|
||||
"players": { "label": "Players" }
|
||||
},
|
||||
"vulnerableAutomation": {
|
||||
"label": "Vulnerable Automation",
|
||||
"hint": "Automatically apply the Vulnerable condition when a actor reaches max stress"
|
||||
},
|
||||
"countdownAutomation": {
|
||||
"label": "Countdown Automation",
|
||||
"hint": "Automatically progress countdowns based on their progression settings"
|
||||
|
|
@ -2627,6 +2649,14 @@
|
|||
"title": "Triggers"
|
||||
}
|
||||
},
|
||||
"Metagaming": {
|
||||
"FIELDS": {
|
||||
"hideObserverPermissionInChat": {
|
||||
"label": "Hide Chat Info From Players",
|
||||
"hint": "Information such as hit/miss on attack rolls against adversaries will be hidden"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Homebrew": {
|
||||
"newDowntimeMove": "Downtime Move",
|
||||
"downtimeMove": "Downtime Move",
|
||||
|
|
@ -2641,6 +2671,8 @@
|
|||
"resetMovesText": "Are you sure you want to reset?",
|
||||
"deleteItemTitle": "Delete Homebrew Item",
|
||||
"deleteItemText": "Are you sure you want to delete the item?",
|
||||
"deleteResourceTitle": "Delete Homebrew Resource",
|
||||
"deleteResourceText": "Are you sure you want to delete the resource?",
|
||||
"FIELDS": {
|
||||
"maxFear": { "label": "Max Fear" },
|
||||
"maxHope": { "label": "Max Hope" },
|
||||
|
|
@ -2649,6 +2681,13 @@
|
|||
"label": "Max Cards in Loadout",
|
||||
"hint": "Set to blank or 0 for unlimited maximum"
|
||||
},
|
||||
"resources": {
|
||||
"resources": {
|
||||
"value": { "label": "Icon" },
|
||||
"isIcon": { "label": "Font Awesome Icon" },
|
||||
"noColorFilter": { "label": "Disable Color Filter" }
|
||||
}
|
||||
},
|
||||
"maxDomains": { "label": "Max Class Domains", "hint": "Max domains you can set on a class" }
|
||||
},
|
||||
"currency": {
|
||||
|
|
@ -2677,6 +2716,13 @@
|
|||
"adversaryType": {
|
||||
"title": "Custom Adversary Types",
|
||||
"newType": "Adversary Type"
|
||||
},
|
||||
"resources": {
|
||||
"typeTitle": "{type} Resources",
|
||||
"filledIcon": "Filled Icon",
|
||||
"emptyIcon": "Empty Icon",
|
||||
"resourceIdentifier": "Resource Identifier",
|
||||
"setResourceIdentifier": "Set Resource Identifier"
|
||||
}
|
||||
},
|
||||
"Menu": {
|
||||
|
|
@ -2686,6 +2732,11 @@
|
|||
"label": "Configure Automation",
|
||||
"hint": "Various settings automating resource management and more"
|
||||
},
|
||||
"metagaming": {
|
||||
"name": "Metagaming Settings",
|
||||
"label": "Configure Metagaming",
|
||||
"hint": "Various settings controlling the flow of information to players"
|
||||
},
|
||||
"homebrew": {
|
||||
"name": "Homebrew Settings",
|
||||
"label": "Configure Homebrew",
|
||||
|
|
@ -2809,7 +2860,7 @@
|
|||
"title": "Domain Card"
|
||||
},
|
||||
"dualityRoll": {
|
||||
"abilityCheckTitle": "{ability} Check"
|
||||
"abilityCheckTitle": "{ability} Roll"
|
||||
},
|
||||
"effectSummary": {
|
||||
"title": "Effects Applied",
|
||||
|
|
@ -2824,7 +2875,7 @@
|
|||
"selectLeader": "Select a Leader",
|
||||
"selectMember": "Select a Member",
|
||||
"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",
|
||||
"wholePartySelected": "The whole party is selected"
|
||||
},
|
||||
|
|
@ -2990,7 +3041,8 @@
|
|||
"tokenActorMissing": "{name} is missing an Actor",
|
||||
"tokenActorsMissing": "[{names}] missing Actors",
|
||||
"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": {
|
||||
"actorDirectory": {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
export { default as DhAppearanceSettings } from './appearanceSettings.mjs';
|
||||
export { default as DhAutomationSettings } from './automationSettings.mjs';
|
||||
export { default as DhHomebrewSettings } from './homebrewSettings.mjs';
|
||||
export { default as DhMetagamingSettings } from './metagamingSettings.mjs';
|
||||
export { default as DhVariantRuleSettings } from './variantRuleSettings.mjs';
|
||||
|
|
|
|||
|
|
@ -31,8 +31,8 @@ export default class DhAutomationSettings extends HandlebarsApplicationMixin(App
|
|||
};
|
||||
|
||||
static PARTS = {
|
||||
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
||||
header: { template: 'systems/daggerheart/templates/settings/automation-settings/header.hbs' },
|
||||
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
||||
general: { template: 'systems/daggerheart/templates/settings/automation-settings/general.hbs' },
|
||||
rules: { template: 'systems/daggerheart/templates/settings/automation-settings/deathMoves.hbs' },
|
||||
roll: { template: 'systems/daggerheart/templates/settings/automation-settings/roll.hbs' },
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { DhHomebrew } from '../../data/settings/_module.mjs';
|
||||
import { Resource } from '../../data/settings/Homebrew.mjs';
|
||||
import { slugify } from '../../helpers/utils.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
|
@ -44,6 +45,9 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
|||
addAdversaryType: this.addAdversaryType,
|
||||
deleteAdversaryType: this.deleteAdversaryType,
|
||||
selectAdversaryType: this.selectAdversaryType,
|
||||
addResource: this.addResource,
|
||||
removeResource: this.removeResource,
|
||||
resetResourceImage: this.resetResourceImage,
|
||||
save: this.save,
|
||||
resetTokenSizes: this.resetTokenSizes,
|
||||
reset: this.reset
|
||||
|
|
@ -56,6 +60,10 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
|||
settings: { template: 'systems/daggerheart/templates/settings/homebrew-settings/settings.hbs' },
|
||||
domains: { template: 'systems/daggerheart/templates/settings/homebrew-settings/domains.hbs' },
|
||||
types: { template: 'systems/daggerheart/templates/settings/homebrew-settings/types.hbs' },
|
||||
resources: {
|
||||
template: 'systems/daggerheart/templates/settings/homebrew-settings/resources.hbs',
|
||||
scrollable: ['.resource-types-container']
|
||||
},
|
||||
itemTypes: { template: 'systems/daggerheart/templates/settings/homebrew-settings/itemFeatures.hbs' },
|
||||
downtime: { template: 'systems/daggerheart/templates/settings/homebrew-settings/downtime.hbs' },
|
||||
footer: { template: 'systems/daggerheart/templates/settings/homebrew-settings/footer.hbs' }
|
||||
|
|
@ -64,7 +72,14 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
|||
/** @inheritdoc */
|
||||
static TABS = {
|
||||
main: {
|
||||
tabs: [{ id: 'settings' }, { id: 'domains' }, { id: 'types' }, { id: 'itemFeatures' }, { id: 'downtime' }],
|
||||
tabs: [
|
||||
{ id: 'settings' },
|
||||
{ id: 'domains' },
|
||||
{ id: 'types' },
|
||||
{ id: 'resources' },
|
||||
{ id: 'itemFeatures' },
|
||||
{ id: 'downtime' }
|
||||
],
|
||||
initial: 'settings',
|
||||
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
|
||||
}
|
||||
|
|
@ -77,9 +92,17 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
|||
this.render();
|
||||
}
|
||||
|
||||
_attachPartListeners(partId, htmlElement, options) {
|
||||
super._attachPartListeners(partId, htmlElement, options);
|
||||
|
||||
for (const element of htmlElement.querySelectorAll('.path-field input'))
|
||||
element.addEventListener('change', this.toggleResourceIsIcon.bind(this));
|
||||
}
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.settingFields = this.settings;
|
||||
context.schemaFields = context.settingFields.schema.fields;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
|
@ -103,6 +126,8 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
|||
? { id: this.selected.adversaryType, ...this.settings.adversaryTypes[this.selected.adversaryType] }
|
||||
: null;
|
||||
break;
|
||||
case 'resources':
|
||||
break;
|
||||
case 'downtime':
|
||||
context.restOptions = {
|
||||
shortRest: CONFIG.DH.GENERAL.defaultRestOptions.shortRest(),
|
||||
|
|
@ -124,6 +149,33 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
|||
this.render();
|
||||
}
|
||||
|
||||
async toggleResourceIsIcon(event) {
|
||||
const element = event.target.closest('.resource-icon-container');
|
||||
const { actorType, resourceKey, imageKey } = element.dataset;
|
||||
|
||||
const current = this.settings.resources[actorType].resources[resourceKey].images[imageKey];
|
||||
await this.settings.updateSource({
|
||||
[`resources.${actorType}.resources.${resourceKey}.images.${imageKey}`]: {
|
||||
isIcon: !current.isIcon,
|
||||
value: ''
|
||||
}
|
||||
});
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async resetResourceImage(_event, button) {
|
||||
const element = button.closest('.resource-icon-container');
|
||||
const { actorType, resourceKey, imageKey } = element.dataset;
|
||||
|
||||
await this.settings.updateSource({
|
||||
[`resources.${actorType}.resources.${resourceKey}.images.${imageKey}`]:
|
||||
Resource.getDefaultImageData(imageKey)
|
||||
});
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async changeCurrencyIcon(_, target) {
|
||||
const type = target.dataset.currency;
|
||||
const currentIcon = this.settings.currency[type].icon;
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
@ -246,6 +300,8 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
|||
await this.settings.updateSource({
|
||||
[`${path}.-=${id}`]: null
|
||||
});
|
||||
|
||||
game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
|
||||
this.render();
|
||||
}
|
||||
|
||||
|
|
@ -462,6 +518,58 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
|||
this.render();
|
||||
}
|
||||
|
||||
static async addResource(_, target) {
|
||||
const { actorType } = target.dataset;
|
||||
const content = new foundry.data.fields.StringField({
|
||||
label: game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.resources.resourceIdentifier'),
|
||||
required: true
|
||||
}).toFormGroup({}, { name: 'identifier', localize: true }).outerHTML;
|
||||
|
||||
async function callback(_, button) {
|
||||
const identifier = button.form.elements.identifier.value;
|
||||
if (!identifier) return;
|
||||
|
||||
const sluggedIdentifier = slugify(identifier);
|
||||
|
||||
await this.settings.updateSource({
|
||||
[`resources.${actorType}.resources.${sluggedIdentifier}`]: Resource.getDefaultResourceData(identifier)
|
||||
});
|
||||
|
||||
game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
|
||||
this.render();
|
||||
}
|
||||
|
||||
await foundry.applications.api.DialogV2.prompt({
|
||||
content: content,
|
||||
rejectClose: false,
|
||||
modal: true,
|
||||
ok: { callback: callback.bind(this) },
|
||||
window: {
|
||||
title: game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.resources.setResourceIdentifier')
|
||||
},
|
||||
position: { width: 400 }
|
||||
});
|
||||
}
|
||||
|
||||
static async removeResource(_, target) {
|
||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||
window: {
|
||||
title: game.i18n.localize(`DAGGERHEART.SETTINGS.Homebrew.deleteResourceTitle`)
|
||||
},
|
||||
content: game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.deleteResourceText')
|
||||
});
|
||||
|
||||
if (!confirmed) return;
|
||||
|
||||
const { actorType, resourceKey } = target.dataset;
|
||||
await this.settings.updateSource({
|
||||
[`resources.${actorType}.resources.-=${resourceKey}`]: null
|
||||
});
|
||||
|
||||
game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async save() {
|
||||
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
|
||||
this.close();
|
||||
|
|
|
|||
62
module/applications/settings/metagamingSettings.mjs
Normal file
62
module/applications/settings/metagamingSettings.mjs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import { DhMetagaming } from '../../data/settings/_module.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
export default class DhMetagamingSettings extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor() {
|
||||
super({});
|
||||
|
||||
this.settings = new DhMetagaming(
|
||||
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Metagaming).toObject()
|
||||
);
|
||||
}
|
||||
|
||||
get title() {
|
||||
return game.i18n.localize('DAGGERHEART.SETTINGS.Menu.title');
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
id: 'daggerheart-metagaming-settings',
|
||||
classes: ['daggerheart', 'dh-style', 'dialog', 'setting'],
|
||||
position: { width: '600', height: 'auto' },
|
||||
window: {
|
||||
icon: 'fa-solid fa-eye-low-vision'
|
||||
},
|
||||
actions: {
|
||||
reset: this.reset,
|
||||
save: this.save
|
||||
},
|
||||
form: { handler: this.updateData, submitOnChange: true }
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
header: { template: 'systems/daggerheart/templates/settings/metagaming-settings/header.hbs' },
|
||||
general: { template: 'systems/daggerheart/templates/settings/metagaming-settings/general.hbs' },
|
||||
footer: { template: 'systems/daggerheart/templates/settings/metagaming-settings/footer.hbs' }
|
||||
};
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.settingFields = this.settings;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static async updateData(_event, _element, formData) {
|
||||
const updatedSettings = foundry.utils.expandObject(formData.object);
|
||||
|
||||
await this.settings.updateSource(updatedSettings);
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async reset() {
|
||||
this.settings = new DhMetagaming();
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async save() {
|
||||
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Metagaming, this.settings.toObject());
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
|
@ -28,6 +28,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
|||
removeEffect: this.removeEffect,
|
||||
addElement: this.addElement,
|
||||
removeElement: this.removeElement,
|
||||
removeTransformActor: this.removeTransformActor,
|
||||
editEffect: this.editEffect,
|
||||
addDamage: this.addDamage,
|
||||
removeDamage: this.removeDamage,
|
||||
|
|
@ -41,7 +42,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
|||
submitOnChange: true,
|
||||
closeOnSubmit: false
|
||||
},
|
||||
dragDrop: [{ dragSelector: null, dropSelector: '#summon-drop-zone', handlers: ['_onDrop'] }]
|
||||
dragDrop: [{ dragSelector: null, dropSelector: '[data-is-drop-zone]', handlers: ['_onDrop'] }]
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
|
|
@ -120,6 +121,10 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
|||
htmlElement.querySelectorAll('.summon-count-wrapper input').forEach(element => {
|
||||
element.addEventListener('change', this.updateSummonCount.bind(this));
|
||||
});
|
||||
|
||||
htmlElement.querySelectorAll('.transform-resource input').forEach(element => {
|
||||
element.addEventListener('change', this.updateTransformResource.bind(this));
|
||||
});
|
||||
}
|
||||
|
||||
async _prepareContext(_options) {
|
||||
|
|
@ -133,6 +138,18 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
|||
context.summons.push({ actor, count: summon.count });
|
||||
}
|
||||
|
||||
if (context.source.transform) {
|
||||
const actor = await foundry.utils.fromUuid(context.source.transform.actorUUID);
|
||||
context.transform = {
|
||||
...context.source.transform,
|
||||
actor:
|
||||
actor ??
|
||||
(context.source.transform.actorUUID && !actor
|
||||
? { error: game.i18n.localize('DAGGERHEART.ACTIONS.Settings.transform.actorIsMissing') }
|
||||
: null)
|
||||
};
|
||||
}
|
||||
|
||||
context.openSection = this.openSection;
|
||||
context.tabs = this._getTabs(this.constructor.TABS);
|
||||
context.config = CONFIG.DH;
|
||||
|
|
@ -266,6 +283,12 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
|||
if (doc) return doc.sheet.render({ force: true });
|
||||
}
|
||||
|
||||
static async removeTransformActor() {
|
||||
const data = this.action.toObject();
|
||||
data.transform.actorUUID = null;
|
||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||
}
|
||||
|
||||
static addDamage(_event) {
|
||||
if (!this.action.damage.parts) return;
|
||||
const data = this.action.toObject(),
|
||||
|
|
@ -346,6 +369,14 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
|||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||
}
|
||||
|
||||
updateTransformResource(event) {
|
||||
event.stopPropagation();
|
||||
|
||||
const data = this.action.toObject();
|
||||
data.transform.resourceRefresh[event.target.dataset.resource] = event.target.checked;
|
||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||
}
|
||||
|
||||
/** Specific implementation in extending classes **/
|
||||
static async addEffect(_event) {}
|
||||
static removeEffect(_event, _button) {}
|
||||
|
|
@ -364,6 +395,18 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
|||
return;
|
||||
}
|
||||
|
||||
const dropZone = event.target.closest('[data-is-drop-zone]');
|
||||
if (!dropZone) return;
|
||||
|
||||
switch (dropZone.id) {
|
||||
case 'summon-drop-zone':
|
||||
return this.onSummonDrop(data);
|
||||
case 'transform-drop-zone':
|
||||
return this.onTransformDrop(data);
|
||||
}
|
||||
}
|
||||
|
||||
async onSummonDrop(data) {
|
||||
const actionData = this.action.toObject();
|
||||
let countvalue = 1;
|
||||
for (const entry of actionData.summon) {
|
||||
|
|
@ -380,4 +423,10 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
|||
actionData.summon.push({ actorUUID: data.uuid, count: countvalue });
|
||||
await this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(actionData) });
|
||||
}
|
||||
|
||||
async onTransformDrop(data) {
|
||||
const actionData = this.action.toObject();
|
||||
actionData.transform.actorUUID = data.uuid;
|
||||
await this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(actionData) });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,42 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
|||
constructor(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 getAllLeaves = (root, group, parentPath = '') => {
|
||||
|
|
@ -23,7 +59,7 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
|||
|
||||
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;
|
||||
|
||||
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) {
|
||||
super._attachPartListeners(partId, htmlElement, options);
|
||||
const changeChoices = this.changeChoices;
|
||||
|
|
|
|||
|
|
@ -7,19 +7,7 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi
|
|||
super({});
|
||||
|
||||
this.effect = foundry.utils.deepClone(effect);
|
||||
const ignoredActorKeys = ['config', 'DhEnvironment'];
|
||||
this.changeChoices = Object.keys(game.system.api.models.actors).reduce((acc, key) => {
|
||||
if (!ignoredActorKeys.includes(key)) {
|
||||
const model = game.system.api.models.actors[key];
|
||||
const attributes = CONFIG.Token.documentClass.getTrackedAttributes(model);
|
||||
const group = game.i18n.localize(model.metadata.label);
|
||||
const choices = CONFIG.Token.documentClass
|
||||
.getTrackedAttributeChoices(attributes, model)
|
||||
.map(x => ({ ...x, group: group }));
|
||||
acc.push(...choices);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
this.changeChoices = game.system.api.applications.sheetConfigs.ActiveEffectConfig.getChangeChoices();
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
|
|
|
|||
|
|
@ -73,9 +73,11 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App
|
|||
return context;
|
||||
}
|
||||
|
||||
static async updateData(event, element, formData) {
|
||||
static async updateData(_event, _element, formData) {
|
||||
const data = foundry.utils.expandObject(formData.object);
|
||||
foundry.utils.mergeObject(this.move, data);
|
||||
await this.updateMove({
|
||||
[`${this.movePath}`]: data
|
||||
});
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
|
@ -135,9 +137,7 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App
|
|||
}
|
||||
);
|
||||
|
||||
await this.settings.updateSource({ [`${this.actionsPath}.${action.id}`]: action });
|
||||
this.move = foundry.utils.getProperty(this.settings, this.movePath);
|
||||
|
||||
await this.updateMove({ [`${this.actionsPath}.${action.id}`]: action });
|
||||
this.render();
|
||||
}
|
||||
|
||||
|
|
@ -150,13 +150,12 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App
|
|||
await game.system.api.applications.sheetConfigs.SettingActiveEffectConfig.configure(effect);
|
||||
if (!updatedEffect) return;
|
||||
|
||||
await this.settings.updateSource({
|
||||
await this.updateMove({
|
||||
[`${this.movePath}.effects`]: this.move.effects.reduce((acc, effect, index) => {
|
||||
acc.push(index === effectIndex ? { ...updatedEffect, id: effect.id } : effect);
|
||||
return acc;
|
||||
}, [])
|
||||
});
|
||||
this.move = foundry.utils.getProperty(this.settings, this.movePath);
|
||||
this.render();
|
||||
} else {
|
||||
const action = this.move.actions.get(id);
|
||||
|
|
@ -171,13 +170,13 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App
|
|||
: existingEffectIndex === -1
|
||||
? [...currentEffects, effectData]
|
||||
: currentEffects.with(existingEffectIndex, effectData);
|
||||
await this.settings.updateSource({
|
||||
await this.updateMove({
|
||||
[`${this.movePath}.effects`]: updatedEffects
|
||||
});
|
||||
}
|
||||
|
||||
await this.settings.updateSource({ [`${this.actionsPath}.${id}`]: updatedMove });
|
||||
this.move = foundry.utils.getProperty(this.settings, this.movePath);
|
||||
await this.updateMove({ [`${this.actionsPath}.${id}`]: updatedMove });
|
||||
|
||||
this.render();
|
||||
return updatedEffects;
|
||||
}).render(true);
|
||||
|
|
@ -199,33 +198,36 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App
|
|||
});
|
||||
}
|
||||
}
|
||||
await this.settings.updateSource({
|
||||
await this.updateMove({
|
||||
[this.movePath]: {
|
||||
effects: move.effects.filter(x => x.id !== id),
|
||||
actions: move.actions
|
||||
}
|
||||
});
|
||||
} 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();
|
||||
}
|
||||
|
||||
static async addEffect(_, target) {
|
||||
static async addEffect() {
|
||||
const currentEffects = foundry.utils.getProperty(this.settings, `${this.movePath}.effects`);
|
||||
await this.settings.updateSource({
|
||||
|
||||
await this.updateMove({
|
||||
[`${this.movePath}.effects`]: [
|
||||
...currentEffects,
|
||||
game.system.api.data.activeEffects.BaseEffect.getDefaultObject()
|
||||
]
|
||||
});
|
||||
|
||||
this.move = foundry.utils.getProperty(this.settings, this.movePath);
|
||||
this.render();
|
||||
}
|
||||
|
||||
async updateMove(update) {
|
||||
await this.settings.updateSource(update);
|
||||
this.move = foundry.utils.getProperty(this.settings, this.movePath);
|
||||
}
|
||||
|
||||
static resetMoves() {}
|
||||
|
||||
_filterTabs(tabs) {
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
handleResourceDice: CharacterSheet.#handleResourceDice,
|
||||
advanceResourceDie: CharacterSheet.#advanceResourceDie,
|
||||
cancelBeastform: CharacterSheet.#cancelBeastform,
|
||||
toggleResourceManagement: CharacterSheet.#toggleResourceManagement,
|
||||
useDowntime: this.useDowntime,
|
||||
viewParty: CharacterSheet.#viewParty
|
||||
},
|
||||
|
|
@ -225,6 +226,9 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
async _preparePartContext(partId, context, options) {
|
||||
context = await super._preparePartContext(partId, context, options);
|
||||
switch (partId) {
|
||||
case 'header':
|
||||
await this._prepareHeaderContext(context, options);
|
||||
break;
|
||||
case 'loadout':
|
||||
await this._prepareLoadoutContext(context, options);
|
||||
break;
|
||||
|
|
@ -239,6 +243,12 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
return context;
|
||||
}
|
||||
|
||||
async _prepareHeaderContext(context, _options) {
|
||||
context.hasExtraResources = Object.keys(CONFIG.DH.RESOURCE.character.all).some(
|
||||
key => !CONFIG.DH.RESOURCE.character.base[key]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare render context for the Loadout part.
|
||||
* @param {ApplicationRenderContext} context
|
||||
|
|
@ -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.
|
||||
* @type {ApplicationClickAction}
|
||||
|
|
|
|||
|
|
@ -264,15 +264,6 @@ export default class Party extends DHBaseActorSheet {
|
|||
).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 */
|
||||
/* -------------------------------------------- */
|
||||
|
|
|
|||
|
|
@ -44,8 +44,32 @@ export default class DHBaseActorSettings extends DHApplicationMixin(DocumentShee
|
|||
const context = await super._prepareContext(options);
|
||||
context.isNPC = this.actor.isNPC;
|
||||
|
||||
if (context.systemFields.attack)
|
||||
if (context.systemFields.attack) {
|
||||
context.systemFields.attack.fields = this.actor.system.attack.schema.fields;
|
||||
}
|
||||
|
||||
// Create fake fields for actor configurable max resource value.
|
||||
const resourceConfig = CONFIG.DH.RESOURCE[this.actor.type]?.all;
|
||||
if (resourceConfig) {
|
||||
const relevant = ['hitPoints', 'stress'].filter(r => r in resourceConfig);
|
||||
context.resources = relevant.map(key => {
|
||||
const data = this.actor._source.system.resources[key];
|
||||
const config = resourceConfig[key];
|
||||
return {
|
||||
label: config.label,
|
||||
name: `system.resources.${key}.max`,
|
||||
value: data.max ?? config.max,
|
||||
tooltip: key === 'hitPoints' ? game.i18n.localize('DAGGERHEART.UI.Tooltip.maxHPClassBound') : null,
|
||||
field: new foundry.data.fields.NumberField({
|
||||
initial: config.max,
|
||||
integer: true,
|
||||
label: game.i18n.format('DAGGERHEART.GENERAL.maxWithThing', {
|
||||
thing: game.i18n.localize(config.label)
|
||||
})
|
||||
})
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -691,6 +691,9 @@ export default function DHApplicationMixin(Base) {
|
|||
case 'weapon':
|
||||
presets.folder = 'equipments.folders.weapons';
|
||||
break;
|
||||
case 'feature':
|
||||
presets.folder = 'features';
|
||||
break;
|
||||
case 'domainCard':
|
||||
presets.folder = 'domains';
|
||||
presets.filter = {
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
|||
],
|
||||
dragDrop: [
|
||||
{ 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]
|
||||
};
|
||||
}
|
||||
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;
|
||||
|
|
@ -270,7 +270,9 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
|||
currency
|
||||
});
|
||||
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 });
|
||||
}
|
||||
return;
|
||||
|
|
@ -292,6 +294,15 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
|||
|
||||
/* Handling transfer of inventoryItems */
|
||||
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) {
|
||||
const actorItem = originActor.items.get(data.originId);
|
||||
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 === 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));
|
||||
if (existingItem) {
|
||||
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 {
|
||||
await originActor.deleteEmbeddedDocuments('Item', [data.originId]);
|
||||
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) {
|
||||
// Handle drag/dropping currencies
|
||||
const currencyEl = event.currentTarget.closest(".currency[data-currency]");
|
||||
const currencyEl = event.currentTarget.closest('.currency[data-currency]');
|
||||
if (currencyEl) {
|
||||
const currency = currencyEl.dataset.currency;
|
||||
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.setDragImage(attackItem.querySelector('img'), 60, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const item = await getDocFromElement(event.target);
|
||||
if (item) {
|
||||
const dragData = {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { refreshIsAllowed } from '../../../helpers/utils.mjs';
|
||||
import { RefreshFeatures } from '../../../helpers/utils.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
const { AbstractSidebarTab } = foundry.applications.sidebar;
|
||||
|
|
@ -54,73 +54,6 @@ export default class DaggerheartMenu extends HandlebarsApplicationMixin(Abstract
|
|||
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 */
|
||||
/* -------------------------------------------- */
|
||||
|
|
@ -133,30 +66,9 @@ export default class DaggerheartMenu extends HandlebarsApplicationMixin(Abstract
|
|||
|
||||
static async #refreshActors() {
|
||||
const refreshKeys = Object.keys(this.refreshSelections).filter(key => this.refreshSelections[key].selected);
|
||||
await this.getRefreshables(refreshKeys);
|
||||
const types = refreshKeys.map(x => this.refreshSelections[x].label).join(', ');
|
||||
ui.notifications.info(
|
||||
game.i18n.format('DAGGERHEART.UI.Notifications.gmMenuRefresh', {
|
||||
types: `[${types}]`
|
||||
})
|
||||
);
|
||||
await RefreshFeatures(refreshKeys);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -251,6 +251,12 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
|
||||
/* If any noticeable slowdown occurs, consider replacing with enriching description on clicking to expand descriptions */
|
||||
for (const item of this.items) {
|
||||
if (["weapon", "armor"].includes(item.type)) {
|
||||
item.system.enrichedTags = await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/sheets/global/partials/item-tags.hbs',
|
||||
item.system,
|
||||
);
|
||||
}
|
||||
item.system.enrichedDescription =
|
||||
(await item.system.getEnrichedDescription?.()) ??
|
||||
(await foundry.applications.ux.TextEditor.implementation.enrichHTML(item.description));
|
||||
|
|
|
|||
|
|
@ -11,3 +11,4 @@ export * as settingsConfig from './settingsConfig.mjs';
|
|||
export * as systemConfig from './system.mjs';
|
||||
export * as itemBrowserConfig from './itemBrowserConfig.mjs';
|
||||
export * as triggerConfig from './triggerConfig.mjs';
|
||||
export * as resourceConfig from './resourceConfig.mjs';
|
||||
|
|
|
|||
|
|
@ -35,6 +35,12 @@ export const actionTypes = {
|
|||
icon: 'fa-ghost',
|
||||
tooltip: 'DAGGERHEART.ACTIONS.TYPES.summon.tooltip'
|
||||
},
|
||||
transform: {
|
||||
id: 'transform',
|
||||
name: 'DAGGERHEART.ACTIONS.TYPES.transform.name',
|
||||
icon: 'fa-dragon',
|
||||
tooltip: 'DAGGERHEART.ACTIONS.TYPES.transform.tooltip'
|
||||
},
|
||||
effect: {
|
||||
id: 'effect',
|
||||
name: 'DAGGERHEART.ACTIONS.TYPES.effect.name',
|
||||
|
|
|
|||
|
|
@ -55,24 +55,6 @@ export const abilities = {
|
|||
}
|
||||
};
|
||||
|
||||
export const scrollingTextResource = {
|
||||
hitPoints: {
|
||||
label: 'DAGGERHEART.GENERAL.HitPoints.plural',
|
||||
reversed: true
|
||||
},
|
||||
stress: {
|
||||
label: 'DAGGERHEART.GENERAL.stress',
|
||||
reversed: true
|
||||
},
|
||||
hope: {
|
||||
label: 'DAGGERHEART.GENERAL.hope'
|
||||
},
|
||||
armor: {
|
||||
label: 'DAGGERHEART.GENERAL.armor',
|
||||
reversed: true
|
||||
}
|
||||
};
|
||||
|
||||
export const featureProperties = {
|
||||
agility: {
|
||||
name: 'DAGGERHEART.CONFIG.Traits.agility.name',
|
||||
|
|
@ -506,8 +488,8 @@ export const subclassFeatureLabels = {
|
|||
* @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.
|
||||
*/
|
||||
export const adversaryScalingData = {
|
||||
|
|
@ -518,7 +500,7 @@ export const adversaryScalingData = {
|
|||
severeThreshold: 10,
|
||||
hp: 1,
|
||||
stress: 2,
|
||||
attack: 2,
|
||||
attack: 2
|
||||
},
|
||||
3: {
|
||||
difficulty: 2,
|
||||
|
|
@ -526,7 +508,7 @@ export const adversaryScalingData = {
|
|||
severeThreshold: 15,
|
||||
hp: 1,
|
||||
stress: 0,
|
||||
attack: 2,
|
||||
attack: 2
|
||||
},
|
||||
4: {
|
||||
difficulty: 2,
|
||||
|
|
@ -534,7 +516,7 @@ export const adversaryScalingData = {
|
|||
severeThreshold: 25,
|
||||
hp: 1,
|
||||
stress: 0,
|
||||
attack: 2,
|
||||
attack: 2
|
||||
}
|
||||
},
|
||||
horde: {
|
||||
|
|
@ -544,7 +526,7 @@ export const adversaryScalingData = {
|
|||
severeThreshold: 8,
|
||||
hp: 2,
|
||||
stress: 0,
|
||||
attack: 0,
|
||||
attack: 0
|
||||
},
|
||||
3: {
|
||||
difficulty: 2,
|
||||
|
|
@ -552,7 +534,7 @@ export const adversaryScalingData = {
|
|||
severeThreshold: 12,
|
||||
hp: 0,
|
||||
stress: 1,
|
||||
attack: 1,
|
||||
attack: 1
|
||||
},
|
||||
4: {
|
||||
difficulty: 2,
|
||||
|
|
@ -560,7 +542,7 @@ export const adversaryScalingData = {
|
|||
severeThreshold: 15,
|
||||
hp: 2,
|
||||
stress: 0,
|
||||
attack: 0,
|
||||
attack: 0
|
||||
}
|
||||
},
|
||||
leader: {
|
||||
|
|
@ -570,7 +552,7 @@ export const adversaryScalingData = {
|
|||
severeThreshold: 10,
|
||||
hp: 0,
|
||||
stress: 0,
|
||||
attack: 1,
|
||||
attack: 1
|
||||
},
|
||||
3: {
|
||||
difficulty: 2,
|
||||
|
|
@ -578,7 +560,7 @@ export const adversaryScalingData = {
|
|||
severeThreshold: 15,
|
||||
hp: 1,
|
||||
stress: 0,
|
||||
attack: 2,
|
||||
attack: 2
|
||||
},
|
||||
4: {
|
||||
difficulty: 2,
|
||||
|
|
@ -586,7 +568,7 @@ export const adversaryScalingData = {
|
|||
severeThreshold: 25,
|
||||
hp: 1,
|
||||
stress: 1,
|
||||
attack: 3,
|
||||
attack: 3
|
||||
}
|
||||
},
|
||||
minion: {
|
||||
|
|
@ -596,7 +578,7 @@ export const adversaryScalingData = {
|
|||
severeThreshold: 0,
|
||||
hp: 0,
|
||||
stress: 0,
|
||||
attack: 1,
|
||||
attack: 1
|
||||
},
|
||||
3: {
|
||||
difficulty: 2,
|
||||
|
|
@ -604,7 +586,7 @@ export const adversaryScalingData = {
|
|||
severeThreshold: 0,
|
||||
hp: 0,
|
||||
stress: 1,
|
||||
attack: 1,
|
||||
attack: 1
|
||||
},
|
||||
4: {
|
||||
difficulty: 2,
|
||||
|
|
@ -612,7 +594,7 @@ export const adversaryScalingData = {
|
|||
severeThreshold: 0,
|
||||
hp: 0,
|
||||
stress: 0,
|
||||
attack: 1,
|
||||
attack: 1
|
||||
}
|
||||
},
|
||||
ranged: {
|
||||
|
|
@ -622,7 +604,7 @@ export const adversaryScalingData = {
|
|||
severeThreshold: 6,
|
||||
hp: 1,
|
||||
stress: 0,
|
||||
attack: 1,
|
||||
attack: 1
|
||||
},
|
||||
3: {
|
||||
difficulty: 2,
|
||||
|
|
@ -630,7 +612,7 @@ export const adversaryScalingData = {
|
|||
severeThreshold: 14,
|
||||
hp: 1,
|
||||
stress: 1,
|
||||
attack: 2,
|
||||
attack: 2
|
||||
},
|
||||
4: {
|
||||
difficulty: 2,
|
||||
|
|
@ -638,7 +620,7 @@ export const adversaryScalingData = {
|
|||
severeThreshold: 10,
|
||||
hp: 1,
|
||||
stress: 1,
|
||||
attack: 1,
|
||||
attack: 1
|
||||
}
|
||||
},
|
||||
skulk: {
|
||||
|
|
@ -648,7 +630,7 @@ export const adversaryScalingData = {
|
|||
severeThreshold: 8,
|
||||
hp: 1,
|
||||
stress: 1,
|
||||
attack: 1,
|
||||
attack: 1
|
||||
},
|
||||
3: {
|
||||
difficulty: 2,
|
||||
|
|
@ -656,7 +638,7 @@ export const adversaryScalingData = {
|
|||
severeThreshold: 12,
|
||||
hp: 1,
|
||||
stress: 1,
|
||||
attack: 1,
|
||||
attack: 1
|
||||
},
|
||||
4: {
|
||||
difficulty: 2,
|
||||
|
|
@ -664,7 +646,7 @@ export const adversaryScalingData = {
|
|||
severeThreshold: 10,
|
||||
hp: 1,
|
||||
stress: 1,
|
||||
attack: 1,
|
||||
attack: 1
|
||||
}
|
||||
},
|
||||
solo: {
|
||||
|
|
@ -674,7 +656,7 @@ export const adversaryScalingData = {
|
|||
severeThreshold: 10,
|
||||
hp: 0,
|
||||
stress: 1,
|
||||
attack: 2,
|
||||
attack: 2
|
||||
},
|
||||
3: {
|
||||
difficulty: 2,
|
||||
|
|
@ -682,7 +664,7 @@ export const adversaryScalingData = {
|
|||
severeThreshold: 15,
|
||||
hp: 2,
|
||||
stress: 1,
|
||||
attack: 2,
|
||||
attack: 2
|
||||
},
|
||||
4: {
|
||||
difficulty: 2,
|
||||
|
|
@ -690,7 +672,7 @@ export const adversaryScalingData = {
|
|||
severeThreshold: 25,
|
||||
hp: 0,
|
||||
stress: 1,
|
||||
attack: 3,
|
||||
attack: 3
|
||||
}
|
||||
},
|
||||
standard: {
|
||||
|
|
@ -700,7 +682,7 @@ export const adversaryScalingData = {
|
|||
severeThreshold: 8,
|
||||
hp: 0,
|
||||
stress: 0,
|
||||
attack: 1,
|
||||
attack: 1
|
||||
},
|
||||
3: {
|
||||
difficulty: 2,
|
||||
|
|
@ -708,7 +690,7 @@ export const adversaryScalingData = {
|
|||
severeThreshold: 15,
|
||||
hp: 1,
|
||||
stress: 1,
|
||||
attack: 1,
|
||||
attack: 1
|
||||
},
|
||||
4: {
|
||||
difficulty: 2,
|
||||
|
|
@ -716,7 +698,7 @@ export const adversaryScalingData = {
|
|||
severeThreshold: 15,
|
||||
hp: 0,
|
||||
stress: 1,
|
||||
attack: 1,
|
||||
attack: 1
|
||||
}
|
||||
},
|
||||
support: {
|
||||
|
|
@ -726,7 +708,7 @@ export const adversaryScalingData = {
|
|||
severeThreshold: 8,
|
||||
hp: 1,
|
||||
stress: 1,
|
||||
attack: 1,
|
||||
attack: 1
|
||||
},
|
||||
3: {
|
||||
difficulty: 2,
|
||||
|
|
@ -734,7 +716,7 @@ export const adversaryScalingData = {
|
|||
severeThreshold: 12,
|
||||
hp: 0,
|
||||
stress: 0,
|
||||
attack: 1,
|
||||
attack: 1
|
||||
},
|
||||
4: {
|
||||
difficulty: 2,
|
||||
|
|
@ -742,27 +724,27 @@ export const adversaryScalingData = {
|
|||
severeThreshold: 10,
|
||||
hp: 1,
|
||||
stress: 1,
|
||||
attack: 1,
|
||||
attack: 1
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
* Scaling data used for an adversary's damage.
|
||||
* Tier 4 is missing certain adversary types and therefore skews upwards.
|
||||
* We manually set tier 4 data to hopefully lead to better results
|
||||
*/
|
||||
export const adversaryExpectedDamage = {
|
||||
basic: {
|
||||
1: { mean: 7.321428571428571, deviation: 1.962519002770912 },
|
||||
2: { mean: 12.444444444444445, deviation: 2.0631069425529676 },
|
||||
3: { mean: 15.722222222222221, deviation: 2.486565208464823 },
|
||||
4: { mean: 26, deviation: 5.2 }
|
||||
},
|
||||
minion: {
|
||||
1: { mean: 2.142857142857143, deviation: 1.0690449676496976 },
|
||||
2: { mean: 5, deviation: 0.816496580927726 },
|
||||
3: { mean: 6.5, deviation: 2.1213203435596424 },
|
||||
4: { mean: 11, deviation: 1 }
|
||||
}
|
||||
basic: {
|
||||
1: { mean: 7.321428571428571, deviation: 1.962519002770912 },
|
||||
2: { mean: 12.444444444444445, deviation: 2.0631069425529676 },
|
||||
3: { mean: 15.722222222222221, deviation: 2.486565208464823 },
|
||||
4: { mean: 26, deviation: 5.2 }
|
||||
},
|
||||
minion: {
|
||||
1: { mean: 2.142857142857143, deviation: 1.0690449676496976 },
|
||||
2: { mean: 5, deviation: 0.816496580927726 },
|
||||
3: { mean: 6.5, deviation: 2.1213203435596424 },
|
||||
4: { mean: 11, deviation: 1 }
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -202,7 +202,8 @@ export const conditions = () => ({
|
|||
id: 'vulnerable',
|
||||
name: 'DAGGERHEART.CONFIG.Condition.vulnerable.name',
|
||||
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: {
|
||||
id: 'hidden',
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
export const hooksConfig = {
|
||||
effectDisplayToggle: 'DHEffectDisplayToggle'
|
||||
effectDisplayToggle: 'DHEffectDisplayToggle',
|
||||
lockedTooltipDismissed: 'DHLockedTooltipDismissed'
|
||||
};
|
||||
|
|
|
|||
|
|
@ -467,9 +467,7 @@ export const allArmorFeatures = () => {
|
|||
};
|
||||
|
||||
export const orderedArmorFeatures = () => {
|
||||
const homebrewFeatures = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).itemFeatures
|
||||
.armorFeatures;
|
||||
const allFeatures = { ...armorFeatures, ...homebrewFeatures };
|
||||
const allFeatures = allArmorFeatures();
|
||||
const all = Object.keys(allFeatures).map(key => {
|
||||
const feature = allFeatures[key];
|
||||
return {
|
||||
|
|
@ -1404,9 +1402,7 @@ export const allWeaponFeatures = () => {
|
|||
};
|
||||
|
||||
export const orderedWeaponFeatures = () => {
|
||||
const homebrewFeatures = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).itemFeatures
|
||||
.weaponFeatures;
|
||||
const allFeatures = { ...weaponFeatures, ...homebrewFeatures };
|
||||
const allFeatures = allWeaponFeatures();
|
||||
const all = Object.keys(allFeatures).map(key => {
|
||||
const feature = allFeatures[key];
|
||||
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',
|
||||
Icon: 'fa-solid fa-robot'
|
||||
},
|
||||
Metagaming: {
|
||||
Name: 'GameSettingsMetagaming',
|
||||
Icon: 'fa-solid fa-eye-low-vision'
|
||||
},
|
||||
Homebrew: {
|
||||
Name: 'GameSettingsHomebrew',
|
||||
Icon: 'fa-solid fa-flask-vial'
|
||||
|
|
@ -19,6 +23,7 @@ export const menu = {
|
|||
|
||||
export const gameSettings = {
|
||||
Automation: 'Automation',
|
||||
Metagaming: 'Metagaming',
|
||||
Homebrew: 'Homebrew',
|
||||
appearance: 'Appearance',
|
||||
variantRules: 'VariantRules',
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import * as GENERAL from './generalConfig.mjs';
|
|||
import * as DOMAIN from './domainConfig.mjs';
|
||||
import * as ENCOUNTER from './encounterConfig.mjs';
|
||||
import * as ACTOR from './actorConfig.mjs';
|
||||
import * as RESOURCE from './resourceConfig.mjs';
|
||||
import * as ITEM from './itemConfig.mjs';
|
||||
import * as SETTINGS from './settingsConfig.mjs';
|
||||
import * as EFFECTS from './effectConfig.mjs';
|
||||
|
|
@ -19,6 +20,7 @@ export const SYSTEM = {
|
|||
GENERAL,
|
||||
DOMAIN,
|
||||
ACTOR,
|
||||
RESOURCE,
|
||||
ITEM,
|
||||
SETTINGS,
|
||||
EFFECTS,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import EffectAction from './effectAction.mjs';
|
|||
import HealingAction from './healingAction.mjs';
|
||||
import MacroAction from './macroAction.mjs';
|
||||
import SummonAction from './summonAction.mjs';
|
||||
import TransformAction from './transformAction.mjs';
|
||||
|
||||
export const actionsTypes = {
|
||||
base: BaseAction,
|
||||
|
|
@ -17,5 +18,6 @@ export const actionsTypes = {
|
|||
summon: SummonAction,
|
||||
effect: EffectAction,
|
||||
macro: MacroAction,
|
||||
beastform: BeastformAction
|
||||
beastform: BeastformAction,
|
||||
transform: TransformAction
|
||||
};
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
async executeWorkflow(config) {
|
||||
for (const [key, part] of this.workflow) {
|
||||
if (Hooks.call(`${CONFIG.DH.id}.pre${key.capitalize()}Action`, this, config) === false) return;
|
||||
if ((await part.execute(config)) === false) return;
|
||||
if ((await part.execute(config)) === false) return false;
|
||||
if (Hooks.call(`${CONFIG.DH.id}.post${key.capitalize()}Action`, this, config) === false) return;
|
||||
}
|
||||
}
|
||||
|
|
@ -224,7 +224,9 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
}
|
||||
|
||||
// Execute the Action Worflow in order based of schema fields
|
||||
await this.executeWorkflow(config);
|
||||
const result = await this.executeWorkflow(config);
|
||||
if (result === false) return;
|
||||
|
||||
await config.resourceUpdates.updateResources();
|
||||
|
||||
if (Hooks.call(`${CONFIG.DH.id}.postUseAction`, this, config) === false) return;
|
||||
|
|
|
|||
5
module/data/action/transformAction.mjs
Normal file
5
module/data/action/transformAction.mjs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import DHBaseAction from './baseAction.mjs';
|
||||
|
||||
export default class DHTransformAction extends DHBaseAction {
|
||||
static extraSchemas = [...super.extraSchemas, 'transform'];
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ import DHAdversarySettings from '../../applications/sheets-configs/adversary-set
|
|||
import { ActionField } from '../fields/actionField.mjs';
|
||||
import { commonActorRules } from './base.mjs';
|
||||
import DhCreature from './creature.mjs';
|
||||
import { resourceField, bonusField } from '../fields/actorField.mjs';
|
||||
import { bonusField } from '../fields/actorField.mjs';
|
||||
import { calculateExpectedValue, parseTermsFromSimpleFormula } from '../../helpers/utils.mjs';
|
||||
import { adversaryExpectedDamage, adversaryScalingData } from '../../config/actorConfig.mjs';
|
||||
|
||||
|
|
@ -65,10 +65,6 @@ export default class DhpAdversary extends DhCreature {
|
|||
label: 'DAGGERHEART.GENERAL.DamageThresholds.severeThreshold'
|
||||
})
|
||||
}),
|
||||
resources: new fields.SchemaField({
|
||||
hitPoints: resourceField(0, 0, 'DAGGERHEART.GENERAL.HitPoints.plural', true),
|
||||
stress: resourceField(0, 0, 'DAGGERHEART.GENERAL.stress', true)
|
||||
}),
|
||||
rules: new fields.SchemaField({
|
||||
...commonActorRules()
|
||||
}),
|
||||
|
|
@ -191,6 +187,7 @@ export default class DhpAdversary extends DhCreature {
|
|||
}
|
||||
|
||||
prepareDerivedData() {
|
||||
super.prepareDerivedData();
|
||||
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 resource = changes.system.resources[key];
|
||||
if (resource.value !== undefined && resource.value !== this.resources[key].value) {
|
||||
acc.push(getScrollTextData(this.resources, resource, key));
|
||||
acc.push(getScrollTextData(this.parent, resource, key));
|
||||
}
|
||||
|
||||
return acc;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
|||
import DhLevelData from '../levelData.mjs';
|
||||
import { commonActorRules } from './base.mjs';
|
||||
import DhCreature from './creature.mjs';
|
||||
import { attributeField, resourceField, stressDamageReductionRule, bonusField } from '../fields/actorField.mjs';
|
||||
import { attributeField, stressDamageReductionRule, bonusField } from '../fields/actorField.mjs';
|
||||
import { ActionField } from '../fields/actionField.mjs';
|
||||
import DHCharacterSettings from '../../applications/sheets-configs/character-settings.mjs';
|
||||
|
||||
|
|
@ -27,28 +27,6 @@ export default class DhCharacter extends DhCreature {
|
|||
|
||||
return {
|
||||
...super.defineSchema(),
|
||||
resources: new fields.SchemaField({
|
||||
hitPoints: resourceField(
|
||||
0,
|
||||
0,
|
||||
'DAGGERHEART.GENERAL.HitPoints.plural',
|
||||
true,
|
||||
'DAGGERHEART.ACTORS.Character.maxHPBonus'
|
||||
),
|
||||
stress: resourceField(6, 0, 'DAGGERHEART.GENERAL.stress', true),
|
||||
hope: new fields.SchemaField(
|
||||
{
|
||||
value: new fields.NumberField({
|
||||
initial: 2,
|
||||
min: 0,
|
||||
integer: true,
|
||||
label: 'DAGGERHEART.GENERAL.hope'
|
||||
}),
|
||||
isReversed: new fields.BooleanField({ initial: false })
|
||||
},
|
||||
{ label: 'DAGGERHEART.GENERAL.hope' }
|
||||
)
|
||||
}),
|
||||
traits: new fields.SchemaField({
|
||||
agility: attributeField('DAGGERHEART.CONFIG.Traits.agility.name'),
|
||||
strength: attributeField('DAGGERHEART.CONFIG.Traits.strength.name'),
|
||||
|
|
@ -609,6 +587,7 @@ export default class DhCharacter extends DhCreature {
|
|||
}
|
||||
|
||||
prepareBaseData() {
|
||||
super.prepareBaseData();
|
||||
this.evasion += this.class.value?.system?.evasion ?? 0;
|
||||
|
||||
const currentLevel = this.levelData.level.current;
|
||||
|
|
@ -680,6 +659,7 @@ export default class DhCharacter extends DhCreature {
|
|||
}
|
||||
|
||||
prepareDerivedData() {
|
||||
super.prepareDerivedData();
|
||||
let baseHope = this.resources.hope.value;
|
||||
if (this.companion) {
|
||||
for (let levelKey in this.companion.system.levelData.levelups) {
|
||||
|
|
@ -699,6 +679,7 @@ export default class DhCharacter extends DhCreature {
|
|||
this.attack.roll.trait = this.rules.attack.roll.trait ?? this.attack.roll.trait;
|
||||
|
||||
this.resources.armor = {
|
||||
label: 'DAGGERHEART.GENERAL.armor',
|
||||
value: this.armor?.system?.marks?.value ?? 0,
|
||||
max: this.armorScore,
|
||||
isReversed: true
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
|||
import { ActionField } from '../fields/actionField.mjs';
|
||||
import { adjustDice, adjustRange } from '../../helpers/utils.mjs';
|
||||
import DHCompanionSettings from '../../applications/sheets-configs/companion-settings.mjs';
|
||||
import { resourceField, bonusField } from '../fields/actorField.mjs';
|
||||
import { bonusField } from '../fields/actorField.mjs';
|
||||
|
||||
export default class DhCompanion extends DhCreature {
|
||||
static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Companion'];
|
||||
|
|
@ -26,10 +26,6 @@ export default class DhCompanion extends DhCreature {
|
|||
return {
|
||||
...super.defineSchema(),
|
||||
partner: new ForeignDocumentUUIDField({ type: 'Actor' }),
|
||||
resources: new fields.SchemaField({
|
||||
stress: resourceField(3, 0, 'DAGGERHEART.GENERAL.stress', true),
|
||||
hope: new fields.NumberField({ initial: 0, integer: true, label: 'DAGGERHEART.GENERAL.hope' })
|
||||
}),
|
||||
evasion: new fields.NumberField({
|
||||
required: true,
|
||||
min: 1,
|
||||
|
|
@ -127,6 +123,7 @@ export default class DhCompanion extends DhCreature {
|
|||
}
|
||||
|
||||
prepareBaseData() {
|
||||
super.prepareBaseData();
|
||||
this.attack.roll.bonus = this.partner?.system?.spellcastModifier ?? 0;
|
||||
|
||||
for (let levelKey in this.levelData.levelups) {
|
||||
|
|
@ -161,6 +158,7 @@ export default class DhCompanion extends DhCreature {
|
|||
}
|
||||
|
||||
prepareDerivedData() {
|
||||
super.prepareDerivedData();
|
||||
/* Partner Related Setup */
|
||||
if (this.partner) {
|
||||
this.levelData.level.changed = this.partner.system.levelData.level.current;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { ResourcesField } from '../fields/actorField.mjs';
|
||||
import BaseDataActor from './base.mjs';
|
||||
|
||||
export default class DhCreature extends BaseDataActor {
|
||||
|
|
@ -7,6 +8,7 @@ export default class DhCreature extends BaseDataActor {
|
|||
|
||||
return {
|
||||
...super.defineSchema(),
|
||||
resources: new ResourcesField(this.metadata.type),
|
||||
advantageSources: new fields.ArrayField(new fields.StringField(), {
|
||||
label: 'DAGGERHEART.ACTORS.Character.advantageSources.label',
|
||||
hint: 'DAGGERHEART.ACTORS.Character.advantageSources.hint'
|
||||
|
|
@ -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 MacroField } from './macroField.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 })
|
||||
});
|
||||
|
||||
const resourceField = (max = 0, initial = 0, label, reverse = false, maxLabel) =>
|
||||
new fields.SchemaField(
|
||||
{
|
||||
value: new fields.NumberField({ initial: initial, min: 0, integer: true, label }),
|
||||
max: new fields.NumberField({
|
||||
initial: max,
|
||||
integer: true,
|
||||
label:
|
||||
maxLabel ??
|
||||
game.i18n.format('DAGGERHEART.GENERAL.maxWithThing', { thing: game.i18n.localize(label) })
|
||||
}),
|
||||
isReversed: new fields.BooleanField({ initial: reverse })
|
||||
},
|
||||
{ label }
|
||||
);
|
||||
|
||||
const stressDamageReductionRule = localizationPath =>
|
||||
new fields.SchemaField({
|
||||
cost: new fields.NumberField({
|
||||
|
|
@ -37,4 +21,67 @@ const bonusField = label =>
|
|||
dice: new fields.ArrayField(new fields.StringField(), { label: `${game.i18n.localize(label)} Dice` })
|
||||
});
|
||||
|
||||
export { attributeField, resourceField, stressDamageReductionRule, bonusField };
|
||||
/**
|
||||
* Field used for actor resources. It is a resource that validates dynamically based on the config.
|
||||
* Because "max" may be defined during runtime, we don't attempt to clamp the maximum value.
|
||||
*/
|
||||
class ResourcesField extends fields.TypedObjectField {
|
||||
constructor(actorType) {
|
||||
super(
|
||||
new fields.SchemaField({
|
||||
value: new fields.NumberField({ min: 0, initial: 0, integer: true }),
|
||||
// Some resources allow changing max. A null max means its the default
|
||||
max: new fields.NumberField({ initial: null, integer: true, nullable: true })
|
||||
})
|
||||
);
|
||||
this.actorType = actorType;
|
||||
}
|
||||
|
||||
getInitialValue() {
|
||||
const resources = CONFIG.DH.RESOURCE[this.actorType].all;
|
||||
return Object.values(resources).reduce((result, resource) => {
|
||||
result[resource.id] = {
|
||||
value: resource.initial,
|
||||
max: null
|
||||
};
|
||||
return result;
|
||||
}, {});
|
||||
}
|
||||
|
||||
_validateKey(key) {
|
||||
return key in CONFIG.DH.RESOURCE[this.actorType].all;
|
||||
}
|
||||
|
||||
_cleanType(value, options) {
|
||||
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(
|
||||
new fields.SchemaField({
|
||||
value: new fields.StringField({
|
||||
required: true,
|
||||
choices: CONFIG.DH.ITEM.allArmorFeatures,
|
||||
blank: true
|
||||
required: true
|
||||
}),
|
||||
effectIds: 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() {
|
||||
const baseDescription = this.description;
|
||||
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(
|
||||
'systems/daggerheart/templates/sheets/items/armor/description.hbs',
|
||||
|
|
|
|||
|
|
@ -224,7 +224,7 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
|||
const armorChanged =
|
||||
changed.system?.marks?.value !== undefined && changed.system.marks.value !== this.marks.value;
|
||||
if (armorChanged && autoSettings.resourceScrollTexts && this.parent.parent?.type === 'character') {
|
||||
const armorData = getScrollTextData(this.parent.parent.system.resources, changed.system.marks, 'armor');
|
||||
const armorData = getScrollTextData(this.parent.parent, changed.system.marks, 'armor');
|
||||
options.scrollingTextData = [armorData];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,9 +38,7 @@ export default class DHWeapon extends AttachableItem {
|
|||
weaponFeatures: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
value: new fields.StringField({
|
||||
required: true,
|
||||
choices: CONFIG.DH.ITEM.allWeaponFeatures,
|
||||
blank: true
|
||||
required: true
|
||||
}),
|
||||
effectIds: 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() {
|
||||
const baseDescription = this.description;
|
||||
|
||||
const tier = game.i18n.localize(`DAGGERHEART.GENERAL.Tiers.${this.tier}`);
|
||||
const trait = game.i18n.localize(CONFIG.DH.ACTOR.abilities[this.attack.roll.trait].label);
|
||||
const range = game.i18n.localize(`DAGGERHEART.CONFIG.Range.${this.attack.range}.name`);
|
||||
const damage = Roll.replaceFormulaData(this.attack.damageFormula, this.parent.parent ?? this.parent);
|
||||
const burden = game.i18n.localize(CONFIG.DH.GENERAL.burden[this.burden].label);
|
||||
|
||||
const allFeatures = CONFIG.DH.ITEM.allWeaponFeatures();
|
||||
const features = this.weaponFeatures.map(x => allFeatures[x.value]);
|
||||
const features = this.weaponFeatures.map(x => allFeatures[x.value]).filter(x => x);
|
||||
|
||||
const prefix = await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/sheets/items/weapon/description.hbs',
|
||||
{
|
||||
features,
|
||||
tier,
|
||||
trait,
|
||||
range,
|
||||
damage,
|
||||
burden
|
||||
item: this,
|
||||
features
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,10 @@ export default class DhAutomation extends foundry.abstract.DataModel {
|
|||
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({
|
||||
required: true,
|
||||
initial: true,
|
||||
|
|
|
|||
|
|
@ -145,6 +145,16 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
|
|||
description: new fields.StringField()
|
||||
})
|
||||
),
|
||||
resources: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
resources: new fields.TypedObjectField(new fields.EmbeddedDataField(Resource))
|
||||
}),
|
||||
{
|
||||
initial: {
|
||||
character: { resources: {} }
|
||||
}
|
||||
}
|
||||
),
|
||||
itemFeatures: new fields.SchemaField({
|
||||
weaponFeatures: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
|
|
@ -185,4 +195,117 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
|
|||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
/** Invoked by the setting when data changes */
|
||||
handleChange() {
|
||||
if (this.maxFear) {
|
||||
if (ui.resources) ui.resources.render({ force: true });
|
||||
}
|
||||
|
||||
this.refreshConfig();
|
||||
this.#resetActors();
|
||||
}
|
||||
|
||||
/** Update config values based on homebrew data. Make sure the references don't change */
|
||||
refreshConfig() {
|
||||
for (const [actorType, actorData] of Object.entries(this.resources)) {
|
||||
const config = CONFIG.DH.RESOURCE[actorType];
|
||||
for (const key of Object.keys(config.all)) {
|
||||
delete config.all[key];
|
||||
}
|
||||
Object.assign(config.all, {
|
||||
...Object.entries(actorData.resources).reduce((result, [key, value]) => {
|
||||
result[key] = value.toObject();
|
||||
result[key].id = key;
|
||||
return result;
|
||||
}, {}),
|
||||
...config.custom,
|
||||
...config.base,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers a reset and non-forced re-render on all given actors (if given)
|
||||
* or all world actors and actors in all scenes to show immediate results for a changed setting.
|
||||
*/
|
||||
#resetActors() {
|
||||
const actors = new Set(
|
||||
[
|
||||
game.actors.contents,
|
||||
game.scenes.contents.flatMap(s => s.tokens.contents).flatMap(t => t.actor ?? [])
|
||||
].flat()
|
||||
);
|
||||
for (const actor of actors) {
|
||||
for (const app of Object.values(actor.apps)) {
|
||||
for (const element of app.element?.querySelectorAll('prose-mirror.active')) {
|
||||
element.open = false; // This triggers a save
|
||||
}
|
||||
}
|
||||
|
||||
actor.reset();
|
||||
actor.render();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class Resource extends foundry.abstract.DataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
initial: new fields.NumberField({
|
||||
required: true,
|
||||
integer: true,
|
||||
initial: 0,
|
||||
min: 0,
|
||||
label: 'DAGGERHEART.GENERAL.initial'
|
||||
}),
|
||||
max: new fields.NumberField({
|
||||
nullable: true,
|
||||
initial: null,
|
||||
min: 0,
|
||||
label: 'DAGGERHEART.GENERAL.max'
|
||||
}),
|
||||
label: new fields.StringField({ label: 'DAGGERHEART.GENERAL.label' }),
|
||||
images: new fields.SchemaField({
|
||||
full: imageIconField('fa solid fa-circle'),
|
||||
empty: imageIconField('fa-regular fa-circle')
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
static getDefaultResourceData = label => {
|
||||
const images = Resource.schema.fields.images.getInitialValue();
|
||||
return {
|
||||
initial: 0,
|
||||
max: 0,
|
||||
label: label ?? '',
|
||||
images
|
||||
};
|
||||
};
|
||||
|
||||
static getDefaultImageData = imageKey => {
|
||||
return Resource.schema.fields.images.fields[imageKey].getInitialValue();
|
||||
};
|
||||
}
|
||||
|
||||
const imageIconField = defaultValue =>
|
||||
new foundry.data.fields.SchemaField(
|
||||
{
|
||||
value: new foundry.data.fields.StringField({
|
||||
initial: defaultValue,
|
||||
label: 'DAGGERHEART.SETTINGS.Homebrew.FIELDS.resources.resources.value.label'
|
||||
}),
|
||||
isIcon: new foundry.data.fields.BooleanField({
|
||||
required: true,
|
||||
initial: true,
|
||||
label: 'DAGGERHEART.SETTINGS.Homebrew.FIELDS.resources.resources.isIcon.label'
|
||||
}),
|
||||
noColorFilter: new foundry.data.fields.BooleanField({
|
||||
required: true,
|
||||
initial: false,
|
||||
label: 'DAGGERHEART.SETTINGS.Homebrew.FIELDS.resources.resources.noColorFilter.label'
|
||||
})
|
||||
},
|
||||
{ required: true }
|
||||
);
|
||||
|
|
|
|||
12
module/data/settings/Metagaming.mjs
Normal file
12
module/data/settings/Metagaming.mjs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
export default class DhMetagaming extends foundry.abstract.DataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
hideObserverPermissionInChat: new fields.BooleanField({
|
||||
initial: false,
|
||||
label: 'DAGGERHEART.SETTINGS.Metagaming.FIELDS.hideObserverPermissionInChat.label',
|
||||
hint: 'DAGGERHEART.SETTINGS.Metagaming.FIELDS.hideObserverPermissionInChat.hint'
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
export { default as DhAppearance } from './Appearance.mjs';
|
||||
export { default as DhAutomation } from './Automation.mjs';
|
||||
export { default as DhHomebrew } from './Homebrew.mjs';
|
||||
export { default as DhMetagaming } from './Metagaming.mjs';
|
||||
export { default as DhVariantRules } from './VariantRules.mjs';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import DamageDialog from '../applications/dialogs/damageDialog.mjs';
|
||||
import { parseRallyDice } from '../helpers/utils.mjs';
|
||||
import { RefreshType, socketEvent } from '../systemRegistration/socket.mjs';
|
||||
import DHRoll from './dhRoll.mjs';
|
||||
|
||||
|
|
@ -33,7 +34,7 @@ export default class DamageRoll extends DHRoll {
|
|||
static async buildPost(roll, config, message) {
|
||||
const chatMessage = 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) {
|
||||
const pool = foundry.dice.terms.PoolTerm.fromRolls(
|
||||
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.blind
|
||||
);
|
||||
config.mute = true;
|
||||
}
|
||||
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) {
|
||||
|
|
@ -192,7 +198,7 @@ export default class DamageRoll extends DHRoll {
|
|||
// Bardic Rally
|
||||
const rallyChoices = config.data?.parent?.appliedEffects.reduce((a, c) => {
|
||||
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;
|
||||
}, []);
|
||||
if (rallyChoices.length) {
|
||||
|
|
|
|||
|
|
@ -143,8 +143,10 @@ export default class DHRoll extends Roll {
|
|||
/** @inheritDoc */
|
||||
async render({ flavor, template = this.constructor.CHAT_TEMPLATE, isPrivate = false, ...options } = {}) {
|
||||
if (!this._evaluated) return;
|
||||
|
||||
const metagamingSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Metagaming);
|
||||
const chatData = await this._prepareChatRenderContext({ flavor, isPrivate, ...options });
|
||||
return foundry.applications.handlebars.renderTemplate(template, chatData);
|
||||
return foundry.applications.handlebars.renderTemplate(template, { ...chatData, metagamingSettings });
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import D20RollDialog from '../applications/dialogs/d20RollDialog.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 { ResourceUpdateMap } from '../data/action/baseAction.mjs';
|
||||
|
||||
|
|
@ -68,7 +68,7 @@ export default class DualityRoll extends D20Roll {
|
|||
setRallyChoices() {
|
||||
return this.data?.parent?.appliedEffects.reduce((a, c) => {
|
||||
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;
|
||||
}, []);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -959,10 +959,23 @@ export default class DhpActor extends Actor {
|
|||
|
||||
/** Get active effects */
|
||||
getActiveEffects() {
|
||||
const conditions = CONFIG.DH.GENERAL.conditions();
|
||||
const statusMap = new Map(foundry.CONFIG.statusEffects.map(status => [status.id, status]));
|
||||
const autoVulnerableActive = this.system.isAutoVulnerableActive;
|
||||
return this.effects
|
||||
.filter(x => !x.disabled)
|
||||
.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);
|
||||
|
||||
const currentStatusActiveEffects = acc.filter(
|
||||
|
|
|
|||
|
|
@ -68,8 +68,11 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
|||
document = fromUuidSync(uuid);
|
||||
if (!document) return;
|
||||
|
||||
e.setAttribute('data-view-perm', document.testUserPermission(game.user, 'OBSERVER'));
|
||||
e.setAttribute('data-use-perm', document.testUserPermission(game.user, 'OWNER'));
|
||||
|
||||
const settings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Metagaming);
|
||||
if (settings.hideObserverPermissionInChat)
|
||||
e.setAttribute('data-view-perm', document.testUserPermission(game.user, 'OBSERVER'));
|
||||
});
|
||||
|
||||
if (this.isContentVisible) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { AdversaryBPPerEncounter, BaseBPPerEncounter } from '../config/encounter
|
|||
export default class DhTooltipManager extends foundry.helpers.interaction.TooltipManager {
|
||||
#wide = false;
|
||||
#bordered = false;
|
||||
#active = false;
|
||||
|
||||
async activate(element, options = {}) {
|
||||
const { TextEditor } = foundry.applications.ux;
|
||||
|
|
@ -168,7 +169,100 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
|
|||
}
|
||||
}
|
||||
|
||||
super.activate(element, { ...options, html: html });
|
||||
this.baseActivate(element, { ...options, html: html });
|
||||
}
|
||||
|
||||
/* Need to pass more options to _setAnchor, so have to copy whole foundry method >_< */
|
||||
async baseActivate(element, options) {
|
||||
let { text, direction, cssClass, locked = false, html, content } = options;
|
||||
if (content && !html) {
|
||||
foundry.utils.logCompatibilityWarning(
|
||||
'The content option has been deprecated in favor of the html option',
|
||||
{ since: 13, until: 15, once: true }
|
||||
);
|
||||
html = content;
|
||||
}
|
||||
if (text && html) throw new Error('Cannot provide both text and html options to TooltipManager#activate.');
|
||||
// Deactivate currently active element
|
||||
this.deactivate();
|
||||
// Check if the element still exists in the DOM.
|
||||
if (!document.body.contains(element)) return;
|
||||
// Mark the new element as active
|
||||
this.#active = true;
|
||||
this.element = element;
|
||||
element.setAttribute('aria-describedby', 'tooltip');
|
||||
html ||= element.dataset.tooltipHtml;
|
||||
if (html) {
|
||||
if (typeof html === 'string') this.tooltip.innerHTML = foundry.utils.cleanHTML(html);
|
||||
else {
|
||||
this.tooltip.innerHTML = ''; // Clear existing HTML
|
||||
this.tooltip.appendChild(html);
|
||||
}
|
||||
} else {
|
||||
text ||= element.dataset.tooltipText;
|
||||
if (text) this.tooltip.textContent = text;
|
||||
else {
|
||||
text = element.dataset.tooltip;
|
||||
// Localized message should be safe
|
||||
if (game.i18n.has(text)) this.tooltip.innerHTML = game.i18n.localize(text);
|
||||
else this.tooltip.innerHTML = foundry.utils.cleanHTML(text);
|
||||
}
|
||||
}
|
||||
|
||||
// Activate display of the tooltip
|
||||
this.tooltip.removeAttribute('class');
|
||||
this.tooltip.classList.add('active', 'themed', 'theme-dark');
|
||||
this.tooltip.showPopover();
|
||||
cssClass ??= element.closest('[data-tooltip-class]')?.dataset.tooltipClass;
|
||||
if (cssClass) this.tooltip.classList.add(...cssClass.split(' '));
|
||||
|
||||
// Set tooltip position
|
||||
direction ??= element.closest('[data-tooltip-direction]')?.dataset.tooltipDirection;
|
||||
if (!direction) direction = this._determineDirection();
|
||||
this._setAnchor(direction, options);
|
||||
|
||||
if (locked || element.dataset.hasOwnProperty('locked')) this.lockTooltip();
|
||||
}
|
||||
|
||||
_setAnchor(direction, options) {
|
||||
const directions = this.constructor.TOOLTIP_DIRECTIONS;
|
||||
const pad = this.constructor.TOOLTIP_MARGIN_PX;
|
||||
const pos = this.element.getBoundingClientRect();
|
||||
|
||||
const { innerHeight, innerWidth } = this.tooltip.ownerDocument.defaultView;
|
||||
const tooltipPadding = 16;
|
||||
const horizontalOffset = options.noOffset ? tooltipPadding : this.tooltip.offsetWidth / 2 - pos.width / 2;
|
||||
const verticalOffset = options.noOffset ? tooltipPadding : this.tooltip.offsetHeight / 2 - pos.height / 2;
|
||||
|
||||
const style = {};
|
||||
switch (direction) {
|
||||
case directions.DOWN:
|
||||
style.textAlign = 'center';
|
||||
style.left = pos.left - horizontalOffset;
|
||||
style.top = pos.bottom + pad;
|
||||
break;
|
||||
case directions.LEFT:
|
||||
style.textAlign = 'left';
|
||||
style.right = innerWidth - pos.left + pad;
|
||||
style.top = pos.top - verticalOffset;
|
||||
break;
|
||||
case directions.RIGHT:
|
||||
style.textAlign = 'right';
|
||||
style.left = pos.right + pad;
|
||||
style.top = pos.top - verticalOffset;
|
||||
break;
|
||||
case directions.UP:
|
||||
style.textAlign = 'center';
|
||||
style.left = pos.left - horizontalOffset;
|
||||
style.bottom = innerHeight - pos.top + pad;
|
||||
break;
|
||||
case directions.CENTER:
|
||||
style.textAlign = 'center';
|
||||
style.left = pos.left - horizontalOffset;
|
||||
style.top = pos.top - verticalOffset;
|
||||
break;
|
||||
}
|
||||
return this._setStyle(style);
|
||||
}
|
||||
|
||||
_determineItemTooltipDirection(element, prefered = this.constructor.TOOLTIP_DIRECTIONS.LEFT) {
|
||||
|
|
@ -270,6 +364,12 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
|
|||
return clone;
|
||||
}
|
||||
|
||||
/**@inheritdoc */
|
||||
dismissLockedTooltips() {
|
||||
super.dismissLockedTooltips();
|
||||
Hooks.callAll(CONFIG.DH.HOOKS.hooksConfig.lockedTooltipDismissed);
|
||||
}
|
||||
|
||||
/** Get HTML for Battlepoints tooltip */
|
||||
async getBattlepointHTML(combatId) {
|
||||
const combat = game.combats.get(combatId);
|
||||
|
|
|
|||
|
|
@ -119,8 +119,8 @@ export const tagifyElement = (element, baseOptions, onChange, tagifyOptions = {}
|
|||
}),
|
||||
maxTags: typeof maxTags === 'function' ? maxTags() : maxTags,
|
||||
dropdown: {
|
||||
searchKeys: ['value', 'name'],
|
||||
mapValueTo: 'name',
|
||||
searchKeys: ['value'],
|
||||
enabled: 0,
|
||||
maxItems: 100,
|
||||
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 function getScrollTextData(resources, resource, key) {
|
||||
const { reversed, label } = CONFIG.DH.ACTOR.scrollingTextResource[key];
|
||||
export function getScrollTextData(actor, resource, key) {
|
||||
const { BOTTOM, TOP } = CONST.TEXT_ANCHOR_POINTS;
|
||||
|
||||
const resources = actor.system.resources;
|
||||
const increased = resources[key].value < resource.value;
|
||||
const value = -1 * (resources[key].value - resource.value);
|
||||
const { label, isReversed } = resources[key];
|
||||
|
||||
const text = `${game.i18n.localize(label)} ${value.signedString()}`;
|
||||
|
||||
const stroke = increased ? (reversed ? 0xffffff : 0x000000) : reversed ? 0x000000 : 0xffffff;
|
||||
const fill = increased ? (reversed ? 0x0032b1 : 0xffe760) : reversed ? 0xffe760 : 0x0032b1;
|
||||
const direction = increased ? (reversed ? BOTTOM : TOP) : reversed ? TOP : BOTTOM;
|
||||
const stroke = increased ? (isReversed ? 0xffffff : 0x000000) : isReversed ? 0x000000 : 0xffffff;
|
||||
const fill = increased ? (isReversed ? 0x0032b1 : 0xffe760) : isReversed ? 0xffe760 : 0x0032b1;
|
||||
const direction = increased ? (isReversed ? BOTTOM : TOP) : isReversed ? TOP : BOTTOM;
|
||||
|
||||
return { text, stroke, fill, direction };
|
||||
}
|
||||
|
|
@ -472,7 +473,7 @@ export function refreshIsAllowed(allowedTypes, typeToCheck) {
|
|||
case CONFIG.DH.GENERAL.refreshTypes.scene.id:
|
||||
case CONFIG.DH.GENERAL.refreshTypes.session.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:
|
||||
return allowedTypes.some(
|
||||
x =>
|
||||
|
|
@ -557,3 +558,121 @@ export function calculateExpectedValue(formulaOrTerms) {
|
|||
: [formulaOrTerms];
|
||||
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/die.hbs',
|
||||
'systems/daggerheart/templates/sheets/global/partials/resource-bar.hbs',
|
||||
'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs',
|
||||
'systems/daggerheart/templates/sheets/global/partials/item-tags.hbs',
|
||||
'systems/daggerheart/templates/components/card-preview.hbs',
|
||||
'systems/daggerheart/templates/levelup/parts/selectable-card-preview.hbs',
|
||||
'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs',
|
||||
'systems/daggerheart/templates/ui/combatTracker/combatTrackerSection.hbs',
|
||||
'systems/daggerheart/templates/actionTypes/damage.hbs',
|
||||
'systems/daggerheart/templates/actionTypes/resource.hbs',
|
||||
|
|
@ -33,6 +34,7 @@ export const preloadHandlebarsTemplates = async function () {
|
|||
'systems/daggerheart/templates/actionTypes/beastform.hbs',
|
||||
'systems/daggerheart/templates/actionTypes/countdown.hbs',
|
||||
'systems/daggerheart/templates/actionTypes/summon.hbs',
|
||||
'systems/daggerheart/templates/actionTypes/transform.hbs',
|
||||
'systems/daggerheart/templates/settings/components/settings-item-line.hbs',
|
||||
'systems/daggerheart/templates/ui/tooltip/parts/tooltipChips.hbs',
|
||||
'systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs',
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import { defaultLevelTiers, DhLevelTiers } from '../data/levelTier.mjs';
|
||||
import DhCountdowns from '../data/countdowns.mjs';
|
||||
import { DhAppearance, DhAutomation, DhHomebrew, DhVariantRules } from '../data/settings/_module.mjs';
|
||||
import { DhAppearance, DhAutomation, DhHomebrew, DhMetagaming, DhVariantRules } from '../data/settings/_module.mjs';
|
||||
import {
|
||||
DhAppearanceSettings,
|
||||
DhAutomationSettings,
|
||||
DhHomebrewSettings,
|
||||
DhMetagamingSettings,
|
||||
DhVariantRuleSettings
|
||||
} from '../applications/settings/_module.mjs';
|
||||
import { CompendiumBrowserSettings, DhTagTeamRoll } from '../data/_module.mjs';
|
||||
|
|
@ -38,17 +39,18 @@ const registerMenuSettings = () => {
|
|||
type: DhAutomation
|
||||
});
|
||||
|
||||
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Metagaming, {
|
||||
scope: 'world',
|
||||
config: false,
|
||||
type: DhMetagaming
|
||||
});
|
||||
|
||||
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, {
|
||||
scope: 'world',
|
||||
config: false,
|
||||
type: DhHomebrew,
|
||||
onChange: value => {
|
||||
if (value.maxFear) {
|
||||
if (ui.resources) ui.resources.render({ force: true });
|
||||
}
|
||||
|
||||
// Some homebrew settings may change sheets in various ways, so trigger a re-render
|
||||
resetActors();
|
||||
value.handleChange();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -76,6 +78,16 @@ const registerMenus = () => {
|
|||
type: DhAutomationSettings,
|
||||
restricted: true
|
||||
});
|
||||
|
||||
game.settings.registerMenu(CONFIG.DH.id, CONFIG.DH.SETTINGS.menu.Metagaming.Name, {
|
||||
name: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.metagaming.name'),
|
||||
label: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.metagaming.label'),
|
||||
hint: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.metagaming.hint'),
|
||||
icon: CONFIG.DH.SETTINGS.menu.Metagaming.Icon,
|
||||
type: DhMetagamingSettings,
|
||||
restricted: true
|
||||
});
|
||||
|
||||
game.settings.registerMenu(CONFIG.DH.id, CONFIG.DH.SETTINGS.menu.Homebrew.Name, {
|
||||
name: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.homebrew.name'),
|
||||
label: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.homebrew.label'),
|
||||
|
|
@ -144,30 +156,8 @@ const registerNonConfigSettings = () => {
|
|||
});
|
||||
|
||||
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.CompendiumBrowserSettings, {
|
||||
scope: 'client',
|
||||
scope: 'world',
|
||||
config: false,
|
||||
type: CompendiumBrowserSettings
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Triggers a reset and non-forced re-render on all given actors (if given)
|
||||
* or all world actors and actors in all scenes to show immediate results for a changed setting.
|
||||
*/
|
||||
function resetActors(actors) {
|
||||
actors ??= [
|
||||
game.actors.contents,
|
||||
game.scenes.contents.flatMap(s => s.tokens.contents).flatMap(t => t.actor ?? [])
|
||||
].flat();
|
||||
actors = new Set(actors);
|
||||
for (const actor of actors) {
|
||||
for (const app of Object.values(actor.apps)) {
|
||||
for (const element of app.element?.querySelectorAll('prose-mirror.active')) {
|
||||
element.open = false; // This triggers a save
|
||||
}
|
||||
}
|
||||
|
||||
actor.reset();
|
||||
actor.render();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -717,7 +717,35 @@
|
|||
"system": {
|
||||
"description": "<p>When the @Lookup[@name] marks their last HP, replace them with the @UUID[Compendium.daggerheart.adversaries.Actor.RXkZTwBRi4dJ3JE5]{Fallen Warlord: Undefeated Champion} and immediately spotlight them.</p>",
|
||||
"resource": null,
|
||||
"actions": {},
|
||||
"actions": {
|
||||
"gP426WmWbtrZEWCD": {
|
||||
"type": "transform",
|
||||
"_id": "gP426WmWbtrZEWCD",
|
||||
"systemPath": "actions",
|
||||
"baseAction": false,
|
||||
"description": "",
|
||||
"chatDisplay": true,
|
||||
"originItem": {
|
||||
"type": "itemCollection"
|
||||
},
|
||||
"actionType": "action",
|
||||
"triggers": [],
|
||||
"cost": [],
|
||||
"uses": {
|
||||
"value": null,
|
||||
"max": null,
|
||||
"recovery": null,
|
||||
"consumeOnSuccess": false
|
||||
},
|
||||
"transform": {
|
||||
"actorUUID": "Compendium.daggerheart.adversaries.Actor.RXkZTwBRi4dJ3JE5",
|
||||
"resourceRefresh": {
|
||||
"hitPoints": true,
|
||||
"stress": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"originItemType": null,
|
||||
"originId": null,
|
||||
"featureForm": "reaction"
|
||||
|
|
|
|||
|
|
@ -846,7 +846,37 @@
|
|||
"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>",
|
||||
"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,
|
||||
"originId": null,
|
||||
"featureForm": "reaction"
|
||||
|
|
|
|||
|
|
@ -742,7 +742,37 @@
|
|||
"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>",
|
||||
"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,
|
||||
"originId": null,
|
||||
"featureForm": "reaction"
|
||||
|
|
|
|||
|
|
@ -20,10 +20,6 @@
|
|||
{
|
||||
"type": "class",
|
||||
"item": "Compendium.daggerheart.classes.Item.PydiMnNCKpd44SGS"
|
||||
},
|
||||
{
|
||||
"type": "class",
|
||||
"item": "Compendium.daggerheart.classes.Item.TVeEyqmPPiRa2r3i"
|
||||
}
|
||||
],
|
||||
"subclasses": [
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@
|
|||
{
|
||||
"key": "system.bonuses.rally",
|
||||
"mode": 2,
|
||||
"value": "d6",
|
||||
"value": "6 + min((floor(@system.levelData.level.current / 5)*2), 2)",
|
||||
"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",
|
||||
"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,
|
||||
"damageMod": "none"
|
||||
},
|
||||
"name": "Agility Check",
|
||||
"name": "Agility Roll",
|
||||
"img": "icons/skills/melee/sword-engraved-glow-purple.webp",
|
||||
"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 */
|
||||
|
|
|
|||
|
|
@ -103,10 +103,9 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: end;
|
||||
gap: 8px;
|
||||
|
||||
a {
|
||||
width: 15px;
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
|
@ -139,24 +138,6 @@
|
|||
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 {
|
||||
|
|
@ -275,8 +256,10 @@
|
|||
grid-area: controls;
|
||||
align-self: start;
|
||||
padding-top: 0.3125rem;
|
||||
gap: 4px;
|
||||
margin-bottom: -1px;
|
||||
a {
|
||||
width: 18px;
|
||||
}
|
||||
}
|
||||
> .item-labels {
|
||||
align-self: start;
|
||||
|
|
@ -334,6 +317,27 @@
|
|||
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 {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
|
|||
|
|
@ -4,48 +4,78 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
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;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
border-radius: 3px;
|
||||
|
||||
.actor-summon-name {
|
||||
flex: 2;
|
||||
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;
|
||||
}
|
||||
img {
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.summon-dragger {
|
||||
.actor-drop-controls {
|
||||
flex: 1;
|
||||
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);
|
||||
gap: 5px;
|
||||
|
||||
&.transform {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.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 {
|
||||
|
|
|
|||
|
|
@ -183,6 +183,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
.domain-details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.level-details {
|
||||
align-self: center;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,8 +133,19 @@
|
|||
padding: 0;
|
||||
margin-bottom: 15px;
|
||||
|
||||
.hope-section {
|
||||
.resource-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
margin-right: 20px;
|
||||
|
||||
.resource-manager {
|
||||
transition: all 0.1s ease;
|
||||
|
||||
&.inverted {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.downtime-section {
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@
|
|||
.daggerheart,
|
||||
#chat-notifications {
|
||||
.chat-message {
|
||||
--text-color: @golden;
|
||||
--text-color: light-dark(@dark-blue, @golden);
|
||||
--bg-color: @golden-40;
|
||||
|
||||
[data-use-perm='false'] {
|
||||
|
|
@ -233,7 +233,7 @@
|
|||
font-family: @font-subtitle;
|
||||
font-size: var(--font-size-18);
|
||||
font-weight: bold;
|
||||
color: light-dark(@dark-blue, var(--text-color));
|
||||
color: var(--text-color);
|
||||
margin-bottom: -2px;
|
||||
}
|
||||
|
||||
|
|
@ -450,6 +450,10 @@
|
|||
|
||||
.target-data {
|
||||
flex: 1;
|
||||
|
||||
.target-name {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.target-save {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
@import './settings/settings.less';
|
||||
@import './settings/homebrew-settings/domains.less';
|
||||
@import './settings/homebrew-settings/types.less';
|
||||
@import './settings/homebrew-settings/resources.less';
|
||||
|
||||
@import './sidebar/tabs.less';
|
||||
@import './sidebar/daggerheartMenu.less';
|
||||
|
|
|
|||
|
|
@ -304,7 +304,15 @@
|
|||
padding: 0 0 0 50px;
|
||||
display: flex;
|
||||
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 {
|
||||
font-size: var(--font-size-32);
|
||||
|
|
@ -350,6 +358,7 @@
|
|||
.filter-content,
|
||||
.item-desc {
|
||||
display: grid;
|
||||
opacity: 0;
|
||||
grid-template-rows: 0fr;
|
||||
transition: all 0.3s ease-in-out;
|
||||
width: 100%;
|
||||
|
|
@ -378,8 +387,8 @@
|
|||
}
|
||||
|
||||
.expanded + .extensible {
|
||||
opacity: 1;
|
||||
grid-template-rows: 1fr;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.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 './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",
|
||||
"title": "Daggerheart",
|
||||
"description": "An unofficial implementation of the Daggerheart system",
|
||||
"version": "1.7.2",
|
||||
"version": "1.9.0",
|
||||
"compatibility": {
|
||||
"minimum": "13.346",
|
||||
"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>
|
||||
{{localize "DAGGERHEART.ACTIONS.TYPES.summon.name"}}
|
||||
</legend>
|
||||
|
||||
<ul class="actor-summon-items">
|
||||
{{#each @root.summons as |summon index|}}
|
||||
<li class="actor-summon-line">
|
||||
<div class="actor-summon-name">
|
||||
<li class="actor-drop-line">
|
||||
<div class="actor-drop-name">
|
||||
<img class="image" src="{{summon.actor.img}}" />
|
||||
<h4 class="h4">
|
||||
{{summon.actor.name}}
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<div class="actor-summon-controls">
|
||||
<div class="actor-drop-controls">
|
||||
<div class="form-group summon-count-wrapper" data-index="{{index}}">
|
||||
<div class="form-fields">
|
||||
<input type="text" value="{{summon.count}}" />
|
||||
|
|
@ -43,7 +43,7 @@
|
|||
</div>
|
||||
</li>
|
||||
{{/each}}
|
||||
<div class="summon-dragger">
|
||||
<div class="drop-dragger">
|
||||
<span>{{localize "DAGGERHEART.ACTIONS.Settings.summon.dropSummonsHere"}}</span>
|
||||
</div>
|
||||
</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}}
|
||||
</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.actionPoints value=settingFields._source.actionPoints 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.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.transform}}{{> 'systems/daggerheart/templates/actionTypes/transform.hbs' fields=fields.transform.fields source=source.transform}}{{/if}}
|
||||
</section>
|
||||
|
|
@ -20,15 +20,11 @@
|
|||
|
||||
<div class="fieldsets-section">
|
||||
<fieldset class="flex">
|
||||
<legend>{{localize "DAGGERHEART.GENERAL.HitPoints.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")}}
|
||||
{{formGroup systemFields.resources.fields.hitPoints.fields.max value=document._source.system.resources.hitPoints.max label=(localize "DAGGERHEART.ACTORS.Adversary.FIELDS.resources.hitPoints.max.label")}}
|
||||
</fieldset>
|
||||
<fieldset class="flex">
|
||||
<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>
|
||||
<legend>{{localize "DAGGERHEART.GENERAL.Resource.plural"}}</legend>
|
||||
{{#each resources as |resource|}}
|
||||
{{formGroup resource.field value=resource.value name=resource.name}}
|
||||
{{/each}}
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<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.severe value=document._source.system.damageThresholds.severe label=(localize "DAGGERHEART.GENERAL.DamageThresholds.severeThreshold")}}
|
||||
</fieldset>
|
||||
</section>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -22,15 +22,12 @@
|
|||
<legend>{{localize 'DAGGERHEART.GENERAL.basics'}}</legend>
|
||||
|
||||
<div class="two-columns even">
|
||||
{{formGroup systemFields.resources.fields.hitPoints.fields.value value=document._source.system.resources.hitPoints.value localize=true}}
|
||||
<span data-tooltip-text="{{localize "DAGGERHEART.UI.Tooltip.maxHPClassBound"}}">
|
||||
{{formGroup systemFields.resources.fields.hitPoints.fields.max value=document._source.system.resources.hitPoints.max localize=true}}
|
||||
</span>
|
||||
{{#each resources as |resource|}}
|
||||
<span {{#if resource.tooltip}}data-tooltip-text="{{resource.tooltip}}"{{/if}}>
|
||||
{{formGroup resource.field value=resource.value name=resource.name}}
|
||||
</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.proficiency value=document._source.system.proficiency localize=true}}
|
||||
|
|
@ -39,4 +36,4 @@
|
|||
</span>
|
||||
</div>
|
||||
</fieldset>
|
||||
</section>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -7,8 +7,9 @@
|
|||
<legend>{{localize 'DAGGERHEART.GENERAL.basics'}}</legend>
|
||||
<div class="nest-inputs">
|
||||
{{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}}
|
||||
{{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}}
|
||||
{{#each resources as |resource|}}
|
||||
{{formGroup resource.field value=resource.value name=resource.name}}
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-fields">
|
||||
|
|
@ -19,4 +20,4 @@
|
|||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</section>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -4,19 +4,21 @@
|
|||
{{#each document.system.sheetLists as |category|}}
|
||||
{{#if (eq category.type 'feature' )}}
|
||||
{{> 'daggerheart.inventory-items'
|
||||
title=category.title
|
||||
type='feature'
|
||||
collection=category.values
|
||||
canCreate=true
|
||||
showActions=true
|
||||
title=category.title
|
||||
type='feature'
|
||||
actorType='character'
|
||||
collection=category.values
|
||||
canCreate=true
|
||||
showActions=true
|
||||
}}
|
||||
{{else if category.values}}
|
||||
{{> 'daggerheart.inventory-items'
|
||||
title=category.title
|
||||
type='feature'
|
||||
collection=category.values
|
||||
canCreate=false
|
||||
showActions=true
|
||||
title=category.title
|
||||
type='feature'
|
||||
actorType='character'
|
||||
collection=category.values
|
||||
canCreate=false
|
||||
showActions=true
|
||||
}}
|
||||
|
||||
{{/if}}
|
||||
|
|
|
|||
|
|
@ -65,22 +65,25 @@
|
|||
</div>
|
||||
|
||||
<div class="character-row">
|
||||
<div class="hope-section">
|
||||
<h4>{{localize "DAGGERHEART.GENERAL.hope"}}</h4>
|
||||
{{#times document.system.resources.hope.max}}
|
||||
<span class='hope-value' data-action='toggleHope' data-value="{{add this 1}}">
|
||||
{{#if (gte ../document.system.resources.hope.value (add this 1))}}
|
||||
<i class='fa-solid fa-diamond'></i>
|
||||
{{else}}
|
||||
<i class='fa-regular fa-circle'></i>
|
||||
{{/if}}
|
||||
</span>
|
||||
{{/times}}
|
||||
{{#times document.system.scars}}
|
||||
<span class='hope-value scar'>
|
||||
<i class='fa-regular fa-ban'></i>
|
||||
</span>
|
||||
{{/times}}
|
||||
<div class="resource-section">
|
||||
<div class="hope-section">
|
||||
<h4>{{localize "DAGGERHEART.GENERAL.hope"}}</h4>
|
||||
{{#times document.system.resources.hope.max}}
|
||||
<span class='hope-value' data-action='toggleHope' data-value="{{add this 1}}">
|
||||
{{#if (gte ../document.system.resources.hope.value (add this 1))}}
|
||||
<i class='fa-solid fa-diamond'></i>
|
||||
{{else}}
|
||||
<i class='fa-regular fa-circle'></i>
|
||||
{{/if}}
|
||||
</span>
|
||||
{{/times}}
|
||||
{{#times document.system.scars}}
|
||||
<span class='hope-value scar'>
|
||||
<i class='fa-regular fa-ban'></i>
|
||||
</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>
|
||||
{{#if document.system.class.value}}
|
||||
<div class="domains-section">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
<li class="card-item" data-item-uuid="{{item.uuid}}" data-type="domainCard">
|
||||
<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="menu {{#if item.system.resource}}resource-menu{{/if}} {{#if (eq item.system.resource.type 'diceValue')}}dice-menu{{/if}}">
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ Parameters:
|
|||
<legend>
|
||||
{{localize title}}
|
||||
{{#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}}"
|
||||
{{#if inVault}}data-in-vault="{{inVault}}"{{/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>
|
||||
|
||||
{{!-- Tags Start --}}
|
||||
{{#with item}}
|
||||
{{#if (not ../hideTags)}}
|
||||
<div class="item-tags">
|
||||
{{#each this._getTags as |tag|}}
|
||||
<div class="tag">
|
||||
{{tag}}
|
||||
</div>
|
||||
{{/each}}
|
||||
|
||||
{{!-- 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 (not ../hideTags)}}
|
||||
{{#> "systems/daggerheart/templates/sheets/global/partials/item-tags.hbs" item }}
|
||||
{{#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}}
|
||||
{{/ "systems/daggerheart/templates/sheets/global/partials/item-tags.hbs"}}
|
||||
{{/if}}
|
||||
{{!-- Feature Form Tag End --}}
|
||||
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/with}}
|
||||
{{!--Tags End --}}
|
||||
</div>
|
||||
|
||||
|
|
@ -109,7 +99,7 @@ Parameters:
|
|||
{{else if (eq type 'armor')}}
|
||||
<a class="{{#unless item.system.equipped}}unequipped{{/unless}}" data-action="toggleEquipItem"
|
||||
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>
|
||||
{{/if}}
|
||||
{{#if (eq type 'domainCard')}}
|
||||
|
|
@ -125,7 +115,7 @@ Parameters:
|
|||
{{/if}}
|
||||
{{#if (hasProperty item "toChat")}}
|
||||
<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>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
|
|
@ -138,7 +128,7 @@ Parameters:
|
|||
{{/unless}}
|
||||
{{#unless hideContextMenu}}
|
||||
<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>
|
||||
{{/unless}}
|
||||
{{/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="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}}
|
||||
<div class="item-description-container">
|
||||
<h4>{{localize "DAGGERHEART.GENERAL.features"}}</h4>
|
||||
{{#each features as | feature |}}
|
||||
<div><strong>{{localize feature.label}}</strong>: {{{localize feature.description}}}</div>
|
||||
{{/each}}
|
||||
|
|
|
|||
|
|
@ -1,36 +1,6 @@
|
|||
<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}}
|
||||
<div class="item-description-container">
|
||||
<h4>{{localize "DAGGERHEART.GENERAL.features"}}</h4>
|
||||
{{#each features as | feature |}}
|
||||
<div><strong>{{localize feature.label}}</strong>: {{{localize feature.description}}}</div>
|
||||
{{/each}}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<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>
|
||||
{{#if isGM}}
|
||||
{{#if (or isGM (not metagamingSettings.hideObserverPermissionInChat))}}
|
||||
<div class="roll-part-extra on-reduced">
|
||||
<div class="wrapper">
|
||||
{{#if (or (gt targetShort.hit 0) (gt targetShort.miss 0))}}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,10 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="item-desc extensible">
|
||||
<span class="wrapper">{{{system.enrichedDescription}}}</span>
|
||||
<span class="wrapper">
|
||||
{{{system.enrichedTags}}}
|
||||
{{{system.enrichedDescription}}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
<i class="fa-solid fa-bolt"></i>
|
||||
</span>
|
||||
<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="">
|
||||
{{/with}}
|
||||
</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