diff --git a/daggerheart.mjs b/daggerheart.mjs
index 9239d338..872da5f3 100644
--- a/daggerheart.mjs
+++ b/daggerheart.mjs
@@ -3,7 +3,7 @@ import * as applications from './module/applications/_module.mjs';
import * as models from './module/data/_module.mjs';
import * as documents from './module/documents/_module.mjs';
import RegisterHandlebarsHelpers from './module/helpers/handlebarsHelper.mjs';
-import { DhDualityRollEnricher, DhTemplateEnricher } from './module/enrichers/_module.mjs';
+import { enricherConfig, enricherRenderSetup } from './module/enrichers/_module.mjs';
import { getCommandTarget, rollCommandToJSON } from './module/helpers/utils.mjs';
import { NarrativeCountdowns } from './module/applications/ui/countdowns.mjs';
import { DualityRollColor } from './module/data/settings/Appearance.mjs';
@@ -20,6 +20,7 @@ import { placeables } from './module/canvas/_module.mjs';
import { registerRollDiceHooks } from './module/dice/dhRoll.mjs';
import { registerDHActorHooks } from './module/documents/actor.mjs';
import './node_modules/@yaireo/tagify/dist/tagify.css';
+import { renderDamageButton } from './module/enrichers/DamageEnricher.mjs';
Hooks.once('init', () => {
CONFIG.DH = SYSTEM;
@@ -29,18 +30,7 @@ Hooks.once('init', () => {
documents
};
- CONFIG.TextEditor.enrichers.push(
- ...[
- {
- pattern: /\[\[\/dr\s?(.*?)\]\]/g,
- enricher: DhDualityRollEnricher
- },
- {
- pattern: /^@Template\[(.*)\]$/g,
- enricher: DhTemplateEnricher
- }
- ]
- );
+ CONFIG.TextEditor.enrichers.push(...enricherConfig);
CONFIG.statusEffects = [
...CONFIG.statusEffects.filter(x => !['dead', 'unconscious'].includes(x.id)),
@@ -178,33 +168,15 @@ Hooks.on('ready', () => {
Hooks.once('dicesoniceready', () => {});
Hooks.on('renderChatMessageHTML', (_, element) => {
- element
- .querySelectorAll('.duality-roll-button')
- .forEach(element => element.addEventListener('click', renderDualityButton));
-
- element
- .querySelectorAll('.measured-template-button')
- .forEach(element => element.addEventListener('click', renderMeasuredTemplate));
+ enricherRenderSetup(element);
});
Hooks.on('renderJournalEntryPageProseMirrorSheet', (_, element) => {
- element
- .querySelectorAll('.duality-roll-button')
- .forEach(element => element.addEventListener('click', renderDualityButton));
-
- element
- .querySelectorAll('.measured-template-button')
- .forEach(element => element.addEventListener('click', renderMeasuredTemplate));
+ enricherRenderSetup(element);
});
Hooks.on('renderHandlebarsApplication', (_, element) => {
- element
- .querySelectorAll('.duality-roll-button')
- .forEach(element => element.addEventListener('click', renderDualityButton));
-
- element
- .querySelectorAll('.measured-template-button')
- .forEach(element => element.addEventListener('click', renderMeasuredTemplate));
+ enricherRenderSetup(element);
});
Hooks.on('chatMessage', (_, message) => {
diff --git a/lang/en.json b/lang/en.json
index 4d3927bc..ae6ccdf4 100755
--- a/lang/en.json
+++ b/lang/en.json
@@ -13,6 +13,9 @@
"armor": "Armor",
"beastform": "Beastform"
},
+ "ActiveEffect": {
+ "beastform": "Beastform"
+ },
"Actor": {
"character": "Character",
"companion": "Companion",
@@ -110,8 +113,25 @@
"horderHp": "Horde/HP"
},
"Character": {
+ "advantageSources": {
+ "label": "Advantage Sources",
+ "hint": "Add single words or short text as reminders and hints of what a character has advantage on."
+ },
"age": "Age",
"companionFeatures": "Companion Features",
+ "contextMenu": {
+ "consume": "Consume Item",
+ "equip": "Equip",
+ "sendToChat": "Send To Chat",
+ "toLoadout": "Send to Loadout",
+ "toVault": "Send to Vault",
+ "unequip": "Unequip",
+ "useItem": "Use Item"
+ },
+ "disadvantageSources": {
+ "label": "Disadvantage Sources",
+ "hint": "Add single words or short text as reminders and hints of what a character has disadvantage on."
+ },
"faith": "Faith",
"levelUp": "You can level up",
"pronouns": "Pronouns",
@@ -572,6 +592,11 @@
"description": "You reduce incoming magic damage by your Armor Score before applying it to your damage thresholds."
}
},
+ "BeastformType": {
+ "normal": "Normal",
+ "evolved": "Evolved",
+ "hybrid": "Hybrid"
+ },
"Burden": {
"oneHanded": "One-Handed",
"twoHanded": "Two-Handed"
@@ -1019,6 +1044,7 @@
},
"Advantage": {
"full": "Advantage",
+ "plural": "Advantages",
"short": "Adv"
},
"Adversary": {
@@ -1202,6 +1228,11 @@
"hint": "The cost in stress you can pay to reduce minor damage to none."
}
}
+ },
+ "attack": {
+ "damage": {
+ "value": { "label": "Base Attack: Damage" }
+ }
}
},
"Tabs": {
@@ -1235,14 +1266,19 @@
"recovery": "Recovery",
"setup": "Setup",
"equipment": "Equipment",
- "attachments": "Attachments"
- },
- "Tiers": {
- "singular": "Tier",
+ "attachments": "Attachments",
+ "advanced": "Advanced",
"tier1": "Tier 1",
"tier2": "Tier 2",
"tier3": "Tier 3",
- "tier4": "Tier 4"
+ "tier4": "tier 4"
+ },
+ "Tiers": {
+ "singular": "Tier",
+ "1": "Tier 1",
+ "2": "Tier 2",
+ "3": "Tier 3",
+ "4": "Tier 4"
},
"Trait": {
"single": "Trait",
@@ -1278,7 +1314,7 @@
"features": "Features",
"formula": "Formula",
"healing": "Healing",
- "hitPoints": {
+ "HitPoints": {
"single": "Hit Point",
"plural": "Hit Points",
"short": "HP"
@@ -1318,6 +1354,8 @@
"total": "Total",
"true": "True",
"type": "Type",
+ "unarmed": "Unarmed",
+ "unarmedStrike": "Unarmed Strike",
"unarmored": "Unarmored",
"use": "Use",
"used": "Used",
@@ -1351,7 +1389,9 @@
},
"Beastform": {
"FIELDS": {
+ "beastformType": { "label": "Beastform Type" },
"tier": { "label": "Tier" },
+ "mainTrait": { "label": "Main Trait" },
"examples": { "label": "Examples" },
"advantageOn": { "label": "Gain Advantage On" },
"tokenImg": { "label": "Token Image" },
@@ -1360,12 +1400,28 @@
"placeholder": "Using character dimensions",
"height": { "label": "Height" },
"width": { "label": "Width" }
+ },
+ "evolved": {
+ "maximumTier": { "label": "Maximum Tier" },
+ "mainTraitBonus": { "label": "Main Trait Bonus" }
+ },
+ "hybrid": {
+ "beastformOptions": { "label": "Nr Beastforms" },
+ "advantages": { "label": "Nr Advantages" },
+ "features": { "label": "Nr Features" }
}
},
+ "attackName": "Beast Attack",
+ "beastformEffect": "Beastform Transformation",
"dialogTitle": "Beastform Selection",
"tokenTitle": "Beastform Token",
"transform": "Transform",
- "beastformEffect": "Beastform Transformation"
+ "evolve": "Evolve",
+ "evolvedFeatureTitle": "Evolved",
+ "evolvedDrag": "Drag a form here to evolve it.",
+ "hybridize": "Hybridize",
+ "hybridizeFeatureTitle": "Hybrid Features",
+ "hybridizeDrag": "Drag a form here to hybridize it."
},
"Class": {
"hopeFeatures": "Hope Features",
@@ -1609,7 +1665,11 @@
"featureNotSecondary": "This feature is used as something else than a Secondary feature and cannot be used here.",
"featureNotFoundation": "This feature is used as something else than a Foundation feature and cannot be used here.",
"featureNotSpecialization": "This feature is used as something else than a Specialization feature and cannot be used here.",
- "featureNotMastery": "This feature is used as something else than a Mastery feature and cannot be used here."
+ "featureNotMastery": "This feature is used as something else than a Mastery feature and cannot be used here.",
+ "beastformMissingEffect": "The Beastform is missing a Beastform Effect. Cannot be used.",
+ "beastformToManyAdvantages": "You cannot select any more advantages.",
+ "beastformToManyFeatures": "You cannot select any more features.",
+ "beastformEquipWeapon": "You cannot use weapons while in a Beastform."
},
"Tooltip": {
"disableEffect": "Disable Effect",
@@ -1624,6 +1684,7 @@
"sendToLoadout": "Send to Loadout",
"makeDeathMove": "Make a Death Move",
"rangeAndTarget": "Range & Target",
+ "dragApplyEffect": "Drag effect to apply it to an actor",
"appliedEvenIfSuccessful": "Applied even if save succeeded"
}
}
diff --git a/module/applications/dialogs/beastformDialog.mjs b/module/applications/dialogs/beastformDialog.mjs
index 367311b0..1d6725ad 100644
--- a/module/applications/dialogs/beastformDialog.mjs
+++ b/module/applications/dialogs/beastformDialog.mjs
@@ -6,6 +6,10 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat
this.configData = configData;
this.selected = null;
+ this.evolved = { form: null };
+ this.hybrid = { forms: {}, advantages: {}, features: {} };
+
+ this._dragDrop = this._createDragDropHandlers();
}
static DEFAULT_OPTIONS = {
@@ -17,13 +21,16 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat
},
actions: {
selectBeastform: this.selectBeastform,
+ toggleHybridFeature: this.toggleHybridFeature,
+ toggleHybridAdvantage: this.toggleHybridAdvantage,
submitBeastform: this.submitBeastform
},
form: {
handler: this.updateBeastform,
submitOnChange: true,
submitOnClose: false
- }
+ },
+ dragDrop: [{ dragSelector: '.beastform-container', dropSelector: '.advanced-form-container' }]
};
get title() {
@@ -32,36 +39,217 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat
/** @override */
static PARTS = {
- beastform: {
- template: 'systems/daggerheart/templates/dialogs/beastformDialog.hbs'
+ tabs: { template: 'systems/daggerheart/templates/dialogs/beastform/tabs.hbs' },
+ beastformTier: { template: 'systems/daggerheart/templates/dialogs/beastform/beastformTier.hbs' },
+ advanced: { template: 'systems/daggerheart/templates/dialogs/beastform/advanced.hbs' },
+ footer: { template: 'systems/daggerheart/templates/dialogs/beastform/footer.hbs' }
+ };
+
+ /** @inheritdoc */
+ static TABS = {
+ primary: {
+ tabs: [{ id: '1' }, { id: '2' }, { id: '3' }, { id: '4' }],
+ initial: '1',
+ labelPrefix: 'DAGGERHEART.GENERAL.Tiers'
}
};
+ changeTab(tab, group, options) {
+ super.changeTab(tab, group, options);
+
+ this.render();
+ }
+
+ _createDragDropHandlers() {
+ return this.options.dragDrop.map(d => {
+ d.callbacks = {
+ dragstart: this._onDragStart.bind(this),
+ drop: this._onDrop.bind(this)
+ };
+ return new foundry.applications.ux.DragDrop.implementation(d);
+ });
+ }
+
+ _attachPartListeners(partId, htmlElement, options) {
+ super._attachPartListeners(partId, htmlElement, options);
+
+ this._dragDrop.forEach(d => d.bind(htmlElement));
+ }
+
async _prepareContext(_options) {
const context = await super._prepareContext(_options);
- context.beastformTiers = game.items.reduce((acc, x) => {
- const tier = CONFIG.DH.GENERAL.tiers[x.system.tier];
- if (x.type !== 'beastform' || tier.value > this.configData.tierLimit) return acc;
+ context.selected = this.selected;
+ context.selectedBeastformEffect = this.selected?.effects?.find?.(x => x.type === 'beastform');
- if (!acc[tier.value]) acc[tier.value] = { label: game.i18n.localize(tier.label), values: {} };
- acc[tier.value].values[x.uuid] = { selected: this.selected == x.uuid, value: x };
+ context.evolved = this.evolved;
+ context.hybridForms = Object.keys(this.hybrid.forms).reduce((acc, formKey) => {
+ if (!this.hybrid.forms[formKey]) {
+ acc[formKey] = null;
+ } else {
+ const data = this.hybrid.forms[formKey].toObject();
+ acc[formKey] = {
+ ...data,
+ system: {
+ ...data.system,
+ features: this.hybrid.forms[formKey].system.features.map(feature => ({
+ ...feature.toObject(),
+ uuid: feature.uuid,
+ selected: Boolean(this.hybrid.features?.[formKey]?.[feature.uuid])
+ })),
+ advantageOn: Object.keys(data.system.advantageOn).reduce((acc, key) => {
+ acc[key] = {
+ ...data.system.advantageOn[key],
+ selected: Boolean(this.hybrid.advantages?.[formKey]?.[key])
+ };
+ return acc;
+ }, {})
+ }
+ };
+ }
return acc;
- }, {}); // Also get from compendium when added
- context.canSubmit = this.selected;
+ }, {});
+
+ const maximumDragTier = Math.max(
+ this.selected?.system?.evolved?.maximumTier ?? 0,
+ this.selected?.system?.hybrid?.maximumTier ?? 0
+ );
+
+ const compendiumBeastforms = await game.packs.get(`daggerheart.beastforms`)?.getDocuments();
+ const beastformTiers = [...(compendiumBeastforms ? compendiumBeastforms : []), ...game.items].reduce(
+ (acc, x) => {
+ const tier = CONFIG.DH.GENERAL.tiers[x.system.tier];
+ if (x.type !== 'beastform' || tier.id > this.configData.tierLimit) return acc;
+
+ if (!acc[tier.id]) acc[tier.id] = { label: game.i18n.localize(tier.label), values: {} };
+
+ acc[tier.id].values[x.uuid] = {
+ selected: this.selected?.uuid == x.uuid,
+ value: x,
+ draggable:
+ !['evolved', 'hybrid'].includes(x.system.beastformType) && maximumDragTier
+ ? x.system.tier <= maximumDragTier
+ : false
+ };
+
+ return acc;
+ },
+ {}
+ );
+
+ context.tier = beastformTiers[this.tabGroups.primary];
+ context.tierKey = this.tabGroups.primary;
+
+ context.canSubmit = this.canSubmit();
return context;
}
+ canSubmit() {
+ if (this.selected) {
+ switch (this.selected.system.beastformType) {
+ case 'normal':
+ return true;
+ case 'evolved':
+ return this.evolved.form;
+ case 'hybrid':
+ const selectedAdvantages = Object.values(this.hybrid.advantages).reduce(
+ (acc, form) => acc + Object.values(form).length,
+ 0
+ );
+ const selectedFeatures = Object.values(this.hybrid.features).reduce(
+ (acc, form) => acc + Object.values(form).length,
+ 0
+ );
+
+ const advantagesSelected = selectedAdvantages === this.selected.system.hybrid.advantages;
+ const featuresSelected = selectedFeatures === this.selected.system.hybrid.features;
+ return advantagesSelected && featuresSelected;
+ }
+ }
+
+ return false;
+ }
+
static updateBeastform(event, _, formData) {
this.selected = foundry.utils.mergeObject(this.selected, formData.object);
this.render();
}
- static selectBeastform(_, target) {
- this.selected = this.selected === target.dataset.uuid ? null : target.dataset.uuid;
+ static async selectBeastform(_, target) {
+ this.element.querySelectorAll('.beastform-container ').forEach(element => {
+ if (element.dataset.uuid === target.dataset.uuid && this.selected?.uuid !== target.dataset.uuid) {
+ element.classList.remove('inactive');
+ } else {
+ element.classList.add('inactive');
+ }
+ });
+
+ const uuid = this.selected?.uuid === target.dataset.uuid ? null : target.dataset.uuid;
+ this.selected = uuid ? await foundry.utils.fromUuid(uuid) : null;
+
+ if (this.selected) {
+ if (this.selected.system.beastformType !== 'evolved') this.evolved.form = null;
+ if (this.selected.system.beastformType !== 'hybrid') {
+ this.hybrid.forms = {};
+ this.hybrid.advantages = {};
+ this.hybrid.features = {};
+ } else {
+ this.hybrid.forms = [...Array(this.selected.system.hybrid.beastformOptions).keys()].reduce((acc, _) => {
+ acc[foundry.utils.randomID()] = null;
+ return acc;
+ }, {});
+ }
+ }
+
+ this.render();
+ }
+
+ static toggleHybridFeature(_, button) {
+ const current = this.hybrid.features[button.dataset.form];
+ if (!current) this.hybrid.features[button.dataset.form] = {};
+
+ if (this.hybrid.features[button.dataset.form][button.id])
+ delete this.hybrid.features[button.dataset.form][button.id];
+ else {
+ const currentFeatures = Object.values(this.hybrid.features).reduce(
+ (acc, form) => acc + Object.values(form).length,
+ 0
+ );
+ if (currentFeatures === this.selected.system.hybrid.features) {
+ ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.beastformToManyFeatures'));
+ return;
+ }
+
+ const feature = this.hybrid.forms[button.dataset.form].system.features.find(x => x.uuid === button.id);
+ this.hybrid.features[button.dataset.form][button.id] = feature;
+ }
+
+ this.render();
+ }
+
+ static toggleHybridAdvantage(_, button) {
+ const current = this.hybrid.advantages[button.dataset.form];
+ if (!current) this.hybrid.advantages[button.dataset.form] = {};
+
+ if (this.hybrid.advantages[button.dataset.form][button.id])
+ delete this.hybrid.advantages[button.dataset.form][button.id];
+ else {
+ const currentAdvantages = Object.values(this.hybrid.advantages).reduce(
+ (acc, form) => acc + Object.values(form).length,
+ 0
+ );
+ if (currentAdvantages === this.selected.system.hybrid.advantages) {
+ ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.beastformToManyAdvantages'));
+ return;
+ }
+
+ const advantage = this.hybrid.forms[button.dataset.form].system.advantageOn[button.id];
+ this.hybrid.advantages[button.dataset.form][button.id] = advantage;
+ }
+
this.render();
}
@@ -71,14 +259,60 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat
/** @override */
_onClose(options = {}) {
- if (!options.submitted) this.config = false;
+ if (!options.submitted) this.selected = null;
}
static async configure(configData) {
return new Promise(resolve => {
const app = new this(configData);
- app.addEventListener('close', () => resolve(app.selected), { once: true });
+ app.addEventListener(
+ 'close',
+ () => resolve({ selected: app.selected, evolved: app.evolved, hybrid: app.hybrid }),
+ { once: true }
+ );
app.render({ force: true });
});
}
+
+ async _onDragStart(event) {
+ const target = event.currentTarget;
+ const abort = () => event.preventDefault();
+ if (!this.selected) abort();
+
+ const draggedForm = await foundry.utils.fromUuid(target.dataset.uuid);
+ if (['evolved', 'hybrid'].includes(draggedForm.system.beastformType)) abort();
+
+ if (this.selected.system.beastformType === 'evolved') {
+ if (draggedForm.system.tier > this.selected.system.evolved.maximumTier) abort();
+ }
+ if (this.selected.system.beastformType === 'hybrid') {
+ if (draggedForm.system.tier > this.selected.system.hybrid.maximumTier) abort();
+ }
+
+ event.dataTransfer.setData('text/plain', JSON.stringify(target.dataset));
+ event.dataTransfer.setDragImage(target, 60, 0);
+ }
+
+ async _onDrop(event) {
+ event.stopPropagation();
+ const data = foundry.applications.ux.TextEditor.getDragEventData(event);
+ const item = await fromUuid(data.uuid);
+ if (!item) return;
+
+ if (event.target.closest('.advanced-form-container.evolved')) {
+ this.evolved.form = item;
+ } else {
+ const hybridContainer = event.target.closest('.advanced-form-container.hybridized');
+ if (hybridContainer) {
+ const existingId = Object.keys(this.hybrid.forms).find(
+ key => this.hybrid.forms[key]?.uuid === item.uuid
+ );
+ if (existingId) this.hybrid.forms[existingId] = null;
+
+ this.hybrid.forms[hybridContainer.id] = item;
+ }
+ }
+
+ this.render();
+ }
}
diff --git a/module/applications/dialogs/damageDialog.mjs b/module/applications/dialogs/damageDialog.mjs
index 1b61b96d..78452054 100644
--- a/module/applications/dialogs/damageDialog.mjs
+++ b/module/applications/dialogs/damageDialog.mjs
@@ -61,7 +61,7 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
static updateRollConfiguration(_event, _, formData) {
const { ...rest } = foundry.utils.expandObject(formData.object);
- foundry.utils.mergeObject(this.config.roll, rest.roll)
+ foundry.utils.mergeObject(this.config.roll, rest.roll);
this.config.selectedRollMode = rest.selectedRollMode;
this.render();
diff --git a/module/applications/dialogs/damageReductionDialog.mjs b/module/applications/dialogs/damageReductionDialog.mjs
index 658cef96..3e3bde44 100644
--- a/module/applications/dialogs/damageReductionDialog.mjs
+++ b/module/applications/dialogs/damageReductionDialog.mjs
@@ -10,7 +10,7 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
this.reject = reject;
this.actor = actor;
this.damage = damage;
-
+
const canApplyArmor = damageType.every(t => actor.system.armorApplicableDamageTypes[t] === true);
const maxArmorMarks = canApplyArmor
? Math.min(
diff --git a/module/applications/sheets-configs/action-config.mjs b/module/applications/sheets-configs/action-config.mjs
index db3c4d27..3f915e41 100644
--- a/module/applications/sheets-configs/action-config.mjs
+++ b/module/applications/sheets-configs/action-config.mjs
@@ -114,7 +114,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
const settingsTiers = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers;
context.tierOptions = [
- { key: 1, label: game.i18n.localize('DAGGERHEART.GENERAL.Tiers.tier1') },
+ { key: 1, label: game.i18n.localize('DAGGERHEART.GENERAL.Tiers.1') },
...Object.values(settingsTiers).map(x => ({ key: x.tier, label: x.name }))
];
diff --git a/module/applications/sheets-configs/environment-settings.mjs b/module/applications/sheets-configs/environment-settings.mjs
index c249d6d5..7a91b272 100644
--- a/module/applications/sheets-configs/environment-settings.mjs
+++ b/module/applications/sheets-configs/environment-settings.mjs
@@ -70,9 +70,9 @@ export default class DHEnvironmentSettings extends DHBaseActorSettings {
}
/**
- *
+ *
* @type {ApplicationClickAction}
- * @returns
+ * @returns
*/
static async #deleteAdversary(_event, target) {
const doc = getDocFromElement(target);
diff --git a/module/applications/sheets/actors/adversary.mjs b/module/applications/sheets/actors/adversary.mjs
index e6508695..f6324881 100644
--- a/module/applications/sheets/actors/adversary.mjs
+++ b/module/applications/sheets/actors/adversary.mjs
@@ -8,7 +8,7 @@ export default class AdversarySheet extends DHBaseActorSheet {
position: { width: 660, height: 766 },
window: { resizable: true },
actions: {
- reactionRoll: AdversarySheet.#reactionRoll,
+ reactionRoll: AdversarySheet.#reactionRoll
},
window: {
resizable: true
diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs
index 8f157c96..4fe4b5e3 100644
--- a/module/applications/sheets/actors/character.mjs
+++ b/module/applications/sheets/actors/character.mjs
@@ -619,6 +619,12 @@ export default class CharacterSheet extends DHBaseActorSheet {
await item.update({ 'system.equipped': true });
break;
case 'weapon':
+ if (this.document.effects.find(x => x.type === 'beastform')) {
+ return ui.notifications.warn(
+ game.i18n.localize('DAGGERHEART.UI.Notifications.beastformEquipWeapon')
+ );
+ }
+
await this.document.system.constructor.unequipBeforeEquip.bind(this.document.system)(item);
await item.update({ 'system.equipped': true });
diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs
index b343eb62..65a43123 100644
--- a/module/applications/sheets/api/application-mixin.mjs
+++ b/module/applications/sheets/api/application-mixin.mjs
@@ -84,24 +84,26 @@ export default function DHApplicationMixin(Base) {
useItem: DHSheetV2.#useItem,
useAction: DHSheetV2.#useAction,
toggleEffect: DHSheetV2.#toggleEffect,
- toggleExtended: DHSheetV2.#toggleExtended,
+ toggleExtended: DHSheetV2.#toggleExtended
},
- contextMenus: [{
- handler: DHSheetV2.#getEffectContextOptions,
- selector: '[data-item-uuid][data-type="effect"]',
- options: {
- parentClassHooks: false,
- fixed: true
+ contextMenus: [
+ {
+ handler: DHSheetV2.#getEffectContextOptions,
+ selector: '[data-item-uuid][data-type="effect"]',
+ options: {
+ parentClassHooks: false,
+ fixed: true
+ }
},
- },
- {
- handler: DHSheetV2.#getActionContextOptions,
- selector: '[data-item-uuid][data-type="action"]',
- options: {
- parentClassHooks: false,
- fixed: true
+ {
+ handler: DHSheetV2.#getActionContextOptions,
+ selector: '[data-item-uuid][data-type="action"]',
+ options: {
+ parentClassHooks: false,
+ fixed: true
+ }
}
- }],
+ ],
dragDrop: [],
tagifyConfigs: []
};
@@ -132,13 +134,13 @@ export default function DHApplicationMixin(Base) {
/**@inheritdoc */
_syncPartState(partId, newElement, priorElement, state) {
super._syncPartState(partId, newElement, priorElement, state);
- for (const el of priorElement.querySelectorAll(".extensible.extended")) {
+ for (const el of priorElement.querySelectorAll('.extensible.extended')) {
const { actionId, itemUuid } = el.parentElement.dataset;
const selector = `${actionId ? `[data-action-id="${actionId}"]` : `[data-item-uuid="${itemUuid}"]`} .extensible`;
const newExtensible = newElement.querySelector(selector);
if (!newExtensible) continue;
- newExtensible.classList.add("extended");
+ newExtensible.classList.add('extended');
const descriptionElement = newExtensible.querySelector('.invetory-description');
if (descriptionElement) {
this.#prepareInventoryDescription(newExtensible, descriptionElement);
@@ -209,14 +211,14 @@ export default function DHApplicationMixin(Base) {
* @param {DragEvent} event
* @protected
*/
- _onDragStart(event) { }
+ _onDragStart(event) {}
/**
* Handle drop event.
* @param {DragEvent} event
* @protected
*/
- _onDrop(event) { }
+ _onDrop(event) {}
/* -------------------------------------------- */
/* Context Menu */
@@ -251,7 +253,7 @@ export default function DHApplicationMixin(Base) {
icon: 'fa-regular fa-lightbulb',
condition: target => getDocFromElement(target).disabled,
callback: target => getDocFromElement(target).update({ disabled: false })
- },
+ }
].map(option => ({
...option,
name: `DAGGERHEART.APPLICATIONS.ContextMenu.${option.name}`,
@@ -269,7 +271,7 @@ export default function DHApplicationMixin(Base) {
*/
static #getActionContextOptions() {
/**@type {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} */
- const getAction = (target) => {
+ const getAction = target => {
const { actionId } = target.closest('[data-action-id]').dataset;
const { actions, attack } = this.document.system;
return attack?.id === actionId ? attack : actions?.find(a => a.id === actionId);
@@ -279,30 +281,31 @@ export default function DHApplicationMixin(Base) {
{
name: 'DAGGERHEART.APPLICATIONS.ContextMenu.useItem',
icon: 'fa-solid fa-burst',
- condition: this.document instanceof foundry.documents.Actor ||
+ condition:
+ this.document instanceof foundry.documents.Actor ||
(this.document instanceof foundry.documents.Item && this.document.parent),
- callback: (target, event) => getAction(target).use(event),
+ callback: (target, event) => getAction(target).use(event)
},
{
name: 'DAGGERHEART.APPLICATIONS.ContextMenu.sendToChat',
icon: 'fa-solid fa-message',
- callback: (target) => getAction(target).toChat(this.document.id),
+ callback: target => getAction(target).toChat(this.document.id)
},
{
name: 'CONTROLS.CommonEdit',
icon: 'fa-solid fa-pen-to-square',
- callback: (target) => new DHActionConfig(getAction(target)).render({ force: true })
+ callback: target => new DHActionConfig(getAction(target)).render({ force: true })
},
{
name: 'CONTROLS.CommonDelete',
icon: 'fa-solid fa-trash',
- condition: (target) => {
+ condition: target => {
const { actionId } = target.closest('[data-action-id]').dataset;
const { attack } = this.document.system;
- return attack?.id !== actionId
+ return attack?.id !== actionId;
},
- callback: async (target) => {
- const action = getAction(target)
+ callback: async target => {
+ const action = getAction(target);
const confirmed = await foundry.applications.api.DialogV2.confirm({
window: {
title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', {
@@ -310,12 +313,14 @@ export default function DHApplicationMixin(Base) {
name: action.name
})
},
- content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { name: action.name })
+ content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', {
+ name: action.name
+ })
});
if (!confirmed) return;
return this.document.update({
- 'system.actions': this.document.system.actions.filter((a) => a.id !== action.id)
+ 'system.actions': this.document.system.actions.filter(a => a.id !== action.id)
});
}
}
@@ -337,35 +342,38 @@ export default function DHApplicationMixin(Base) {
name: 'CONTROLS.CommonEdit',
icon: 'fa-solid fa-pen-to-square',
callback: target => getDocFromElement(target).sheet.render({ force: true })
- },
+ }
];
- if (usable) options.unshift({
- name: 'DAGGERHEART.APPLICATIONS.ContextMenu.useItem',
- icon: 'fa-solid fa-burst',
- callback: (target, event) => getDocFromElement(target).use(event),
- });
+ if (usable)
+ options.unshift({
+ name: 'DAGGERHEART.APPLICATIONS.ContextMenu.useItem',
+ icon: 'fa-solid fa-burst',
+ callback: (target, event) => getDocFromElement(target).use(event)
+ });
- if (toChat) options.unshift({
- name: 'DAGGERHEART.APPLICATIONS.ContextMenu.sendToChat',
- icon: 'fa-solid fa-message',
- callback: (target) => getDocFromElement(target).toChat(this.document.id),
- });
+ if (toChat)
+ options.unshift({
+ name: 'DAGGERHEART.APPLICATIONS.ContextMenu.sendToChat',
+ icon: 'fa-solid fa-message',
+ callback: target => getDocFromElement(target).toChat(this.document.id)
+ });
- if (deletable) options.push({
- name: 'CONTROLS.CommonDelete',
- icon: 'fa-solid fa-trash',
- callback: (target, event) => {
- const doc = getDocFromElement(target);
- if (event.shiftKey) return doc.delete();
- else return doc.deleteDialog();
- }
- })
+ if (deletable)
+ options.push({
+ name: 'CONTROLS.CommonDelete',
+ icon: 'fa-solid fa-trash',
+ callback: (target, event) => {
+ const doc = getDocFromElement(target);
+ if (event.shiftKey) return doc.delete();
+ else return doc.deleteDialog();
+ }
+ });
return options.map(option => ({
...option,
icon: ``
- }))
+ }));
}
/* -------------------------------------------- */
@@ -400,17 +408,20 @@ export default function DHApplicationMixin(Base) {
const doc = itemUuid
? getDocFromElement(extensibleElement)
: this.document.system.attack?.id === actionId
- ? this.document.system.attack
- : this.document.system.actions?.find(a => a.id === actionId);
+ ? this.document.system.attack
+ : this.document.system.actions?.find(a => a.id === actionId);
if (!doc) return;
const description = doc.system?.description ?? doc.description;
const isAction = !!actionId;
- descriptionElement.innerHTML = await foundry.applications.ux.TextEditor.implementation.enrichHTML(description, {
- relativeTo: isAction ? doc.parent : doc,
- rollData: doc.getRollData?.(),
- secrets: isAction ? doc.parent.isOwner : doc.isOwner
- });
+ descriptionElement.innerHTML = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
+ description,
+ {
+ relativeTo: isAction ? doc.parent : doc,
+ rollData: doc.getRollData?.(),
+ secrets: isAction ? doc.parent.isOwner : doc.isOwner
+ }
+ );
}
/* -------------------------------------------- */
@@ -427,26 +438,28 @@ export default function DHApplicationMixin(Base) {
const parent = parentIsItem && documentClass === 'Item' ? null : this.document;
if (type === 'action') {
- const { type: actionType } = await foundry.applications.api.DialogV2.input({
- window: { title: 'Select Action Type' },
- content: await foundry.applications.handlebars.renderTemplate(
- 'systems/daggerheart/templates/actionTypes/actionType.hbs',
- { types: CONFIG.DH.ACTIONS.actionTypes }
- ),
- ok: {
- label: game.i18n.format('DOCUMENT.Create', {
- type: game.i18n.localize('DAGGERHEART.GENERAL.Action.single')
- }),
- }
- }) ?? {};
+ const { type: actionType } =
+ (await foundry.applications.api.DialogV2.input({
+ window: { title: 'Select Action Type' },
+ content: await foundry.applications.handlebars.renderTemplate(
+ 'systems/daggerheart/templates/actionTypes/actionType.hbs',
+ { types: CONFIG.DH.ACTIONS.actionTypes }
+ ),
+ ok: {
+ label: game.i18n.format('DOCUMENT.Create', {
+ type: game.i18n.localize('DAGGERHEART.GENERAL.Action.single')
+ })
+ }
+ })) ?? {};
if (!actionType) return;
- const cls = game.system.api.models.actions.actionsTypes[actionType]
- const action = new cls({
- _id: foundry.utils.randomID(),
- type: actionType,
- name: game.i18n.localize(CONFIG.DH.ACTIONS.actionTypes[actionType].name),
- ...cls.getSourceConfig(this.document)
- },
+ const cls = game.system.api.models.actions.actionsTypes[actionType];
+ const action = new cls(
+ {
+ _id: foundry.utils.randomID(),
+ type: actionType,
+ name: game.i18n.localize(CONFIG.DH.ACTIONS.actionTypes[actionType].name),
+ ...cls.getSourceConfig(this.document)
+ },
{
parent: this.document
}
@@ -456,14 +469,13 @@ export default function DHApplicationMixin(Base) {
force: true
});
return action;
-
} else {
const cls = getDocumentClass(documentClass);
const data = {
name: cls.defaultName({ type, parent }),
- type,
- }
- if (inVault) data["system.inVault"] = true;
+ type
+ };
+ if (inVault) data['system.inVault'] = true;
if (disabled) data.disabled = true;
const doc = await cls.create(data, { parent, renderSheet: !event.shiftKey });
@@ -474,7 +486,6 @@ export default function DHApplicationMixin(Base) {
}
return doc;
}
-
}
/**
@@ -489,7 +500,7 @@ export default function DHApplicationMixin(Base) {
const { actionId } = target.closest('[data-action-id]').dataset;
const { actions, attack } = this.document.system;
const action = attack?.id === actionId ? attack : actions?.find(a => a.id === actionId);
- new DHActionConfig(action).render({ force: true })
+ new DHActionConfig(action).render({ force: true });
}
/**
@@ -500,8 +511,8 @@ export default function DHApplicationMixin(Base) {
const doc = getDocFromElement(target);
if (doc) {
- if (event.shiftKey) return doc.delete()
- else return await doc.deleteDialog()
+ if (event.shiftKey) return doc.delete();
+ else return await doc.deleteDialog();
}
// TODO: REDO this
@@ -524,7 +535,7 @@ export default function DHApplicationMixin(Base) {
}
return await this.document.update({
- 'system.actions': actions.filter((a) => a.id !== action.id)
+ 'system.actions': actions.filter(a => a.id !== action.id)
});
}
@@ -555,7 +566,7 @@ export default function DHApplicationMixin(Base) {
const { actionId } = target.closest('[data-action-id]').dataset;
const { actions, attack } = this.document.system;
doc = attack?.id === actionId ? attack : actions?.find(a => a.id === actionId);
- if(this.document instanceof foundry.documents.Item && !this.document.parent) return;
+ if (this.document instanceof foundry.documents.Item && !this.document.parent) return;
}
await doc.use(event);
@@ -573,7 +584,6 @@ export default function DHApplicationMixin(Base) {
await action.use(event);
}
-
/**
* Toggle a ActiveEffect
* @type {ApplicationClickAction}
@@ -604,7 +614,6 @@ export default function DHApplicationMixin(Base) {
const descriptionElement = extensible?.querySelector('.invetory-description');
if (t && !!descriptionElement) this.#prepareInventoryDescription(extensible, descriptionElement);
}
-
}
return DHSheetV2;
diff --git a/module/applications/sheets/api/base-actor.mjs b/module/applications/sheets/api/base-actor.mjs
index 56f00e9b..346a0ab6 100644
--- a/module/applications/sheets/api/base-actor.mjs
+++ b/module/applications/sheets/api/base-actor.mjs
@@ -22,7 +22,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
},
actions: {
openSettings: DHBaseActorSheet.#openSettings,
- sendExpToChat: DHBaseActorSheet.#sendExpToChat,
+ sendExpToChat: DHBaseActorSheet.#sendExpToChat
},
contextMenus: [
{
@@ -59,7 +59,6 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
return context;
}
-
/**@inheritdoc */
async _preparePartContext(partId, context, options) {
context = await super._preparePartContext(partId, context, options);
@@ -81,7 +80,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
async _prepareEffectsContext(context, _options) {
context.effects = {
actives: [],
- inactives: [],
+ inactives: []
};
for (const effect of this.actor.allApplicableEffects()) {
@@ -104,7 +103,6 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
return this._getContextMenuCommonOptions.call(this, { usable: true, toChat: true });
}
-
/* -------------------------------------------- */
/* Application Clicks Actions */
/* -------------------------------------------- */
diff --git a/module/applications/sheets/api/base-item.mjs b/module/applications/sheets/api/base-item.mjs
index 766b443c..1888be9e 100644
--- a/module/applications/sheets/api/base-item.mjs
+++ b/module/applications/sheets/api/base-item.mjs
@@ -72,10 +72,10 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
secrets: this.item.isOwner
});
break;
- case "effects":
- await this._prepareEffectsContext(context, options)
+ case 'effects':
+ await this._prepareEffectsContext(context, options);
break;
- case "features":
+ case 'features':
context.isGM = game.user.isGM;
break;
}
@@ -93,7 +93,7 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
async _prepareEffectsContext(context, _options) {
context.effects = {
actives: [],
- inactives: [],
+ inactives: []
};
for (const effect of this.item.effects) {
@@ -113,30 +113,30 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
* @protected
*/
static #getFeatureContextOptions() {
- const options = this._getContextMenuCommonOptions({ usable: true, toChat: true, deletable: false })
- options.push(
- {
- name: 'CONTROLS.CommonDelete',
- icon: '',
- callback: async (target) => {
- const feature = getDocFromElement(target);
- if (!feature) return;
- const confirmed = await foundry.applications.api.DialogV2.confirm({
- window: {
- title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', {
- type: game.i18n.localize(`TYPES.Item.feature`),
- name: feature.name
- })
- },
- content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { name: feature.name })
- });
- if (!confirmed) return;
- await this.document.update({
- 'system.features': this.document.system.toObject().features.filter(uuid => uuid !== feature.uuid)
- });
- },
+ const options = this._getContextMenuCommonOptions({ usable: true, toChat: true, deletable: false });
+ options.push({
+ name: 'CONTROLS.CommonDelete',
+ icon: '',
+ callback: async target => {
+ const feature = getDocFromElement(target);
+ if (!feature) return;
+ const confirmed = await foundry.applications.api.DialogV2.confirm({
+ window: {
+ title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', {
+ type: game.i18n.localize(`TYPES.Item.feature`),
+ name: feature.name
+ })
+ },
+ content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', {
+ name: feature.name
+ })
+ });
+ if (!confirmed) return;
+ await this.document.update({
+ 'system.features': this.document.system.toObject().features.filter(uuid => uuid !== feature.uuid)
+ });
}
- )
+ });
return options;
}
@@ -153,7 +153,7 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
const actionIndex = button.closest('[data-index]').dataset.index;
const action = this.document.system.actions[actionIndex];
- if(!event.shiftKey) {
+ if (!event.shiftKey) {
const confirmed = await foundry.applications.api.DialogV2.confirm({
window: {
title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', {
@@ -166,7 +166,6 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
if (!confirmed) return;
}
-
await this.document.update({
'system.actions': this.document.system.actions.filter((_, index) => index !== Number.parseInt(actionIndex))
});
@@ -180,9 +179,9 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
const { type } = target.dataset;
const cls = foundry.documents.Item.implementation;
const feature = await cls.create({
- type: 'feature',
- name: cls.defaultName({ type: 'feature' }),
- "system.subType": CONFIG.DH.ITEM.featureSubTypes[type]
+ 'type': 'feature',
+ 'name': cls.defaultName({ type: 'feature' }),
+ 'system.subType': CONFIG.DH.ITEM.featureSubTypes[type]
});
await this.document.update({
'system.features': [...this.document.system.features, feature].map(f => f.uuid)
@@ -198,9 +197,7 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
if (!feature) return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureIsMissing'));
await feature.update({ 'system.subType': null });
await this.document.update({
- 'system.features': this.document.system.features
- .map(x => x.uuid)
- .filter(uuid => uuid !== feature.uuid)
+ 'system.features': this.document.system.features.map(x => x.uuid).filter(uuid => uuid !== feature.uuid)
});
}
diff --git a/module/applications/sheets/items/armor.mjs b/module/applications/sheets/items/armor.mjs
index 80bbe139..bdc482c3 100644
--- a/module/applications/sheets/items/armor.mjs
+++ b/module/applications/sheets/items/armor.mjs
@@ -31,7 +31,7 @@ export default class ArmorSheet extends ItemAttachmentSheet(DHBaseItemSheet) {
template: 'systems/daggerheart/templates/sheets/global/tabs/tab-effects.hbs',
scrollable: ['.effects']
},
- ...super.PARTS,
+ ...super.PARTS
};
/**@inheritdoc */
diff --git a/module/applications/sheets/items/beastform.mjs b/module/applications/sheets/items/beastform.mjs
index db769e94..8894b694 100644
--- a/module/applications/sheets/items/beastform.mjs
+++ b/module/applications/sheets/items/beastform.mjs
@@ -1,4 +1,5 @@
import DHBaseItemSheet from '../api/base-item.mjs';
+import Tagify from '@yaireo/tagify';
export default class BeastformSheet extends DHBaseItemSheet {
/**@inheritdoc */
@@ -15,6 +16,7 @@ export default class BeastformSheet extends DHBaseItemSheet {
template: 'systems/daggerheart/templates/sheets/global/tabs/tab-features.hbs',
scrollable: ['.features']
},
+ advanced: { template: 'systems/daggerheart/templates/sheets/items/beastform/advanced.hbs' },
effects: {
template: 'systems/daggerheart/templates/sheets/global/tabs/tab-effects.hbs',
scrollable: ['.effects']
@@ -23,9 +25,77 @@ export default class BeastformSheet extends DHBaseItemSheet {
static TABS = {
primary: {
- tabs: [{ id: 'settings' }, { id: 'features' }, { id: 'effects' }],
+ tabs: [{ id: 'settings' }, { id: 'features' }, { id: 'advanced' }, { id: 'effects' }],
initial: 'settings',
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
}
};
+
+ _attachPartListeners(partId, htmlElement, options) {
+ super._attachPartListeners(partId, htmlElement, options);
+
+ const advantageOnInput = htmlElement.querySelector('.advantageon-input');
+ if (advantageOnInput) {
+ const tagifyElement = new Tagify(advantageOnInput, {
+ tagTextProp: 'name',
+ templates: {
+ tag(tagData) {
+ return `` : ''}
+
+ ${effect.name}
+
+ `;
+
+ return dualityElement;
+}
diff --git a/module/enrichers/_module.mjs b/module/enrichers/_module.mjs
index 1907f117..abfd8158 100644
--- a/module/enrichers/_module.mjs
+++ b/module/enrichers/_module.mjs
@@ -1,2 +1,43 @@
-export { default as DhDualityRollEnricher } from './DualityRollEnricher.mjs';
-export { default as DhTemplateEnricher } from './TemplateEnricher.mjs';
+import { default as DhDamageEnricher, renderDamageButton } from './DamageEnricher.mjs';
+import { default as DhDualityRollEnricher, renderDualityButton } from './DualityRollEnricher.mjs';
+import { default as DhEffectEnricher } from './EffectEnricher.mjs';
+import { default as DhTemplateEnricher, renderMeasuredTemplate } from './TemplateEnricher.mjs';
+
+export { DhDamageEnricher, DhDualityRollEnricher, DhEffectEnricher, DhTemplateEnricher };
+
+export const enricherConfig = [
+ {
+ pattern: /^@Damage\[(.*)\]$/g,
+ enricher: DhDamageEnricher
+ },
+ {
+ pattern: /\[\[\/dr\s?(.*?)\]\]/g,
+ enricher: DhDualityRollEnricher
+ },
+ {
+ pattern: /^@Effect\[(.*)\]$/g,
+ enricher: DhEffectEnricher
+ },
+ {
+ pattern: /^@Template\[(.*)\]$/g,
+ enricher: DhTemplateEnricher
+ }
+];
+
+export const enricherRenderSetup = element => {
+ element
+ .querySelectorAll('.enriched-damage-button')
+ .forEach(element => element.addEventListener('click', renderDamageButton));
+
+ element
+ .querySelectorAll('.duality-roll-button')
+ .forEach(element => element.addEventListener('click', renderDualityButton));
+
+ element
+ .querySelectorAll('.measured-template-button')
+ .forEach(element => element.addEventListener('click', renderMeasuredTemplate));
+
+ // element
+ // .querySelectorAll('.enriched-effect')
+ // .forEach(element => element.addEventListener('dragstart', dragEnrichedEffect));
+};
diff --git a/module/helpers/handlebarsHelper.mjs b/module/helpers/handlebarsHelper.mjs
index 0a57a6a6..0c919191 100644
--- a/module/helpers/handlebarsHelper.mjs
+++ b/module/helpers/handlebarsHelper.mjs
@@ -9,7 +9,7 @@ export default class RegisterHandlebarsHelpers {
damageFormula: this.damageFormula,
damageSymbols: this.damageSymbols,
rollParsed: this.rollParsed,
- hasProperty: foundry.utils.hasProperty,
+ hasProperty: foundry.utils.hasProperty
});
}
static add(a, b) {
diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs
index 2aacd5c7..7e73695e 100644
--- a/module/helpers/utils.mjs
+++ b/module/helpers/utils.mjs
@@ -1,80 +1,10 @@
import { diceTypes, getDiceSoNicePresets, range } from '../config/generalConfig.mjs';
import Tagify from '@yaireo/tagify';
-export const loadCompendiumOptions = async compendiums => {
- const compendiumValues = [];
-
- for (var compendium of compendiums) {
- const values = await getCompendiumOptions(compendium);
- compendiumValues.push(values);
- }
-
- return compendiumValues;
-};
-
-const getCompendiumOptions = async compendium => {
- const compendiumPack = await game.packs.get(compendium);
-
- const values = [];
- for (var value of compendiumPack.index) {
- const document = await compendiumPack.getDocument(value._id);
- values.push(document);
- }
-
- return values;
-};
-
-export const getWidthOfText = (txt, fontsize, allCaps, bold) => {
- const text = allCaps ? txt.toUpperCase() : txt;
- if (getWidthOfText.c === undefined) {
- getWidthOfText.c = document.createElement('canvas');
- getWidthOfText.ctx = getWidthOfText.c.getContext('2d');
- }
- var fontspec = `${bold ? 'bold' : ''} ${fontsize}px` + ' ' + 'Signika, sans-serif';
- if (getWidthOfText.ctx.font !== fontspec) getWidthOfText.ctx.font = fontspec;
-
- return getWidthOfText.ctx.measureText(text).width;
-};
-
-export const padArray = (arr, len, fill) => {
- return arr.concat(Array(len).fill(fill)).slice(0, len);
-};
-
-export const getTier = (level, asNr) => {
- switch (Math.floor((level + 1) / 3)) {
- case 1:
- return asNr ? 1 : 'tier1';
- case 2:
- return asNr ? 2 : 'tier2';
- case 3:
- return asNr ? 3 : 'tier3';
- default:
- return asNr ? 0 : 'tier0';
- }
-};
-
export const capitalize = string => {
return string.charAt(0).toUpperCase() + string.slice(1);
};
-export const getPathValue = (path, entity, numeric) => {
- const pathValue = foundry.utils.getProperty(entity, path);
- if (pathValue) return numeric ? Number.parseInt(pathValue) : pathValue;
-
- return numeric ? Number.parseInt(path) : path;
-};
-
-export const generateId = (title, length) => {
- const id = title
- .split(' ')
- .map((w, i) => {
- const p = w.slugify({ replacement: '', strict: true });
- return i ? p.titleCase() : p;
- })
- .join('');
- return Number.isNumeric(length) ? id.slice(0, length).padEnd(length, '0') : id;
-};
-
export function rollCommandToJSON(text) {
if (!text) return {};
@@ -311,7 +241,6 @@ export function getDocFromElement(element) {
return foundry.utils.fromUuidSync(target.dataset.itemUuid) ?? null;
}
-
export const itemAbleRollParse = (value, actor, item) => {
if (!value) return value;
@@ -325,13 +254,7 @@ export const itemAbleRollParse = (value, actor, item) => {
};
export const arraysEqual = (a, b) =>
- a.length === b.length &&
- [...new Set([...a, ...b])].every(
- v => a.filter(e => e === v).length === b.filter(e => e === v).length
- );
+ a.length === b.length &&
+ [...new Set([...a, ...b])].every(v => a.filter(e => e === v).length === b.filter(e => e === v).length);
-export const setsEqual = (a, b) =>
- a.size === b.size &&
- [...a].every(
- value => b.has(value)
- );
\ No newline at end of file
+export const setsEqual = (a, b) => a.size === b.size && [...a].every(value => b.has(value));
diff --git a/module/systemRegistration/handlebars.mjs b/module/systemRegistration/handlebars.mjs
index ec5f2df1..b15bf820 100644
--- a/module/systemRegistration/handlebars.mjs
+++ b/module/systemRegistration/handlebars.mjs
@@ -1,8 +1,9 @@
export const preloadHandlebarsTemplates = async function () {
foundry.applications.handlebars.loadTemplates({
- 'daggerheart.inventory-items': 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs',
- 'daggerheart.inventory-item': 'systems/daggerheart/templates/sheets/global/partials/inventory-item-V2.hbs',
- })
+ 'daggerheart.inventory-items':
+ 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs',
+ 'daggerheart.inventory-item': 'systems/daggerheart/templates/sheets/global/partials/inventory-item-V2.hbs'
+ });
return foundry.applications.handlebars.loadTemplates([
'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs',
'systems/daggerheart/templates/sheets/global/partials/action-item.hbs',
@@ -27,6 +28,7 @@ export const preloadHandlebarsTemplates = async function () {
'systems/daggerheart/templates/settings/components/settings-item-line.hbs',
'systems/daggerheart/templates/ui/chat/parts/damage-chat.hbs',
'systems/daggerheart/templates/ui/chat/parts/target-chat.hbs',
+ 'systems/daggerheart/templates/ui/tooltip/parts/tooltipChips.hbs',
'systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs',
'systems/daggerheart/templates/dialogs/downtime/activities.hbs'
]);
diff --git a/src/packs/beastforms/beastform_Agile_Scout_6tr99y6wHaJJYy3J.json b/src/packs/beastforms/beastform_Agile_Scout_6tr99y6wHaJJYy3J.json
new file mode 100644
index 00000000..0885d9f3
--- /dev/null
+++ b/src/packs/beastforms/beastform_Agile_Scout_6tr99y6wHaJJYy3J.json
@@ -0,0 +1,160 @@
+{
+ "name": "Agile Scout",
+ "type": "beastform",
+ "img": "icons/creatures/mammals/goat-horned-blue.webp",
+ "system": {
+ "beastformType": "normal",
+ "tier": 1,
+ "tokenImg": "icons/creatures/mammals/goat-horned-blue.webp",
+ "tokenRingImg": "icons/svg/mystery-man.svg",
+ "tokenSize": {
+ "height": null,
+ "width": null
+ },
+ "mainTrait": "agility",
+ "advantageOn": {
+ "tXlf8FvWrgGqfaJu": {
+ "value": "deceit"
+ },
+ "lp1gv9iNUCpC2Fli": {
+ "value": "locate"
+ },
+ "GxDMKUpOeDHzyhAT": {
+ "value": "sneak"
+ }
+ },
+ "features": [
+ "Compendium.daggerheart.beastforms.Item.sef9mwD2eRLZ64oV",
+ "Compendium.daggerheart.beastforms.Item.9ryNrYWjNtOT6DXN"
+ ],
+ "evolved": {
+ "mainTraitBonus": 0
+ },
+ "hybrid": {
+ "beastformOptions": 2,
+ "advantages": 2,
+ "features": 2
+ },
+ "examples": "Fox, Mouse, Weasel, etc."
+ },
+ "effects": [
+ {
+ "type": "beastform",
+ "name": "Beastform Transformation",
+ "img": "icons/creatures/abilities/paw-print-pair-purple.webp",
+ "_id": "m098fyKkAjTFZ6UJ",
+ "system": {
+ "characterTokenData": {
+ "tokenImg": null,
+ "tokenRingImg": "icons/svg/mystery-man.svg",
+ "tokenSize": {}
+ },
+ "advantageOn": [],
+ "featureIds": [],
+ "effectIds": []
+ },
+ "changes": [
+ {
+ "key": "system.traits.agility.value",
+ "mode": 2,
+ "value": "1",
+ "priority": null
+ },
+ {
+ "key": "system.evasion",
+ "mode": 2,
+ "value": "2",
+ "priority": null
+ }
+ ],
+ "disabled": false,
+ "duration": {
+ "startTime": null,
+ "combat": null,
+ "seconds": null,
+ "rounds": null,
+ "turns": null,
+ "startRound": null,
+ "startTurn": null
+ },
+ "description": "",
+ "origin": null,
+ "tint": "#ffffff",
+ "transfer": true,
+ "statuses": [],
+ "sort": 0,
+ "flags": {},
+ "_stats": {
+ "compendiumSource": null,
+ "duplicateSource": null,
+ "exportSource": null,
+ "coreVersion": "13.346",
+ "systemId": "daggerheart",
+ "systemVersion": "0.0.1",
+ "lastModifiedBy": null
+ },
+ "_key": "!items.effects!6tr99y6wHaJJYy3J.m098fyKkAjTFZ6UJ"
+ },
+ {
+ "type": "beastform",
+ "name": "Beastform Transformation",
+ "img": "icons/creatures/abilities/paw-print-pair-purple.webp",
+ "_id": "5mi9ku2R4paP579i",
+ "system": {
+ "characterTokenData": {
+ "tokenImg": null,
+ "tokenRingImg": "icons/svg/mystery-man.svg",
+ "tokenSize": {}
+ },
+ "advantageOn": [],
+ "featureIds": [],
+ "effectIds": []
+ },
+ "changes": [],
+ "disabled": false,
+ "duration": {
+ "startTime": null,
+ "combat": null
+ },
+ "description": "",
+ "origin": null,
+ "tint": "#ffffff",
+ "transfer": true,
+ "statuses": [],
+ "sort": 0,
+ "flags": {},
+ "_stats": {
+ "compendiumSource": null,
+ "duplicateSource": null,
+ "exportSource": null,
+ "coreVersion": "13.346",
+ "systemId": "daggerheart",
+ "systemVersion": "0.0.1",
+ "createdTime": 1752976985351,
+ "modifiedTime": 1752976985351,
+ "lastModifiedBy": "k0gmQFlvrPvlTtbh"
+ },
+ "_key": "!items.effects!6tr99y6wHaJJYy3J.5mi9ku2R4paP579i"
+ }
+ ],
+ "folder": null,
+ "ownership": {
+ "default": 0,
+ "k0gmQFlvrPvlTtbh": 3
+ },
+ "flags": {},
+ "_stats": {
+ "compendiumSource": null,
+ "duplicateSource": null,
+ "exportSource": null,
+ "coreVersion": "13.346",
+ "systemId": "daggerheart",
+ "systemVersion": "0.0.1",
+ "createdTime": 1752976985346,
+ "modifiedTime": 1752976987362,
+ "lastModifiedBy": "k0gmQFlvrPvlTtbh"
+ },
+ "_id": "6tr99y6wHaJJYy3J",
+ "sort": 100000,
+ "_key": "!items!6tr99y6wHaJJYy3J"
+}
diff --git a/src/packs/beastforms/beastform_Household_Friend_uxBugKULjn7O1KQc.json b/src/packs/beastforms/beastform_Household_Friend_uxBugKULjn7O1KQc.json
new file mode 100644
index 00000000..288afead
--- /dev/null
+++ b/src/packs/beastforms/beastform_Household_Friend_uxBugKULjn7O1KQc.json
@@ -0,0 +1,166 @@
+{
+ "name": "Household Friend",
+ "type": "beastform",
+ "img": "icons/creatures/mammals/cat-hunched-glowing-red.webp",
+ "system": {
+ "beastformType": "normal",
+ "tier": 1,
+ "tokenImg": "icons/creatures/mammals/cat-hunched-glowing-red.webp",
+ "tokenRingImg": "icons/svg/mystery-man.svg",
+ "tokenSize": {
+ "height": null,
+ "width": null
+ },
+ "mainTrait": "instinct",
+ "advantageOn": {
+ "u0mzlWihDHITgh1x": {
+ "value": "climb"
+ },
+ "m53oFXA2SA5jAjWc": {
+ "value": "locate"
+ },
+ "XLPn5Egg9mIuLyVP": {
+ "value": "protect"
+ }
+ },
+ "features": [
+ "Compendium.daggerheart.beastforms.Item.0tlnxIxlIw2hl1UE",
+ "Compendium.daggerheart.beastforms.Item.9ryNrYWjNtOT6DXN"
+ ],
+ "evolved": {
+ "mainTraitBonus": 0
+ },
+ "hybrid": {
+ "beastformOptions": 2,
+ "advantages": 2,
+ "features": 2
+ },
+ "examples": "Cat, Dog, Rabbit, etc."
+ },
+ "effects": [
+ {
+ "type": "beastform",
+ "name": "Beastform Transformation",
+ "img": "icons/creatures/abilities/paw-print-pair-purple.webp",
+ "_id": "d25tcdgssnDvekKR",
+ "system": {
+ "characterTokenData": {
+ "tokenImg": null,
+ "tokenRingImg": "icons/svg/mystery-man.svg",
+ "tokenSize": {}
+ },
+ "advantageOn": [],
+ "featureIds": [],
+ "effectIds": []
+ },
+ "changes": [
+ {
+ "key": "system.rules.attack.trait",
+ "mode": 5,
+ "value": "instinct",
+ "priority": null
+ },
+ {
+ "key": "system.rules.attack.range",
+ "mode": 5,
+ "value": "melee",
+ "priority": null
+ },
+ {
+ "key": "system.rules.attack.damage.value",
+ "mode": 5,
+ "value": "1d8",
+ "priority": null
+ }
+ ],
+ "disabled": false,
+ "duration": {
+ "startTime": null,
+ "combat": null,
+ "seconds": null,
+ "rounds": null,
+ "turns": null,
+ "startRound": null,
+ "startTurn": null
+ },
+ "description": "",
+ "origin": null,
+ "tint": "#ffffff",
+ "transfer": true,
+ "statuses": [],
+ "sort": 0,
+ "flags": {},
+ "_stats": {
+ "compendiumSource": null,
+ "duplicateSource": null,
+ "exportSource": null,
+ "coreVersion": "13.346",
+ "systemId": "daggerheart",
+ "systemVersion": "0.0.1",
+ "lastModifiedBy": null
+ },
+ "_key": "!items.effects!uxBugKULjn7O1KQc.d25tcdgssnDvekKR"
+ },
+ {
+ "type": "beastform",
+ "name": "Beastform Transformation",
+ "img": "icons/creatures/abilities/paw-print-pair-purple.webp",
+ "_id": "idqGh9sm1zBLME1O",
+ "system": {
+ "characterTokenData": {
+ "tokenImg": null,
+ "tokenRingImg": "icons/svg/mystery-man.svg",
+ "tokenSize": {}
+ },
+ "advantageOn": [],
+ "featureIds": [],
+ "effectIds": []
+ },
+ "changes": [],
+ "disabled": false,
+ "duration": {
+ "startTime": null,
+ "combat": null
+ },
+ "description": "",
+ "origin": null,
+ "tint": "#ffffff",
+ "transfer": true,
+ "statuses": [],
+ "sort": 0,
+ "flags": {},
+ "_stats": {
+ "compendiumSource": null,
+ "duplicateSource": null,
+ "exportSource": null,
+ "coreVersion": "13.346",
+ "systemId": "daggerheart",
+ "systemVersion": "0.0.1",
+ "createdTime": 1752976986453,
+ "modifiedTime": 1752976986453,
+ "lastModifiedBy": "k0gmQFlvrPvlTtbh"
+ },
+ "_key": "!items.effects!uxBugKULjn7O1KQc.idqGh9sm1zBLME1O"
+ }
+ ],
+ "folder": null,
+ "ownership": {
+ "default": 0,
+ "k0gmQFlvrPvlTtbh": 3
+ },
+ "flags": {},
+ "_stats": {
+ "compendiumSource": null,
+ "duplicateSource": null,
+ "exportSource": null,
+ "coreVersion": "13.346",
+ "systemId": "daggerheart",
+ "systemVersion": "0.0.1",
+ "createdTime": 1752976986449,
+ "modifiedTime": 1752976987362,
+ "lastModifiedBy": "k0gmQFlvrPvlTtbh"
+ },
+ "_id": "uxBugKULjn7O1KQc",
+ "sort": 300000,
+ "_key": "!items!uxBugKULjn7O1KQc"
+}
diff --git a/src/packs/beastforms/beastform_Legendary_Beast_mERwC7aMDoIKfZTf.json b/src/packs/beastforms/beastform_Legendary_Beast_mERwC7aMDoIKfZTf.json
new file mode 100644
index 00000000..4588bc0a
--- /dev/null
+++ b/src/packs/beastforms/beastform_Legendary_Beast_mERwC7aMDoIKfZTf.json
@@ -0,0 +1,154 @@
+{
+ "name": "Legendary Beast",
+ "type": "beastform",
+ "img": "icons/creatures/magical/spirit-undead-horned-blue.webp",
+ "system": {
+ "beastformType": "evolved",
+ "tier": 3,
+ "tokenImg": "icons/creatures/magical/spirit-undead-horned-blue.webp",
+ "tokenRingImg": "icons/svg/mystery-man.svg",
+ "tokenSize": {
+ "height": null,
+ "width": null
+ },
+ "mainTrait": "agility",
+ "advantageOn": {},
+ "features": [],
+ "evolved": {
+ "mainTraitBonus": 1,
+ "maximumTier": 1
+ },
+ "hybrid": {
+ "beastformOptions": 2,
+ "advantages": 2,
+ "features": 2
+ },
+ "examples": ""
+ },
+ "effects": [
+ {
+ "type": "beastform",
+ "name": "Beastform Transformation",
+ "img": "icons/creatures/abilities/paw-print-pair-purple.webp",
+ "_id": "cKAoI5JqYOtGBscd",
+ "system": {
+ "characterTokenData": {
+ "tokenImg": null,
+ "tokenRingImg": "icons/svg/mystery-man.svg",
+ "tokenSize": {}
+ },
+ "advantageOn": [],
+ "featureIds": [],
+ "effectIds": []
+ },
+ "changes": [
+ {
+ "key": "system.bonuses.damage.physical.bonus",
+ "mode": 2,
+ "value": "6",
+ "priority": null
+ },
+ {
+ "key": "system.bonuses.damage.magical.bonus",
+ "mode": 2,
+ "value": "6",
+ "priority": null
+ },
+ {
+ "key": "system.evasion",
+ "mode": 2,
+ "value": "2",
+ "priority": null
+ }
+ ],
+ "disabled": false,
+ "duration": {
+ "startTime": null,
+ "combat": null,
+ "seconds": null,
+ "rounds": null,
+ "turns": null,
+ "startRound": null,
+ "startTurn": null
+ },
+ "description": "
Pick a Tier 1 Beastform option and become a larger, more powerful version of that creature. While you’re in this form, you retain all traits and features from the original form and gain the following bonuses:
- A +6 bonus to damage rolls
- A +1 bonus to the trait used by this form
- A +2 bonus to evasion
To transform into this creature, mark 2 additional Stress. Choose any three Beastform options from Tiers 1–3. Choose a total of five advantages and three features from those options.
", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.346", + "systemId": "daggerheart", + "systemVersion": "0.0.1", + "lastModifiedBy": null + }, + "_key": "!items.effects!VI1DyowECDCDdsC1.dvqS0a0ur8xM2swY" + }, + { + "type": "beastform", + "name": "Beastform Transformation", + "img": "icons/creatures/abilities/paw-print-pair-purple.webp", + "_id": "xSMy4BO5PuqASEKW", + "system": { + "characterTokenData": { + "tokenImg": null, + "tokenRingImg": "icons/svg/mystery-man.svg", + "tokenSize": {} + }, + "advantageOn": [], + "featureIds": [], + "effectIds": [] + }, + "changes": [], + "disabled": false, + "duration": { + "startTime": null, + "combat": null + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.346", + "systemId": "daggerheart", + "systemVersion": "0.0.1", + "createdTime": 1752976990470, + "modifiedTime": 1752976990470, + "lastModifiedBy": "k0gmQFlvrPvlTtbh" + }, + "_key": "!items.effects!VI1DyowECDCDdsC1.xSMy4BO5PuqASEKW" + } + ], + "folder": null, + "ownership": { + "default": 0, + "k0gmQFlvrPvlTtbh": 3 + }, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.346", + "systemId": "daggerheart", + "systemVersion": "0.0.1", + "createdTime": 1752976990466, + "modifiedTime": 1752976990466, + "lastModifiedBy": "k0gmQFlvrPvlTtbh" + }, + "_id": "VI1DyowECDCDdsC1", + "sort": 0, + "_key": "!items!VI1DyowECDCDdsC1" +} diff --git a/src/packs/beastforms/feature_Agile_sef9mwD2eRLZ64oV.json b/src/packs/beastforms/feature_Agile_sef9mwD2eRLZ64oV.json new file mode 100644 index 00000000..70fd6e11 --- /dev/null +++ b/src/packs/beastforms/feature_Agile_sef9mwD2eRLZ64oV.json @@ -0,0 +1,34 @@ +{ + "type": "feature", + "name": "Agile", + "img": "icons/skills/movement/arrow-upward-blue.webp", + "system": { + "description": "Your movement is silent, and you can spend a Hope to move up to Far range without rolling.
", + "resource": null, + "originItemType": null, + "subType": null, + "originId": null, + "actions": [] + }, + "effects": [], + "folder": "uU8bIoZvXge0rLaU", + "ownership": { + "default": 0, + "k0gmQFlvrPvlTtbh": 3 + }, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.346", + "systemId": "daggerheart", + "systemVersion": "0.0.1", + "createdTime": 1752976877948, + "modifiedTime": 1752976906072, + "lastModifiedBy": "k0gmQFlvrPvlTtbh" + }, + "_id": "sef9mwD2eRLZ64oV", + "sort": 100000, + "_key": "!items!sef9mwD2eRLZ64oV" +} diff --git a/src/packs/beastforms/feature_Carrier_YSolAjtv6Sfnai98.json b/src/packs/beastforms/feature_Carrier_YSolAjtv6Sfnai98.json new file mode 100644 index 00000000..0ecd47f5 --- /dev/null +++ b/src/packs/beastforms/feature_Carrier_YSolAjtv6Sfnai98.json @@ -0,0 +1,34 @@ +{ + "type": "feature", + "name": "Carrier", + "img": "icons/creatures/abilities/bull-head-horns-glowing.webp", + "system": { + "description": "You can carry up to two willing allies with you when you move.
", + "resource": null, + "originItemType": null, + "subType": null, + "originId": null, + "actions": [] + }, + "effects": [], + "folder": "uU8bIoZvXge0rLaU", + "ownership": { + "default": 0, + "k0gmQFlvrPvlTtbh": 3 + }, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.346", + "systemId": "daggerheart", + "systemVersion": "0.0.1", + "createdTime": 1752976866309, + "modifiedTime": 1752976907469, + "lastModifiedBy": "k0gmQFlvrPvlTtbh" + }, + "_id": "YSolAjtv6Sfnai98", + "sort": 200000, + "_key": "!items!YSolAjtv6Sfnai98" +} diff --git a/src/packs/beastforms/feature_Companion_0tlnxIxlIw2hl1UE.json b/src/packs/beastforms/feature_Companion_0tlnxIxlIw2hl1UE.json new file mode 100644 index 00000000..d7f35b73 --- /dev/null +++ b/src/packs/beastforms/feature_Companion_0tlnxIxlIw2hl1UE.json @@ -0,0 +1,34 @@ +{ + "type": "feature", + "name": "Companion", + "img": "icons/magic/life/heart-hand-gold-green-light.webp", + "system": { + "description": "When you Help an Ally, you can roll a d8 as your advantage die.
", + "resource": null, + "originItemType": null, + "subType": null, + "originId": null, + "actions": [] + }, + "effects": [], + "folder": "uU8bIoZvXge0rLaU", + "ownership": { + "default": 0, + "k0gmQFlvrPvlTtbh": 3 + }, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.346", + "systemId": "daggerheart", + "systemVersion": "0.0.1", + "createdTime": 1752976848672, + "modifiedTime": 1752976908157, + "lastModifiedBy": "k0gmQFlvrPvlTtbh" + }, + "_id": "0tlnxIxlIw2hl1UE", + "sort": 0, + "_key": "!items!0tlnxIxlIw2hl1UE" +} diff --git a/src/packs/beastforms/feature_Evolved_MG21w4u5wXSGZ5WB.json b/src/packs/beastforms/feature_Evolved_MG21w4u5wXSGZ5WB.json new file mode 100644 index 00000000..520283bb --- /dev/null +++ b/src/packs/beastforms/feature_Evolved_MG21w4u5wXSGZ5WB.json @@ -0,0 +1,34 @@ +{ + "type": "feature", + "name": "Evolved", + "img": "icons/creatures/abilities/dragon-breath-purple.webp", + "system": { + "description": "Pick a Tier 1 Beastform option and become a larger, more powerful version of that creature. While you’re in this form, you retain all traits and features from the original form and gain the following bonuses:
When you take Major or greater damage, you drop out of Beastform.
", + "resource": null, + "originItemType": null, + "subType": null, + "originId": null, + "actions": [] + }, + "effects": [], + "folder": "uU8bIoZvXge0rLaU", + "ownership": { + "default": 0, + "k0gmQFlvrPvlTtbh": 3 + }, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.346", + "systemId": "daggerheart", + "systemVersion": "0.0.1", + "createdTime": 1752976855944, + "modifiedTime": 1752976909267, + "lastModifiedBy": "k0gmQFlvrPvlTtbh" + }, + "_id": "9ryNrYWjNtOT6DXN", + "sort": 150000, + "_key": "!items!9ryNrYWjNtOT6DXN" +} diff --git a/src/packs/beastforms/feature_Trample_P6tWFIZzXWyekw6r.json b/src/packs/beastforms/feature_Trample_P6tWFIZzXWyekw6r.json new file mode 100644 index 00000000..9e1ca0c5 --- /dev/null +++ b/src/packs/beastforms/feature_Trample_P6tWFIZzXWyekw6r.json @@ -0,0 +1,34 @@ +{ + "type": "feature", + "name": "Trample", + "img": "icons/creatures/mammals/ox-bull-horned-glowing-orange.webp", + "system": { + "description": "Mark a Stress to move up to Close range in a straight line and make an attack against all targets within Melee range of the line. Targets you succeed against take [[/r d8+1]] physical damage using your Proficiency and are temporarily Vulnerable.
", + "resource": null, + "originItemType": null, + "subType": null, + "originId": null, + "actions": [] + }, + "effects": [], + "folder": "uU8bIoZvXge0rLaU", + "ownership": { + "default": 0, + "k0gmQFlvrPvlTtbh": 3 + }, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.346", + "systemId": "daggerheart", + "systemVersion": "0.0.1", + "createdTime": 1752976867812, + "modifiedTime": 1752976976609, + "lastModifiedBy": "k0gmQFlvrPvlTtbh" + }, + "_id": "P6tWFIZzXWyekw6r", + "sort": -100000, + "_key": "!items!P6tWFIZzXWyekw6r" +} diff --git a/src/packs/beastforms/folders_Beastform_Features_uU8bIoZvXge0rLaU.json b/src/packs/beastforms/folders_Beastform_Features_uU8bIoZvXge0rLaU.json new file mode 100644 index 00000000..74cd56a6 --- /dev/null +++ b/src/packs/beastforms/folders_Beastform_Features_uU8bIoZvXge0rLaU.json @@ -0,0 +1,23 @@ +{ + "type": "Item", + "folder": null, + "name": "Beastform Features", + "color": null, + "sorting": "a", + "_id": "uU8bIoZvXge0rLaU", + "description": "", + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null, + "duplicateSource": null, + "exportSource": null, + "coreVersion": "13.346", + "systemId": "daggerheart", + "systemVersion": "0.0.1", + "createdTime": 1752976835537, + "modifiedTime": 1752976835537, + "lastModifiedBy": "k0gmQFlvrPvlTtbh" + }, + "_key": "!folders!uU8bIoZvXge0rLaU" +} diff --git a/styles/less/dialog/beastform/beastform-container.less b/styles/less/dialog/beastform/beastform-container.less deleted file mode 100644 index bf0d0cae..00000000 --- a/styles/less/dialog/beastform/beastform-container.less +++ /dev/null @@ -1,46 +0,0 @@ -@import '../../utils/colors.less'; - -.application.daggerheart.dh-style.views.beastform-selection { - .beastforms-container { - display: flex; - flex-direction: column; - gap: 4px; - - .beastforms-tier { - display: grid; - grid-template-columns: 1fr 1fr 1fr 1fr; - gap: 4px; - - .beastform-container { - position: relative; - display: flex; - justify-content: center; - border: 1px solid light-dark(@dark-blue, @golden); - border-radius: 6px; - cursor: pointer; - - &.inactive { - opacity: 0.4; - } - - img { - width: 100%; - border-radius: 6px; - } - - .beastform-title { - position: absolute; - top: 4px; - display: flex; - flex-wrap: wrap; - font-size: 16px; - margin: 0 4px; - border: 1px solid light-dark(@dark-blue, @golden); - border-radius: 6px; - color: light-dark(@beige, @dark); - background-image: url('../assets/parchments/dh-parchment-light.png'); - } - } - } - } -} diff --git a/styles/less/dialog/beastform/sheet.less b/styles/less/dialog/beastform/sheet.less index bd757b87..ce2e990d 100644 --- a/styles/less/dialog/beastform/sheet.less +++ b/styles/less/dialog/beastform/sheet.less @@ -1,15 +1,208 @@ @import '../../utils/colors.less'; @import '../../utils/mixin.less'; -.appTheme({ - &.beastform-selection { - .beastforms-container .beastforms-tier .beastform-container .beastform-title { - background-image: url('../assets/parchments/dh-parchment-dark.png'); +.theme-light .application.daggerheart.dh-style.views.beastform-selection .beastforms-outer-container { + .beastform-title { + background-image: url('../assets/parchments/dh-parchment-light.png'); + } + + .advanced-container { + .advanced-forms-container { + .advanced-form-container { + background-image: url('../assets/parchments/dh-parchment-light.png'); + } + + .hybrid-data-wrapper .hybrid-data-container .hybrid-data-inner-container .hybrid-data { + background-image: url('../assets/parchments/dh-parchment-light.png'); + } + } + .form-features .form-feature { + background-image: url('../assets/parchments/dh-parchment-light.png'); } } -}, {}); +} .application.daggerheart.dh-style.views.beastform-selection { + .beastform-nav { + nav { + flex: 1; + + a { + white-space: nowrap; + } + } + } + + .beastform-title { + position: absolute; + top: 4px; + padding: 0 2px; + display: flex; + flex-wrap: wrap; + text-align: center; + font-size: 16px; + margin: 0 4px; + border: 1px solid light-dark(@dark-blue, @golden); + border-radius: 6px; + color: light-dark(@dark, @beige); + background-image: url('../assets/parchments/dh-parchment-dark.png'); + } + + .beastforms-tier { + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr; + gap: 4px; + + .beastform-container { + position: relative; + display: flex; + justify-content: center; + border: 1px solid light-dark(@dark-blue, @golden); + border-radius: 6px; + cursor: pointer; + width: 120px; + height: 120px; + + &.inactive { + opacity: 0.4; + cursor: default; + } + + &.draggable { + cursor: pointer; + filter: drop-shadow(0 0 15px light-dark(@dark-blue, @golden)); + } + + img { + width: 100%; + border-radius: 6px; + } + } + } + + .advanced-container { + display: flex; + flex-direction: column; + align-items: center; + padding-top: 12px; + transition: width 0.3s ease; + + h2 { + margin: 0; + } + + .advanced-forms-container { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + gap: 16px; + } + + .advanced-form-container { + position: relative; + display: flex; + justify-content: center; + border: 1px solid light-dark(#18162e, #f3c267); + border-radius: 6px; + cursor: pointer; + width: 120px; + height: 120px; + align-items: center; + text-align: center; + color: light-dark(@dark, @beige); + background-image: url('../assets/parchments/dh-parchment-dark.png'); + + &.hybridized { + flex-direction: column; + justify-content: start; + padding-top: 4px; + height: 200px; + width: 100%; + overflow: hidden; + + &.empty { + justify-content: center; + } + + .beastform-title { + position: initial; + } + } + + .empty-form { + display: flex; + flex-direction: column; + align-items: center; + + i { + font-size: 24px; + } + } + + .beastform-title-wrapper { + height: 44px; + } + + .hybrid-data-wrapper { + overflow: auto; + + .hybrid-data-container { + display: flex; + flex-direction: column; + gap: 2px; + padding: 0 4px; + + label { + font-weight: bold; + } + + .hybrid-data-inner-container { + display: flex; + justify-content: center; + flex-wrap: wrap; + gap: 4px; + + .hybrid-data { + padding: 0 2px; + border: 1px solid light-dark(@dark-blue, @golden); + border-radius: 6px; + color: light-dark(@dark, @beige); + background-image: url('../assets/parchments/dh-parchment-dark.png'); + opacity: 0.4; + + &.active { + opacity: 1; + } + } + } + } + } + } + + .form-features { + display: flex; + flex-direction: column; + gap: 8px; + padding: 0 16px; + margin: 8px 0; + + .form-feature { + display: flex; + flex-direction: column; + gap: 4px; + padding: 0 4px; + border: 1px solid light-dark(@dark-blue, @golden); + border-radius: 6px; + color: light-dark(@dark, @beige); + background-image: url('../assets/parchments/dh-parchment-dark.png'); + + h4 { + text-align: center; + margin: 0; + } + } + } + } + footer { margin-top: 8px; display: flex; diff --git a/styles/less/dialog/index.less b/styles/less/dialog/index.less index f3e86518..66fd981d 100644 --- a/styles/less/dialog/index.less +++ b/styles/less/dialog/index.less @@ -12,7 +12,6 @@ @import './downtime/downtime-container.less'; -@import './beastform/beastform-container.less'; @import './beastform/sheet.less'; @import './character-creation/creation-action-footer.less'; diff --git a/styles/less/sheets/index.less b/styles/less/sheets/index.less index 89370ec6..3470de37 100644 --- a/styles/less/sheets/index.less +++ b/styles/less/sheets/index.less @@ -18,6 +18,7 @@ @import './actors/environment/header.less'; @import './actors/environment/sheet.less'; +@import './items/beastform.less'; @import './items/class.less'; @import './items/domain-card.less'; @import './items/feature.less'; diff --git a/styles/less/sheets/items/beastform.less b/styles/less/sheets/items/beastform.less new file mode 100644 index 00000000..162c4925 --- /dev/null +++ b/styles/less/sheets/items/beastform.less @@ -0,0 +1,9 @@ +.application.sheet.daggerheart.dh-style.beastform { + .settings.tab { + .advantage-on-section { + display: flex; + flex-direction: column; + margin-top: 10px; + } + } +} diff --git a/styles/less/ui/chat/chat.less b/styles/less/ui/chat/chat.less index 6b5db1b9..c2c72d48 100644 --- a/styles/less/ui/chat/chat.less +++ b/styles/less/ui/chat/chat.less @@ -21,13 +21,8 @@ width: 80px; } - .downtime-refresh-container { - margin-top: @fullMargin; + .action-use-button { width: 100%; - - .refresh-title { - font-weight: bold; - } } } @@ -365,6 +360,7 @@ width: 80px; } } + button { &.inner-button { --button-size: 1.25rem; diff --git a/styles/less/ui/chat/sheet.less b/styles/less/ui/chat/sheet.less index 58281690..1c41f97e 100644 --- a/styles/less/ui/chat/sheet.less +++ b/styles/less/ui/chat/sheet.less @@ -4,6 +4,27 @@ .dice-title { display: none; } + + .message-content { + .enriched-effect { + display: flex; + align-items: center; + border: 1px solid black; + width: fit-content; + padding: 0 2px 0 0; + border-radius: 6px; + color: @dark; + background-image: url(../assets/parchments/dh-parchment-light.png); + + &:hover { + text-shadow: none; + } + + span { + white-space: nowrap; + } + } + } } fieldset.daggerheart.chat { diff --git a/styles/less/ux/tooltip/tooltip.less b/styles/less/ux/tooltip/tooltip.less index 0060f74b..e6b660a5 100644 --- a/styles/less/ux/tooltip/tooltip.less +++ b/styles/less/ux/tooltip/tooltip.less @@ -74,6 +74,22 @@ } } + .tooltip-chips { + display: flex; + justify-content: space-around; + flex-wrap: wrap; + gap: 8px; + + .tooltip-chip { + font-size: 18px; + padding: 2px 4px; + border: 1px solid light-dark(@dark-blue, @golden); + border-radius: 6px; + color: light-dark(@dark, @beige); + background-image: url(../assets/parchments/dh-parchment-dark.png); + } + } + .tooltip-tags { width: 100%; display: flex; diff --git a/system.json b/system.json index bfa25df3..eaef71c1 100644 --- a/system.json +++ b/system.json @@ -144,6 +144,15 @@ "type": "Item", "private": false, "flags": {} + }, + { + "name": "beastforms", + "label": "Beastforms", + "system": "daggerheart", + "path": "packs/beastforms.db", + "type": "Item", + "private": false, + "flags": {} } ], "packFolders": [ @@ -157,7 +166,7 @@ "name": "Character Options", "sorting": "m", "color": "#000000", - "packs": ["ancestries", "communities", "classes", "subclasses", "domains"] + "packs": ["ancestries", "communities", "classes", "subclasses", "domains", "beastforms"] }, { "name": "Items", diff --git a/templates/dialogs/beastform/advanced.hbs b/templates/dialogs/beastform/advanced.hbs new file mode 100644 index 00000000..78a690da --- /dev/null +++ b/templates/dialogs/beastform/advanced.hbs @@ -0,0 +1,73 @@ +{{document.system.proficiency}}