mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-12 03:31:07 +01:00
[Feature] Beastform Types (#372)
* Temp * Finished Evolved * Fixed hybrid * Changed generalConfig.tiers to be number based * Weaponhandling while in beastform * Added unarmed strike in sidebar * Added DamageEnricher * Added effect enricher * Corrected downtime buttons and actions * Added BeastformTooltip * Split the BeastformDialog into parts with tabs * Added temp beastform features * rollData change * Improvement * character.getRollData cleanup
This commit is contained in:
parent
867947c2c5
commit
42a705a870
93 changed files with 2795 additions and 538 deletions
|
|
@ -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) => {
|
||||
|
|
|
|||
77
lang/en.json
77
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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}, {});
|
||||
|
||||
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;
|
||||
}, {}); // Also get from compendium when added
|
||||
context.canSubmit = this.selected;
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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 }))
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
|
|
|
|||
|
|
@ -84,15 +84,16 @@ export default function DHApplicationMixin(Base) {
|
|||
useItem: DHSheetV2.#useItem,
|
||||
useAction: DHSheetV2.#useAction,
|
||||
toggleEffect: DHSheetV2.#toggleEffect,
|
||||
toggleExtended: DHSheetV2.#toggleExtended,
|
||||
toggleExtended: DHSheetV2.#toggleExtended
|
||||
},
|
||||
contextMenus: [{
|
||||
contextMenus: [
|
||||
{
|
||||
handler: DHSheetV2.#getEffectContextOptions,
|
||||
selector: '[data-item-uuid][data-type="effect"]',
|
||||
options: {
|
||||
parentClassHooks: false,
|
||||
fixed: true
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
handler: DHSheetV2.#getActionContextOptions,
|
||||
|
|
@ -101,7 +102,8 @@ export default function DHApplicationMixin(Base) {
|
|||
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,22 +342,25 @@ 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({
|
||||
if (usable)
|
||||
options.unshift({
|
||||
name: 'DAGGERHEART.APPLICATIONS.ContextMenu.useItem',
|
||||
icon: 'fa-solid fa-burst',
|
||||
callback: (target, event) => getDocFromElement(target).use(event),
|
||||
callback: (target, event) => getDocFromElement(target).use(event)
|
||||
});
|
||||
|
||||
if (toChat) options.unshift({
|
||||
if (toChat)
|
||||
options.unshift({
|
||||
name: 'DAGGERHEART.APPLICATIONS.ContextMenu.sendToChat',
|
||||
icon: 'fa-solid fa-message',
|
||||
callback: (target) => getDocFromElement(target).toChat(this.document.id),
|
||||
callback: target => getDocFromElement(target).toChat(this.document.id)
|
||||
});
|
||||
|
||||
if (deletable) options.push({
|
||||
if (deletable)
|
||||
options.push({
|
||||
name: 'CONTROLS.CommonDelete',
|
||||
icon: 'fa-solid fa-trash',
|
||||
callback: (target, event) => {
|
||||
|
|
@ -360,12 +368,12 @@ export default function DHApplicationMixin(Base) {
|
|||
if (event.shiftKey) return doc.delete();
|
||||
else return doc.deleteDialog();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
return options.map(option => ({
|
||||
...option,
|
||||
icon: `<i class="${option.icon}"></i>`
|
||||
}))
|
||||
}));
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
|
@ -406,11 +414,14 @@ export default function DHApplicationMixin(Base) {
|
|||
|
||||
const description = doc.system?.description ?? doc.description;
|
||||
const isAction = !!actionId;
|
||||
descriptionElement.innerHTML = await foundry.applications.ux.TextEditor.implementation.enrichHTML(description, {
|
||||
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,7 +438,8 @@ 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({
|
||||
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',
|
||||
|
|
@ -436,12 +448,13 @@ export default function DHApplicationMixin(Base) {
|
|||
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({
|
||||
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),
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
/* -------------------------------------------- */
|
||||
|
|
|
|||
|
|
@ -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,12 +113,11 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
|||
* @protected
|
||||
*/
|
||||
static #getFeatureContextOptions() {
|
||||
const options = this._getContextMenuCommonOptions({ usable: true, toChat: true, deletable: false })
|
||||
options.push(
|
||||
{
|
||||
const options = this._getContextMenuCommonOptions({ usable: true, toChat: true, deletable: false });
|
||||
options.push({
|
||||
name: 'CONTROLS.CommonDelete',
|
||||
icon: '<i class="fa-solid fa-trash"></i>',
|
||||
callback: async (target) => {
|
||||
callback: async target => {
|
||||
const feature = getDocFromElement(target);
|
||||
if (!feature) return;
|
||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||
|
|
@ -128,15 +127,16 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
|||
name: feature.name
|
||||
})
|
||||
},
|
||||
content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { 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)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 `<tag
|
||||
contenteditable='false'
|
||||
spellcheck='false'
|
||||
tabIndex="${this.settings.a11y.focusableTags ? 0 : -1}"
|
||||
class="${this.settings.classNames.tag} ${tagData.class ? tagData.class : ''}"
|
||||
${this.getAttributes(tagData)}>
|
||||
<x class="${this.settings.classNames.tagX}" role='button' aria-label='remove tag'></x>
|
||||
<div>
|
||||
<span class="${this.settings.classNames.tagText}">${tagData[this.settings.tagTextProp] || tagData.value}</span>
|
||||
${tagData.src ? `<img src="${tagData.src}"></i>` : ''}
|
||||
</div>
|
||||
</tag>`;
|
||||
}
|
||||
}
|
||||
});
|
||||
tagifyElement.on('add', this.advantageOnAdd.bind(this));
|
||||
tagifyElement.on('remove', this.advantageOnRemove.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
/**@inheritdoc */
|
||||
async _preparePartContext(partId, context, options) {
|
||||
await super._preparePartContext(partId, context, options);
|
||||
|
||||
switch (partId) {
|
||||
case 'settings':
|
||||
context.advantageOn = JSON.stringify(
|
||||
Object.keys(context.document.system.advantageOn).map(key => ({
|
||||
value: key,
|
||||
name: context.document.system.advantageOn[key].value
|
||||
}))
|
||||
);
|
||||
break;
|
||||
case 'effects':
|
||||
context.effects.actives = context.effects.actives.map(effect => {
|
||||
const data = effect.toObject();
|
||||
data.id = effect.id;
|
||||
if (effect.type === 'beastform') data.mandatory = true;
|
||||
|
||||
return data;
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
async advantageOnAdd(event) {
|
||||
await this.document.update({
|
||||
[`system.advantageOn.${foundry.utils.randomID()}`]: { value: event.detail.data.value }
|
||||
});
|
||||
}
|
||||
|
||||
async advantageOnRemove(event) {
|
||||
await this.document.update({
|
||||
[`system.advantageOn.-=${event.detail.data.value}`]: null
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ export default class ClassSheet extends DHBaseItemSheet {
|
|||
position: { width: 700 },
|
||||
actions: {
|
||||
removeItemFromCollection: ClassSheet.#removeItemFromCollection,
|
||||
removeSuggestedItem: ClassSheet.#removeSuggestedItem,
|
||||
removeSuggestedItem: ClassSheet.#removeSuggestedItem
|
||||
},
|
||||
tagifyConfigs: [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ export default class WeaponSheet extends ItemAttachmentSheet(DHBaseItemSheet) {
|
|||
template: 'systems/daggerheart/templates/sheets/global/tabs/tab-effects.hbs',
|
||||
scrollable: ['.effects']
|
||||
},
|
||||
...super.PARTS,
|
||||
...super.PARTS
|
||||
};
|
||||
|
||||
/**@inheritdoc */
|
||||
|
|
|
|||
|
|
@ -212,15 +212,18 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
|
||||
for (let target of targets) {
|
||||
let damages = foundry.utils.deepClone(message.system.damage?.roll ?? message.system.roll);
|
||||
if (message.system.onSave && message.system.targets.find(t => t.id === target.id)?.saved?.success === true) {
|
||||
if (
|
||||
message.system.onSave &&
|
||||
message.system.targets.find(t => t.id === target.id)?.saved?.success === true
|
||||
) {
|
||||
const mod = CONFIG.DH.ACTIONS.damageOnSave[message.system.onSave]?.mod ?? 1;
|
||||
Object.entries(damages).forEach(([k,v]) => {
|
||||
Object.entries(damages).forEach(([k, v]) => {
|
||||
v.total = 0;
|
||||
v.parts.forEach(part => {
|
||||
part.total = Math.ceil(part.total * mod);
|
||||
v.total += part.total;
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
target.actor.takeDamage(damages);
|
||||
|
|
@ -294,15 +297,16 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
await actor.useAction(action);
|
||||
};
|
||||
|
||||
actionUseButton = async (_, message) => {
|
||||
actionUseButton = async (event, message) => {
|
||||
const { moveIndex, actionIndex } = event.currentTarget.dataset;
|
||||
const parent = await foundry.utils.fromUuid(message.system.actor);
|
||||
const actionType = Object.values(message.system.moves)[0].actions[0];
|
||||
const cls = CONFIG.DH.ACTIONS.actionTypes[actionType.type];
|
||||
const actionType = message.system.moves[moveIndex].actions[actionIndex];
|
||||
const cls = game.system.api.models.actions.actionsTypes[actionType.type];
|
||||
const action = new cls(
|
||||
{ ...actionType, _id: foundry.utils.randomID(), name: game.i18n.localize(actionType.name) },
|
||||
{ parent: parent }
|
||||
{ parent: parent.system }
|
||||
);
|
||||
|
||||
action.use();
|
||||
action.use(event);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ export default class DhHotbar extends foundry.applications.ui.Hotbar {
|
|||
|
||||
async createItemMacro(data, slot) {
|
||||
const macro = await Macro.implementation.create({
|
||||
name: `${game.i18n.localize('Display')} ${name}`,
|
||||
name: data.name,
|
||||
type: CONST.MACRO_TYPES.SCRIPT,
|
||||
img: data.img,
|
||||
command: `await game.system.api.applications.ui.DhHotbar.useItem("${data.uuid}");`
|
||||
|
|
@ -109,7 +109,7 @@ export default class DhHotbar extends foundry.applications.ui.Hotbar {
|
|||
|
||||
async createActionMacro(data, slot) {
|
||||
const macro = await Macro.implementation.create({
|
||||
name: `${game.i18n.localize('Display')} ${name}`,
|
||||
name: data.data.name,
|
||||
type: CONST.MACRO_TYPES.SCRIPT,
|
||||
img: data.data.img,
|
||||
command: `await game.system.api.applications.ui.DhHotbar.useAction("${data.data.itemUuid}", "${data.data.id}");`
|
||||
|
|
@ -119,7 +119,7 @@ export default class DhHotbar extends foundry.applications.ui.Hotbar {
|
|||
|
||||
async createAttackMacro(data, slot) {
|
||||
const macro = await Macro.implementation.create({
|
||||
name: `${game.i18n.localize('Display')} ${name}`,
|
||||
name: data.name,
|
||||
type: CONST.MACRO_TYPES.SCRIPT,
|
||||
img: data.img,
|
||||
command: `await game.system.api.applications.ui.DhHotbar.useAttack("${data.actorUuid}");`
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
export const abilities = {
|
||||
agility: {
|
||||
id: 'agility',
|
||||
label: 'DAGGERHEART.CONFIG.Traits.agility.name',
|
||||
verbs: [
|
||||
'DAGGERHEART.CONFIG.Traits.agility.verb.sprint',
|
||||
|
|
@ -8,6 +9,7 @@ export const abilities = {
|
|||
]
|
||||
},
|
||||
strength: {
|
||||
id: 'strength',
|
||||
label: 'DAGGERHEART.CONFIG.Traits.strength.name',
|
||||
verbs: [
|
||||
'DAGGERHEART.CONFIG.Traits.strength.verb.lift',
|
||||
|
|
@ -16,6 +18,7 @@ export const abilities = {
|
|||
]
|
||||
},
|
||||
finesse: {
|
||||
id: 'finesse',
|
||||
label: 'DAGGERHEART.CONFIG.Traits.finesse.name',
|
||||
verbs: [
|
||||
'DAGGERHEART.CONFIG.Traits.finesse.verb.control',
|
||||
|
|
@ -24,6 +27,7 @@ export const abilities = {
|
|||
]
|
||||
},
|
||||
instinct: {
|
||||
id: 'instinct',
|
||||
label: 'DAGGERHEART.CONFIG.Traits.instinct.name',
|
||||
verbs: [
|
||||
'DAGGERHEART.CONFIG.Traits.instinct.verb.perceive',
|
||||
|
|
@ -32,6 +36,7 @@ export const abilities = {
|
|||
]
|
||||
},
|
||||
presence: {
|
||||
id: 'presence',
|
||||
label: 'DAGGERHEART.CONFIG.Traits.presence.name',
|
||||
verbs: [
|
||||
'DAGGERHEART.CONFIG.Traits.presence.verb.charm',
|
||||
|
|
@ -40,6 +45,7 @@ export const abilities = {
|
|||
]
|
||||
},
|
||||
knowledge: {
|
||||
id: 'knowledge',
|
||||
label: 'DAGGERHEART.CONFIG.Traits.knowledge.name',
|
||||
verbs: [
|
||||
'DAGGERHEART.CONFIG.Traits.knowledge.verb.recall',
|
||||
|
|
|
|||
|
|
@ -145,11 +145,11 @@ export const defaultRestOptions = {
|
|||
img: 'icons/magic/life/cross-worn-green.webp',
|
||||
actionType: 'action',
|
||||
healing: {
|
||||
type: 'health',
|
||||
applyTo: healingTypes.hitPoints.id,
|
||||
value: {
|
||||
custom: {
|
||||
enabled: true,
|
||||
formula: '1d4 + 1' // should be 1d4 + {tier}. How to use the roll param?
|
||||
formula: '1d4 + @tier'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -169,11 +169,11 @@ export const defaultRestOptions = {
|
|||
img: 'icons/magic/perception/eye-ringed-green.webp',
|
||||
actionType: 'action',
|
||||
healing: {
|
||||
type: 'stress',
|
||||
applyTo: healingTypes.stress.id,
|
||||
value: {
|
||||
custom: {
|
||||
enabled: true,
|
||||
formula: '1d4 + 1' // should be 1d4 + {tier}. How to use the roll param?
|
||||
formula: '1d4 + @tier'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -186,7 +186,23 @@ export const defaultRestOptions = {
|
|||
icon: 'fa-solid fa-hammer',
|
||||
img: 'icons/skills/trades/smithing-anvil-silver-red.webp',
|
||||
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.repairArmor.description'),
|
||||
actions: []
|
||||
actions: [
|
||||
{
|
||||
type: 'healing',
|
||||
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.repairArmor.name'),
|
||||
img: 'icons/skills/trades/smithing-anvil-silver-red.webp',
|
||||
actionType: 'action',
|
||||
healing: {
|
||||
applyTo: healingTypes.armorStack.id,
|
||||
value: {
|
||||
custom: {
|
||||
enabled: true,
|
||||
formula: '1d4 + @tier'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
prepare: {
|
||||
id: 'prepare',
|
||||
|
|
@ -263,25 +279,21 @@ export const deathMoves = {
|
|||
};
|
||||
|
||||
export const tiers = {
|
||||
tier1: {
|
||||
id: 'tier1',
|
||||
label: 'DAGGERHEART.GENERAL.Tiers.tier1',
|
||||
value: 1
|
||||
1: {
|
||||
id: 1,
|
||||
label: 'DAGGERHEART.GENERAL.Tiers.1'
|
||||
},
|
||||
tier2: {
|
||||
id: 'tier2',
|
||||
label: 'DAGGERHEART.GENERAL.Tiers.tier2',
|
||||
value: 2
|
||||
2: {
|
||||
id: 2,
|
||||
label: 'DAGGERHEART.GENERAL.Tiers.2'
|
||||
},
|
||||
tier3: {
|
||||
id: 'tier3',
|
||||
label: 'DAGGERHEART.GENERAL.Tiers.tier3',
|
||||
value: 3
|
||||
3: {
|
||||
id: 3,
|
||||
label: 'DAGGERHEART.GENERAL.Tiers.3'
|
||||
},
|
||||
tier4: {
|
||||
id: 'tier4',
|
||||
label: 'DAGGERHEART.GENERAL.Tiers.tier4',
|
||||
value: 4
|
||||
4: {
|
||||
id: 4,
|
||||
label: 'DAGGERHEART.GENERAL.Tiers.4'
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1346,3 +1346,18 @@ export const itemResourceTypes = {
|
|||
label: 'DAGGERHEART.CONFIG.ItemResourceType.diceValue'
|
||||
}
|
||||
};
|
||||
|
||||
export const beastformTypes = {
|
||||
normal: {
|
||||
id: 'normal',
|
||||
label: 'DAGGERHEART.CONFIG.BeastformType.normal'
|
||||
},
|
||||
evolved: {
|
||||
id: 'evolved',
|
||||
label: 'DAGGERHEART.CONFIG.BeastformType.evolved'
|
||||
},
|
||||
hybrid: {
|
||||
id: 'hybrid',
|
||||
label: 'DAGGERHEART.CONFIG.BeastformType.hybrid'
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ export class DHResourceData extends foundry.abstract.DataModel {
|
|||
}),
|
||||
value: new fields.EmbeddedDataField(DHActionDiceData),
|
||||
valueAlt: new fields.EmbeddedDataField(DHActionDiceData)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -134,6 +134,6 @@ export class DHDamageData extends DHResourceData {
|
|||
label: 'Type'
|
||||
}
|
||||
)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
|||
}
|
||||
|
||||
getRollData(data = {}) {
|
||||
if(!this.actor) return null;
|
||||
if (!this.actor) return null;
|
||||
const actorData = this.actor.getRollData(false);
|
||||
|
||||
// Add Roll results to RollDatas
|
||||
|
|
@ -178,7 +178,7 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
|||
}
|
||||
|
||||
async use(event, ...args) {
|
||||
if(!this.actor) throw new Error("An Action can't be used outside of an Actor context.");
|
||||
if (!this.actor) throw new Error("An Action can't be used outside of an Actor context.");
|
||||
|
||||
const isFastForward = event.shiftKey || (!this.hasRoll && !this.hasSave);
|
||||
// Prepare base Config
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ export default class DhBeastformAction extends DHBaseAction {
|
|||
const abort = await this.handleActiveTransformations();
|
||||
if (abort) return;
|
||||
|
||||
const beastformUuid = await BeastformDialog.configure(beastformConfig);
|
||||
if (!beastformUuid) return;
|
||||
const { selected, evolved, hybrid } = await BeastformDialog.configure(beastformConfig);
|
||||
if (!selected) return;
|
||||
|
||||
await this.transform(beastformUuid);
|
||||
await this.transform(selected, evolved, hybrid);
|
||||
}
|
||||
|
||||
prepareBeastformConfig(config) {
|
||||
|
|
@ -29,21 +29,48 @@ export default class DhBeastformAction extends DHBaseAction {
|
|||
};
|
||||
}
|
||||
|
||||
async transform(beastformUuid) {
|
||||
const beastform = await foundry.utils.fromUuid(beastformUuid);
|
||||
this.actor.createEmbeddedDocuments('Item', [beastform.toObject()]);
|
||||
async transform(selectedForm, evolvedData, hybridData) {
|
||||
const formData = evolvedData?.form ? evolvedData.form.toObject() : selectedForm.toObject();
|
||||
const beastformEffect = formData.effects.find(x => x.type === 'beastform');
|
||||
if (!beastformEffect) {
|
||||
ui.notifications.error('DAGGERHEART.UI.Notifications.beastformMissingEffect');
|
||||
return;
|
||||
}
|
||||
|
||||
if (evolvedData?.form) {
|
||||
const evolvedForm = selectedForm.effects.find(x => x.type === 'beastform');
|
||||
if (!evolvedForm) {
|
||||
ui.notifications.error('DAGGERHEART.UI.Notifications.beastformMissingEffect');
|
||||
return;
|
||||
}
|
||||
|
||||
beastformEffect.changes = [...beastformEffect.changes, ...evolvedForm.changes];
|
||||
formData.system.features = [...formData.system.features, ...selectedForm.system.features.map(x => x.uuid)];
|
||||
}
|
||||
|
||||
if (selectedForm.system.beastformType === CONFIG.DH.ITEM.beastformTypes.hybrid.id) {
|
||||
formData.system.advantageOn = Object.values(hybridData.advantages).reduce((advantages, formCategory) => {
|
||||
Object.keys(formCategory).forEach(advantageKey => {
|
||||
advantages[advantageKey] = formCategory[advantageKey];
|
||||
});
|
||||
return advantages;
|
||||
}, {});
|
||||
formData.system.features = [
|
||||
...formData.system.features,
|
||||
...Object.values(hybridData.features).flatMap(x => Object.keys(x))
|
||||
];
|
||||
}
|
||||
|
||||
this.actor.createEmbeddedDocuments('Item', [formData]);
|
||||
}
|
||||
|
||||
async handleActiveTransformations() {
|
||||
const beastformEffects = this.actor.effects.filter(x => x.type === 'beastform');
|
||||
if (beastformEffects.length > 0) {
|
||||
for (let effect of beastformEffects) {
|
||||
await effect.delete();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
const existingEffects = beastformEffects.length > 0;
|
||||
await this.actor.deleteEmbeddedDocuments(
|
||||
'ActiveEffect',
|
||||
beastformEffects.map(x => x.id)
|
||||
);
|
||||
return existingEffects;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,13 +22,14 @@ export default class DHDamageAction extends DHBaseAction {
|
|||
formatFormulas(formulas, systemData) {
|
||||
const formattedFormulas = [];
|
||||
formulas.forEach(formula => {
|
||||
if (isNaN(formula.formula)) formula.formula = Roll.replaceFormulaData(formula.formula, this.getRollData(systemData));
|
||||
const same = formattedFormulas.find(f => setsEqual(f.damageTypes, formula.damageTypes) && f.applyTo === formula.applyTo);
|
||||
if(same)
|
||||
same.formula += ` + ${formula.formula}`;
|
||||
else
|
||||
formattedFormulas.push(formula);
|
||||
})
|
||||
if (isNaN(formula.formula))
|
||||
formula.formula = Roll.replaceFormulaData(formula.formula, this.getRollData(systemData));
|
||||
const same = formattedFormulas.find(
|
||||
f => setsEqual(f.damageTypes, formula.damageTypes) && f.applyTo === formula.applyTo
|
||||
);
|
||||
if (same) same.formula += ` + ${formula.formula}`;
|
||||
else formattedFormulas.push(formula);
|
||||
});
|
||||
return formattedFormulas;
|
||||
}
|
||||
|
||||
|
|
@ -41,12 +42,12 @@ export default class DHDamageAction extends DHBaseAction {
|
|||
applyTo: p.applyTo
|
||||
}));
|
||||
|
||||
if(!formulas.length) return;
|
||||
if (!formulas.length) return;
|
||||
|
||||
formulas = this.formatFormulas(formulas, systemData);
|
||||
|
||||
const config = {
|
||||
title: game.i18n.format('DAGGERHEART.UI.Chat.damageRoll.title', { damage: this.name }),
|
||||
title: game.i18n.format('DAGGERHEART.UI.Chat.damageRoll.title', { damage: game.i18n.localize(this.name) }),
|
||||
roll: formulas,
|
||||
targets: systemData.targets.filter(t => t.hit) ?? data.targets,
|
||||
hasSave: this.hasSave,
|
||||
|
|
|
|||
|
|
@ -16,10 +16,12 @@ export default class DHHealingAction extends DHBaseAction {
|
|||
|
||||
async rollHealing(event, data) {
|
||||
const systemData = data.system ?? data;
|
||||
let formulas = [{
|
||||
let formulas = [
|
||||
{
|
||||
formula: this.getFormulaValue(data).getFormula(this.actor),
|
||||
applyTo: this.healing.applyTo
|
||||
}];
|
||||
}
|
||||
];
|
||||
|
||||
const config = {
|
||||
title: game.i18n.format('DAGGERHEART.UI.Chat.healingRoll.title', {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,13 @@ export default class BeastformEffect extends foundry.abstract.TypeDataModel {
|
|||
};
|
||||
}
|
||||
|
||||
async _onCreate() {
|
||||
if (this.parent.parent?.type === 'character') {
|
||||
this.parent.parent.system.primaryWeapon?.update?.({ 'system.equipped': false });
|
||||
this.parent.parent.system.secondayWeapon?.update?.({ 'system.equipped': false });
|
||||
}
|
||||
}
|
||||
|
||||
async _preDelete() {
|
||||
if (this.parent.parent.type === 'character') {
|
||||
const update = {
|
||||
|
|
|
|||
|
|
@ -18,10 +18,11 @@ export default class DhpAdversary extends BaseDataActor {
|
|||
const fields = foundry.data.fields;
|
||||
return {
|
||||
...super.defineSchema(),
|
||||
tier: new fields.StringField({
|
||||
tier: new fields.NumberField({
|
||||
required: true,
|
||||
integer: true,
|
||||
choices: CONFIG.DH.GENERAL.tiers,
|
||||
initial: CONFIG.DH.GENERAL.tiers.tier1.id
|
||||
initial: CONFIG.DH.GENERAL.tiers[1].id
|
||||
}),
|
||||
type: new fields.StringField({
|
||||
required: true,
|
||||
|
|
@ -52,7 +53,7 @@ export default class DhpAdversary extends BaseDataActor {
|
|||
})
|
||||
}),
|
||||
resources: new fields.SchemaField({
|
||||
hitPoints: resourceField(0, 'DAGGERHEART.GENERAL.hitPoints.plural', true),
|
||||
hitPoints: resourceField(0, 'DAGGERHEART.GENERAL.HitPoints.plural', true),
|
||||
stress: resourceField(0, 'DAGGERHEART.GENERAL.stress', true)
|
||||
}),
|
||||
attack: new ActionField({
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
|||
import DhLevelData from '../levelData.mjs';
|
||||
import BaseDataActor from './base.mjs';
|
||||
import { attributeField, resourceField, stressDamageReductionRule, bonusField } from '../fields/actorField.mjs';
|
||||
import ActionField from '../fields/actionField.mjs';
|
||||
|
||||
export default class DhCharacter extends BaseDataActor {
|
||||
static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Character'];
|
||||
|
|
@ -21,7 +22,7 @@ export default class DhCharacter extends BaseDataActor {
|
|||
return {
|
||||
...super.defineSchema(),
|
||||
resources: new fields.SchemaField({
|
||||
hitPoints: resourceField(0, 'DAGGERHEART.GENERAL.hitPoints.plural', true),
|
||||
hitPoints: resourceField(0, 'DAGGERHEART.GENERAL.HitPoints.plural', true),
|
||||
stress: resourceField(6, 'DAGGERHEART.GENERAL.stress', true),
|
||||
hope: resourceField(6, 'DAGGERHEART.GENERAL.hope')
|
||||
}),
|
||||
|
|
@ -87,8 +88,45 @@ export default class DhCharacter extends BaseDataActor {
|
|||
value: new ForeignDocumentUUIDField({ type: 'Item', nullable: true }),
|
||||
subclass: new ForeignDocumentUUIDField({ type: 'Item', nullable: true })
|
||||
}),
|
||||
advantageSources: new fields.ArrayField(new fields.StringField()),
|
||||
disadvantageSources: new fields.ArrayField(new fields.StringField()),
|
||||
attack: new ActionField({
|
||||
initial: {
|
||||
name: 'Attack',
|
||||
img: 'icons/skills/melee/unarmed-punch-fist-yellow-red.webp',
|
||||
_id: foundry.utils.randomID(),
|
||||
systemPath: 'attack',
|
||||
type: 'attack',
|
||||
range: 'melee',
|
||||
target: {
|
||||
type: 'any',
|
||||
amount: 1
|
||||
},
|
||||
roll: {
|
||||
type: 'attack',
|
||||
trait: 'strength'
|
||||
},
|
||||
damage: {
|
||||
parts: [
|
||||
{
|
||||
type: ['physical'],
|
||||
value: {
|
||||
custom: {
|
||||
enabled: true,
|
||||
formula: '@system.rules.attack.damage.value'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}),
|
||||
advantageSources: new fields.ArrayField(new fields.StringField(), {
|
||||
label: 'DAGGERHEART.ACTORS.Character.advantageSources.label',
|
||||
hint: 'DAGGERHEART.ACTORS.Character.advantageSources.hint'
|
||||
}),
|
||||
disadvantageSources: new fields.ArrayField(new fields.StringField(), {
|
||||
label: 'DAGGERHEART.ACTORS.Character.disadvantageSources.label',
|
||||
hint: 'DAGGERHEART.ACTORS.Character.disadvantageSources.hint'
|
||||
}),
|
||||
levelData: new fields.EmbeddedDataField(DhLevelData),
|
||||
bonuses: new fields.SchemaField({
|
||||
roll: new fields.SchemaField({
|
||||
|
|
@ -198,6 +236,15 @@ export default class DhCharacter extends BaseDataActor {
|
|||
magical: new fields.BooleanField({ initial: false }),
|
||||
physical: new fields.BooleanField({ initial: false })
|
||||
}),
|
||||
attack: new fields.SchemaField({
|
||||
damage: new fields.SchemaField({
|
||||
value: new fields.StringField({
|
||||
required: true,
|
||||
initial: '@profd4',
|
||||
label: 'DAGGERHEART.GENERAL.Rules.attack.damage.value.label'
|
||||
})
|
||||
})
|
||||
}),
|
||||
weapon: new fields.SchemaField({
|
||||
/* Unimplemented
|
||||
-> Should remove the lowest damage dice from weapon damage
|
||||
|
|
@ -277,6 +324,24 @@ export default class DhCharacter extends BaseDataActor {
|
|||
return this.parent.items.find(x => x.type === 'armor' && x.system.equipped);
|
||||
}
|
||||
|
||||
get activeBeastform() {
|
||||
return this.parent.effects.find(x => x.type === 'beastform');
|
||||
}
|
||||
|
||||
get usedUnarmed() {
|
||||
const primaryWeaponEquipped = this.primaryWeapon?.system?.equipped;
|
||||
const secondaryWeaponEquipped = this.secondaryWeapon?.system?.equipped;
|
||||
return !primaryWeaponEquipped && !secondaryWeaponEquipped
|
||||
? {
|
||||
...this.attack,
|
||||
id: this.attack.id,
|
||||
name: this.activeBeastform ? 'DAGGERHEART.ITEMS.Beastform.attackName' : this.attack.name,
|
||||
img: this.activeBeastform ? 'icons/creatures/claws/claw-straight-brown.webp' : this.attack.img,
|
||||
actor: this.parent
|
||||
}
|
||||
: null;
|
||||
}
|
||||
|
||||
get sheetLists() {
|
||||
const ancestryFeatures = [],
|
||||
communityFeatures = [],
|
||||
|
|
@ -457,9 +522,6 @@ export default class DhCharacter extends BaseDataActor {
|
|||
const data = super.getRollData();
|
||||
return {
|
||||
...data,
|
||||
...this.resources.tokens,
|
||||
...this.resources.dice,
|
||||
...this.bonuses,
|
||||
tier: this.tier,
|
||||
level: this.levelData.level.current
|
||||
};
|
||||
|
|
|
|||
|
|
@ -18,10 +18,11 @@ export default class DhEnvironment extends BaseDataActor {
|
|||
const fields = foundry.data.fields;
|
||||
return {
|
||||
...super.defineSchema(),
|
||||
tier: new fields.StringField({
|
||||
tier: new fields.NumberField({
|
||||
required: true,
|
||||
integer: true,
|
||||
choices: CONFIG.DH.GENERAL.tiers,
|
||||
initial: CONFIG.DH.GENERAL.tiers.tier1.id
|
||||
initial: CONFIG.DH.GENERAL.tiers[1].id
|
||||
}),
|
||||
type: new fields.StringField({ choices: CONFIG.DH.ACTOR.environmentTypes }),
|
||||
impulses: new fields.StringField(),
|
||||
|
|
|
|||
|
|
@ -19,10 +19,16 @@ export default class DHBeastform extends BaseDataItem {
|
|||
const fields = foundry.data.fields;
|
||||
return {
|
||||
...super.defineSchema(),
|
||||
tier: new fields.StringField({
|
||||
beastformType: new fields.StringField({
|
||||
required: true,
|
||||
choices: CONFIG.DH.ITEM.beastformTypes,
|
||||
initial: CONFIG.DH.ITEM.beastformTypes.normal.id
|
||||
}),
|
||||
tier: new fields.NumberField({
|
||||
required: true,
|
||||
integer: true,
|
||||
choices: CONFIG.DH.GENERAL.tiers,
|
||||
initial: CONFIG.DH.GENERAL.tiers.tier1.id
|
||||
initial: CONFIG.DH.GENERAL.tiers[1].id
|
||||
}),
|
||||
tokenImg: new fields.FilePathField({
|
||||
initial: 'icons/svg/mystery-man.svg',
|
||||
|
|
@ -38,9 +44,40 @@ export default class DHBeastform extends BaseDataItem {
|
|||
height: new fields.NumberField({ integer: true, min: 1, initial: null, nullable: true }),
|
||||
width: new fields.NumberField({ integer: true, min: 1, initial: null, nullable: true })
|
||||
}),
|
||||
mainTrait: new fields.StringField({
|
||||
required: true,
|
||||
choices: CONFIG.DH.ACTOR.abilities,
|
||||
initial: CONFIG.DH.ACTOR.abilities.agility.id
|
||||
}),
|
||||
examples: new fields.StringField(),
|
||||
advantageOn: new fields.StringField(),
|
||||
features: new ForeignDocumentUUIDArrayField({ type: 'Item' })
|
||||
advantageOn: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
value: new fields.StringField()
|
||||
})
|
||||
),
|
||||
features: new ForeignDocumentUUIDArrayField({ type: 'Item' }),
|
||||
evolved: new fields.SchemaField({
|
||||
maximumTier: new fields.NumberField({
|
||||
integer: true,
|
||||
choices: CONFIG.DH.GENERAL.tiers
|
||||
}),
|
||||
mainTraitBonus: new fields.NumberField({
|
||||
required: true,
|
||||
integer: true,
|
||||
min: 0,
|
||||
initial: 0
|
||||
})
|
||||
}),
|
||||
hybrid: new fields.SchemaField({
|
||||
maximumTier: new fields.NumberField({
|
||||
integer: true,
|
||||
choices: CONFIG.DH.GENERAL.tiers,
|
||||
label: 'DAGGERHEART.ITEMS.Beastform.FIELDS.evolved.maximumTier.label'
|
||||
}),
|
||||
beastformOptions: new fields.NumberField({ required: true, integer: true, initial: 2, min: 2 }),
|
||||
advantages: new fields.NumberField({ required: true, integer: true, initial: 2, min: 2 }),
|
||||
features: new fields.NumberField({ required: true, integer: true, initial: 2, min: 2 })
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -69,7 +106,16 @@ export default class DHBeastform extends BaseDataItem {
|
|||
|
||||
const beastformEffect = this.parent.effects.find(x => x.type === 'beastform');
|
||||
await beastformEffect.updateSource({
|
||||
changes: [...beastformEffect.changes, { key: 'system.advantageSources', mode: 2, value: this.advantageOn }],
|
||||
changes: [
|
||||
...beastformEffect.changes,
|
||||
{
|
||||
key: 'system.advantageSources',
|
||||
mode: 2,
|
||||
value: Object.values(this.advantageOn)
|
||||
.map(x => x.value)
|
||||
.join(', ')
|
||||
}
|
||||
],
|
||||
system: {
|
||||
characterTokenData: {
|
||||
tokenImg: this.parent.parent.prototypeToken.texture.src,
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export default class DHClass extends BaseDataItem {
|
|||
integer: true,
|
||||
min: 1,
|
||||
initial: 5,
|
||||
label: 'DAGGERHEART.GENERAL.hitPoints.plural'
|
||||
label: 'DAGGERHEART.GENERAL.HitPoints.plural'
|
||||
}),
|
||||
evasion: new fields.NumberField({ initial: 0, integer: true, label: 'DAGGERHEART.GENERAL.evasion' }),
|
||||
features: new ForeignDocumentUUIDArrayField({ type: 'Item' }),
|
||||
|
|
|
|||
|
|
@ -147,8 +147,7 @@ export default class D20Roll extends DHRoll {
|
|||
const difficulty = config.roll.difficulty ?? target.difficulty ?? target.evasion;
|
||||
target.hit = this.isCritical || roll.total >= difficulty;
|
||||
});
|
||||
} else if (config.roll.difficulty)
|
||||
data.success = roll.isCritical || roll.total >= config.roll.difficulty;
|
||||
} else if (config.roll.difficulty) data.success = roll.isCritical || roll.total >= config.roll.difficulty;
|
||||
data.advantage = {
|
||||
type: config.roll.advantage,
|
||||
dice: roll.dAdvantage?.denomination,
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ export default class DamageRoll extends DHRoll {
|
|||
static DefaultDialog = DamageDialog;
|
||||
|
||||
static async buildEvaluate(roll, config = {}, message = {}) {
|
||||
if ( config.evaluate !== false ) {
|
||||
for ( const roll of config.roll ) await roll.roll.evaluate();
|
||||
if (config.evaluate !== false) {
|
||||
for (const roll of config.roll) await roll.roll.evaluate();
|
||||
}
|
||||
roll._evaluated = true;
|
||||
const parts = config.roll.map(r => this.postEvaluate(r));
|
||||
|
|
@ -27,7 +27,7 @@ export default class DamageRoll extends DHRoll {
|
|||
roll: roll.roll,
|
||||
type: config.type,
|
||||
modifierTotal: this.calculateTotalModifiers(roll.roll)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static async buildPost(roll, config, message) {
|
||||
|
|
@ -46,33 +46,33 @@ export default class DamageRoll extends DHRoll {
|
|||
resource.total += r.total;
|
||||
resource.parts.push(r);
|
||||
unified[r.applyTo] = resource;
|
||||
})
|
||||
});
|
||||
return unified;
|
||||
}
|
||||
|
||||
static formatGlobal(rolls) {
|
||||
let formula, total;
|
||||
const applyTo = new Set(rolls.flatMap(r => r.applyTo));
|
||||
if(applyTo.size > 1) {
|
||||
if (applyTo.size > 1) {
|
||||
const data = {};
|
||||
rolls.forEach(r => {
|
||||
if(data[r.applyTo]) {
|
||||
data[r.applyTo].formula += ` + ${r.formula}` ;
|
||||
data[r.applyTo].total += r.total ;
|
||||
if (data[r.applyTo]) {
|
||||
data[r.applyTo].formula += ` + ${r.formula}`;
|
||||
data[r.applyTo].total += r.total;
|
||||
} else {
|
||||
data[r.applyTo] = {
|
||||
formula: r.formula,
|
||||
total: r.total
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
formula = Object.entries(data).reduce((a, [k,v]) => a + ` ${k}: ${v.formula}`, '');
|
||||
total = Object.entries(data).reduce((a, [k,v]) => a + ` ${k}: ${v.total}`, '');
|
||||
formula = Object.entries(data).reduce((a, [k, v]) => a + ` ${k}: ${v.formula}`, '');
|
||||
total = Object.entries(data).reduce((a, [k, v]) => a + ` ${k}: ${v.total}`, '');
|
||||
} else {
|
||||
formula = rolls.map(r => r.formula).join(' + ');
|
||||
total = rolls.reduce((a,c) => a + c.total, 0)
|
||||
total = rolls.reduce((a, c) => a + c.total, 0);
|
||||
}
|
||||
return {formula, total}
|
||||
return { formula, total };
|
||||
}
|
||||
|
||||
applyBaseBonus(part) {
|
||||
|
|
@ -94,17 +94,17 @@ export default class DamageRoll extends DHRoll {
|
|||
}
|
||||
|
||||
constructFormula(config) {
|
||||
this.options.roll.forEach( part => {
|
||||
part.roll = new Roll(part.formula);
|
||||
this.constructFormulaPart(config, part)
|
||||
})
|
||||
this.options.roll.forEach(part => {
|
||||
part.roll = new Roll(Roll.replaceFormulaData(part.formula, config.data));
|
||||
this.constructFormulaPart(config, part);
|
||||
});
|
||||
return this.options.roll;
|
||||
}
|
||||
|
||||
constructFormulaPart(config, part) {
|
||||
part.roll.terms = Roll.parse(part.roll.formula, config.data);
|
||||
|
||||
if(part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) {
|
||||
if (part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) {
|
||||
part.modifiers = this.applyBaseBonus(part);
|
||||
this.addModifiers(part);
|
||||
part.modifiers?.forEach(m => {
|
||||
|
|
|
|||
|
|
@ -57,13 +57,14 @@ export default class DHRoll extends Roll {
|
|||
|
||||
// Create Chat Message
|
||||
if (config.source?.message) {
|
||||
if(Object.values(config.roll)?.length) {
|
||||
const pool = foundry.dice.terms.PoolTerm.fromRolls(Object.values(config.roll).flatMap(r => r.parts.map(p => p.roll)));
|
||||
if (Object.values(config.roll)?.length) {
|
||||
const pool = foundry.dice.terms.PoolTerm.fromRolls(
|
||||
Object.values(config.roll).flatMap(r => r.parts.map(p => p.roll))
|
||||
);
|
||||
roll = Roll.fromTerms([pool]);
|
||||
}
|
||||
if (game.modules.get('dice-so-nice')?.active) await game.dice3d.showForRoll(roll, game.user, true);
|
||||
} else
|
||||
config.message = await this.toMessage(roll, config);
|
||||
} else config.message = await this.toMessage(roll, config);
|
||||
}
|
||||
|
||||
static postEvaluate(roll, config = {}) {
|
||||
|
|
@ -76,7 +77,7 @@ export default class DHRoll extends Roll {
|
|||
formula: d.formula,
|
||||
results: d.results
|
||||
}))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static async toMessage(roll, config) {
|
||||
|
|
|
|||
|
|
@ -63,19 +63,17 @@ export default class DualityRoll extends D20Roll {
|
|||
}
|
||||
|
||||
setRallyChoices() {
|
||||
return this.data?.parent?.effects.reduce((a,c) => {
|
||||
return this.data?.parent?.effects.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: change.value });
|
||||
return a;
|
||||
}, []);
|
||||
}
|
||||
|
||||
get dRally() {
|
||||
if(!this.rallyFaces) return null;
|
||||
if(this.hasDisadvantage || this.hasAdvantage)
|
||||
return this.dice[3];
|
||||
else
|
||||
return this.dice[2];
|
||||
if (!this.rallyFaces) return null;
|
||||
if (this.hasDisadvantage || this.hasAdvantage) return this.dice[3];
|
||||
else return this.dice[2];
|
||||
}
|
||||
|
||||
get rallyFaces() {
|
||||
|
|
@ -129,13 +127,13 @@ export default class DualityRoll extends D20Roll {
|
|||
if (this.hasAdvantage || this.hasDisadvantage) {
|
||||
const dieFaces = this.advantageFaces,
|
||||
advDie = new foundry.dice.terms.Die({ faces: dieFaces, number: this.advantageNumber });
|
||||
if(this.advantageNumber > 1) advDie.modifiers = ['kh'];
|
||||
if (this.advantageNumber > 1) advDie.modifiers = ['kh'];
|
||||
this.terms.push(
|
||||
new foundry.dice.terms.OperatorTerm({ operator: this.hasDisadvantage ? '-' : '+' }),
|
||||
advDie
|
||||
);
|
||||
}
|
||||
if(this.rallyFaces)
|
||||
if (this.rallyFaces)
|
||||
this.terms.push(
|
||||
new foundry.dice.terms.OperatorTerm({ operator: this.hasDisadvantage ? '-' : '+' }),
|
||||
new foundry.dice.terms.Die({ faces: this.rallyFaces })
|
||||
|
|
@ -181,7 +179,7 @@ export default class DualityRoll extends D20Roll {
|
|||
label: roll.totalLabel
|
||||
};
|
||||
|
||||
if(roll._rallyIndex && roll.data?.parent)
|
||||
if (roll._rallyIndex && roll.data?.parent)
|
||||
roll.data.parent.deleteEmbeddedDocuments('ActiveEffect', [roll._rallyIndex]);
|
||||
|
||||
setDiceSoNiceForDualityRoll(roll, data.advantage.type);
|
||||
|
|
|
|||
|
|
@ -370,6 +370,7 @@ export default class DhpActor extends Actor {
|
|||
|
||||
getRollData() {
|
||||
const rollData = super.getRollData();
|
||||
rollData.system = this.system.getRollData();
|
||||
rollData.prof = this.system.proficiency ?? 1;
|
||||
rollData.cast = this.system.spellcastModifier ?? 1;
|
||||
return rollData;
|
||||
|
|
@ -403,24 +404,28 @@ export default class DhpActor extends Actor {
|
|||
|
||||
Object.entries(damages).forEach(([key, damage]) => {
|
||||
damage.parts.forEach(part => {
|
||||
if(part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id)
|
||||
if (part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id)
|
||||
part.total = this.calculateDamage(part.total, part.damageTypes);
|
||||
const update = updates.find(u => u.key === key);
|
||||
if(update) {
|
||||
if (update) {
|
||||
update.value += part.total;
|
||||
update.damageTypes.add(...new Set(part.damageTypes));
|
||||
} else updates.push({ value: part.total, key, damageTypes: new Set(part.damageTypes) })
|
||||
})
|
||||
} else updates.push({ value: part.total, key, damageTypes: new Set(part.damageTypes) });
|
||||
});
|
||||
});
|
||||
|
||||
if (Hooks.call(`${CONFIG.DH.id}.postCalculateDamage`, this, damages) === false) return null;
|
||||
|
||||
if(!updates.length) return;
|
||||
if (!updates.length) return;
|
||||
|
||||
const hpDamage = updates.find(u => u.key === CONFIG.DH.GENERAL.healingTypes.hitPoints.id);
|
||||
if(hpDamage) {
|
||||
if (hpDamage) {
|
||||
hpDamage.value = this.convertDamageToThreshold(hpDamage.value);
|
||||
if (this.type === 'character' && this.system.armor && this.#canReduceDamage(hpDamage.value, hpDamage.damageTypes)) {
|
||||
if (
|
||||
this.type === 'character' &&
|
||||
this.system.armor &&
|
||||
this.#canReduceDamage(hpDamage.value, hpDamage.damageTypes)
|
||||
) {
|
||||
const armorStackResult = await this.owner.query('armorStack', {
|
||||
actorId: this.uuid,
|
||||
damage: hpDamage.value,
|
||||
|
|
@ -437,8 +442,10 @@ export default class DhpActor extends Actor {
|
|||
}
|
||||
}
|
||||
|
||||
updates.forEach( u =>
|
||||
u.value = u.key === 'fear' || this.system?.resources?.[u.key]?.isReversed === false ? u.value * -1 : u.value
|
||||
updates.forEach(
|
||||
u =>
|
||||
(u.value =
|
||||
u.key === 'fear' || this.system?.resources?.[u.key]?.isReversed === false ? u.value * -1 : u.value)
|
||||
);
|
||||
|
||||
await this.modifyResource(updates);
|
||||
|
|
@ -447,7 +454,6 @@ export default class DhpActor extends Actor {
|
|||
}
|
||||
|
||||
calculateDamage(baseDamage, type) {
|
||||
|
||||
if (this.canResist(type, 'immunity')) return 0;
|
||||
if (this.canResist(type, 'resistance')) baseDamage = Math.ceil(baseDamage / 2);
|
||||
|
||||
|
|
@ -472,12 +478,12 @@ export default class DhpActor extends Actor {
|
|||
}
|
||||
|
||||
async takeHealing(resources) {
|
||||
const updates = Object.entries(resources).map(([key, value]) => (
|
||||
{
|
||||
const updates = Object.entries(resources).map(([key, value]) => ({
|
||||
key: key,
|
||||
value: !(key === 'fear' || this.system?.resources?.[key]?.isReversed === false) ? value.total * -1 : value.total
|
||||
}
|
||||
))
|
||||
value: !(key === 'fear' || this.system?.resources?.[key]?.isReversed === false)
|
||||
? value.total * -1
|
||||
: value.total
|
||||
}));
|
||||
await this.modifyResource(updates);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,21 @@ export default class DHToken extends TokenDocument {
|
|||
});
|
||||
bars.sort((a, b) => a.label.compare(b.label));
|
||||
|
||||
const invalidAttributes = ['gold', 'levelData', 'actions'];
|
||||
const invalidAttributes = [
|
||||
'gold',
|
||||
'levelData',
|
||||
'actions',
|
||||
'biography',
|
||||
'class',
|
||||
'multiclass',
|
||||
'companion',
|
||||
'notes',
|
||||
'partner',
|
||||
'description',
|
||||
'impulses',
|
||||
'tier',
|
||||
'type'
|
||||
];
|
||||
const values = attributes.value.reduce((acc, v) => {
|
||||
const a = v.join('.');
|
||||
if (invalidAttributes.some(x => a.startsWith(x))) return acc;
|
||||
|
|
@ -38,9 +52,11 @@ export default class DHToken extends TokenDocument {
|
|||
for (const [name, field] of Object.entries(schema.fields)) {
|
||||
const p = _path.concat([name]);
|
||||
if (field instanceof foundry.data.fields.NumberField) attributes.value.push(p);
|
||||
if (field instanceof foundry.data.fields.StringField) attributes.value.push(p);
|
||||
if (field instanceof foundry.data.fields.ArrayField) attributes.value.push(p);
|
||||
const isSchema = field instanceof foundry.data.fields.SchemaField;
|
||||
const isModel = field instanceof foundry.data.fields.EmbeddedDataField;
|
||||
|
||||
if (isSchema || isModel) {
|
||||
const schema = isModel ? field.model.schema : field;
|
||||
const isBar = schema.has && schema.has('value') && schema.has('max');
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
export default class DhTooltipManager extends foundry.helpers.interaction.TooltipManager {
|
||||
async activate(element, options = {}) {
|
||||
const { TextEditor } = foundry.applications.ux;
|
||||
|
||||
let html = options.html;
|
||||
if (element.dataset.tooltip?.startsWith('#item#')) {
|
||||
const splitValues = element.dataset.tooltip.slice(6).split('#action#');
|
||||
|
|
@ -10,10 +12,16 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
|
|||
const item = actionId ? baseItem.system.actions.find(x => x.id === actionId) : baseItem;
|
||||
if (item) {
|
||||
const type = actionId ? 'action' : item.type;
|
||||
const description = await TextEditor.enrichHTML(item.system.description);
|
||||
for (let feature of item.system.features) {
|
||||
feature.system.enrichedDescription = await TextEditor.enrichHTML(feature.system.description);
|
||||
}
|
||||
|
||||
html = await foundry.applications.handlebars.renderTemplate(
|
||||
`systems/daggerheart/templates/ui/tooltip/${type}.hbs`,
|
||||
{
|
||||
item: item,
|
||||
description: description,
|
||||
config: CONFIG.DH
|
||||
}
|
||||
);
|
||||
|
|
@ -22,6 +30,26 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
|
|||
options.direction = this._determineItemTooltipDirection(element);
|
||||
}
|
||||
} else {
|
||||
const attack = element.dataset.tooltip?.startsWith('#attack#');
|
||||
if (attack) {
|
||||
const actorUuid = element.dataset.tooltip.slice(8);
|
||||
const actor = await foundry.utils.fromUuid(actorUuid);
|
||||
const attack = actor.system.attack;
|
||||
|
||||
const description = await TextEditor.enrichHTML(attack.description);
|
||||
html = await foundry.applications.handlebars.renderTemplate(
|
||||
`systems/daggerheart/templates/ui/tooltip/attack.hbs`,
|
||||
{
|
||||
attack: attack,
|
||||
description: description,
|
||||
parent: actor,
|
||||
config: CONFIG.DH
|
||||
}
|
||||
);
|
||||
|
||||
this.tooltip.innerHTML = html;
|
||||
}
|
||||
|
||||
const shortRest = element.dataset.tooltip?.startsWith('#shortRest#');
|
||||
const longRest = element.dataset.tooltip?.startsWith('#longRest#');
|
||||
if (shortRest || longRest) {
|
||||
|
|
@ -29,11 +57,14 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
|
|||
const downtimeOptions = shortRest
|
||||
? CONFIG.DH.GENERAL.defaultRestOptions.shortRest()
|
||||
: CONFIG.DH.GENERAL.defaultRestOptions.longRest();
|
||||
|
||||
const move = downtimeOptions[key];
|
||||
const description = await TextEditor.enrichHTML(move.description);
|
||||
html = await foundry.applications.handlebars.renderTemplate(
|
||||
`systems/daggerheart/templates/ui/tooltip/downtime.hbs`,
|
||||
{
|
||||
move: move
|
||||
move: move,
|
||||
description: description
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
|||
80
module/enrichers/DamageEnricher.mjs
Normal file
80
module/enrichers/DamageEnricher.mjs
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
export default function DhDamageEnricher(match, _options) {
|
||||
const parts = match[1].split('|').map(x => x.trim());
|
||||
|
||||
let value = null,
|
||||
type = null;
|
||||
|
||||
parts.forEach(part => {
|
||||
const split = part.split(':').map(x => x.toLowerCase().trim());
|
||||
if (split.length === 2) {
|
||||
switch (split[0]) {
|
||||
case 'value':
|
||||
value = split[1];
|
||||
break;
|
||||
case 'type':
|
||||
type = split[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!value || !value) return match[0];
|
||||
|
||||
return getDamageMessage(value, type, match[0]);
|
||||
}
|
||||
|
||||
function getDamageMessage(damage, type, defaultElement) {
|
||||
const typeIcons = type
|
||||
.replace('[', '')
|
||||
.replace(']', '')
|
||||
.split(',')
|
||||
.map(x => x.trim())
|
||||
.map(x => {
|
||||
return CONFIG.DH.GENERAL.damageTypes[x]?.icon ?? null;
|
||||
})
|
||||
.filter(x => x);
|
||||
|
||||
if (!typeIcons.length) return defaultElement;
|
||||
|
||||
const iconNodes = typeIcons.map(x => `<i class="fa-solid ${x}"></i>`).join('');
|
||||
|
||||
const dualityElement = document.createElement('span');
|
||||
dualityElement.innerHTML = `
|
||||
<button class="enriched-damage-button"
|
||||
data-value="${damage}"
|
||||
data-type="${type}"
|
||||
data-tooltip="${game.i18n.localize('DAGGERHEART.GENERAL.damage')}"
|
||||
>
|
||||
${damage}
|
||||
${iconNodes}
|
||||
</button>
|
||||
`;
|
||||
|
||||
return dualityElement;
|
||||
}
|
||||
|
||||
export const renderDamageButton = async event => {
|
||||
const button = event.currentTarget,
|
||||
value = button.dataset.value,
|
||||
type = button.dataset.type
|
||||
.replace('[', '')
|
||||
.replace(']', '')
|
||||
.split(',')
|
||||
.map(x => x.trim());
|
||||
|
||||
const config = {
|
||||
event: event,
|
||||
title: game.i18n.localize('Damage Roll'),
|
||||
data: { bonuses: [] },
|
||||
source: {},
|
||||
roll: [
|
||||
{
|
||||
formula: value,
|
||||
applyTo: CONFIG.DH.GENERAL.healingTypes.hitPoints.id,
|
||||
type: type
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
CONFIG.Dice.daggerheart.DamageRoll.build(config);
|
||||
};
|
||||
|
|
@ -8,7 +8,7 @@ export default function DhDualityRollEnricher(match, _options) {
|
|||
return getDualityMessage(roll);
|
||||
}
|
||||
|
||||
export function getDualityMessage(roll) {
|
||||
function getDualityMessage(roll) {
|
||||
const traitLabel =
|
||||
roll.trait && abilities[roll.trait]
|
||||
? game.i18n.format('DAGGERHEART.GENERAL.check', {
|
||||
|
|
|
|||
19
module/enrichers/EffectEnricher.mjs
Normal file
19
module/enrichers/EffectEnricher.mjs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
export default async function DhEffectEnricher(match, _options) {
|
||||
const effect = await foundry.utils.fromUuid(match[1]);
|
||||
if (!effect) return match[0];
|
||||
|
||||
const dualityElement = document.createElement('span');
|
||||
dualityElement.innerHTML = `
|
||||
<a class="flexrow enriched-effect"
|
||||
data-link
|
||||
draggable="true"
|
||||
data-uuid="${match[1]}"
|
||||
data-tooltip="${game.i18n.localize('DAGGERHEART.UI.Tooltip.dragApplyEffect')}"
|
||||
>
|
||||
<img src="icons/svg/aura.svg" style="width: 24px;" />
|
||||
<span>${effect.name}</span>
|
||||
</a>
|
||||
`;
|
||||
|
||||
return dualityElement;
|
||||
}
|
||||
|
|
@ -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));
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
@ -326,12 +255,6 @@ 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
|
||||
);
|
||||
[...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)
|
||||
);
|
||||
export const setsEqual = (a, b) => a.size === b.size && [...a].every(value => b.has(value));
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
]);
|
||||
|
|
|
|||
160
src/packs/beastforms/beastform_Agile_Scout_6tr99y6wHaJJYy3J.json
Normal file
160
src/packs/beastforms/beastform_Agile_Scout_6tr99y6wHaJJYy3J.json
Normal file
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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": "<p>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:</p><ul><li><p>- A +6 bonus to damage rolls</p></li><li><p>- A +1 bonus to the trait used by this form</p></li><li><p>- A +2 bonus to evasion</p></li></ul>",
|
||||
"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!mERwC7aMDoIKfZTf.cKAoI5JqYOtGBscd"
|
||||
},
|
||||
{
|
||||
"type": "beastform",
|
||||
"name": "Beastform Transformation",
|
||||
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
|
||||
"_id": "LltLvTqjhk9RseV8",
|
||||
"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": 1752976989252,
|
||||
"modifiedTime": 1752976989252,
|
||||
"lastModifiedBy": "k0gmQFlvrPvlTtbh"
|
||||
},
|
||||
"_key": "!items.effects!mERwC7aMDoIKfZTf.LltLvTqjhk9RseV8"
|
||||
}
|
||||
],
|
||||
"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": 1752976989245,
|
||||
"modifiedTime": 1752976989245,
|
||||
"lastModifiedBy": "k0gmQFlvrPvlTtbh"
|
||||
},
|
||||
"_id": "mERwC7aMDoIKfZTf",
|
||||
"sort": 0,
|
||||
"_key": "!items!mERwC7aMDoIKfZTf"
|
||||
}
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
{
|
||||
"name": "Mighty Strider",
|
||||
"type": "beastform",
|
||||
"img": "icons/creatures/mammals/bull-horned-blue.webp",
|
||||
"system": {
|
||||
"beastformType": "normal",
|
||||
"tier": 2,
|
||||
"tokenImg": "icons/creatures/mammals/bull-horned-blue.webp",
|
||||
"tokenRingImg": "icons/svg/mystery-man.svg",
|
||||
"tokenSize": {
|
||||
"height": null,
|
||||
"width": null
|
||||
},
|
||||
"mainTrait": "agility",
|
||||
"advantageOn": {
|
||||
"Xxr01TwSerOS8qsd": {
|
||||
"value": "leap"
|
||||
},
|
||||
"cASut9AUij2Uf4zm": {
|
||||
"value": "navigate"
|
||||
},
|
||||
"XJie4FhCSwUCg9uN": {
|
||||
"value": "sprint"
|
||||
}
|
||||
},
|
||||
"features": [
|
||||
"Compendium.daggerheart.beastforms.Item.YSolAjtv6Sfnai98",
|
||||
"Compendium.daggerheart.beastforms.Item.P6tWFIZzXWyekw6r"
|
||||
],
|
||||
"evolved": {
|
||||
"mainTraitBonus": 0
|
||||
},
|
||||
"hybrid": {
|
||||
"beastformOptions": 2,
|
||||
"advantages": 2,
|
||||
"features": 2
|
||||
},
|
||||
"examples": "Camel, Horse, Zebra, etc."
|
||||
},
|
||||
"effects": [
|
||||
{
|
||||
"type": "beastform",
|
||||
"name": "Beastform Transformation",
|
||||
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
|
||||
"_id": "1KdhXARm6rg2fg22",
|
||||
"system": {
|
||||
"characterTokenData": {
|
||||
"tokenImg": null,
|
||||
"tokenRingImg": "icons/svg/mystery-man.svg",
|
||||
"tokenSize": {}
|
||||
},
|
||||
"advantageOn": [],
|
||||
"featureIds": [],
|
||||
"effectIds": []
|
||||
},
|
||||
"changes": [
|
||||
{
|
||||
"key": "system.rules.attack.damage.value",
|
||||
"mode": 5,
|
||||
"value": "d8 + 1",
|
||||
"priority": null
|
||||
},
|
||||
{
|
||||
"key": "system.rules.attack.trait",
|
||||
"mode": 5,
|
||||
"value": "agility",
|
||||
"priority": null
|
||||
},
|
||||
{
|
||||
"key": "system.rules.attack.range",
|
||||
"mode": 5,
|
||||
"value": "melee",
|
||||
"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!LF68kGAcOTZQ81GB.1KdhXARm6rg2fg22"
|
||||
},
|
||||
{
|
||||
"type": "beastform",
|
||||
"name": "Beastform Transformation",
|
||||
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
|
||||
"_id": "ngEREmS8hzNyshak",
|
||||
"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": 1752976987358,
|
||||
"modifiedTime": 1752976987358,
|
||||
"lastModifiedBy": "k0gmQFlvrPvlTtbh"
|
||||
},
|
||||
"_key": "!items.effects!LF68kGAcOTZQ81GB.ngEREmS8hzNyshak"
|
||||
}
|
||||
],
|
||||
"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": 1752976987354,
|
||||
"modifiedTime": 1752976987362,
|
||||
"lastModifiedBy": "k0gmQFlvrPvlTtbh"
|
||||
},
|
||||
"_id": "LF68kGAcOTZQ81GB",
|
||||
"sort": 200000,
|
||||
"_key": "!items!LF68kGAcOTZQ81GB"
|
||||
}
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
{
|
||||
"name": "Mythic Hybrid",
|
||||
"type": "beastform",
|
||||
"img": "icons/creatures/magical/humanoid-silhouette-glowing-pink.webp",
|
||||
"system": {
|
||||
"beastformType": "hybrid",
|
||||
"tier": 4,
|
||||
"tokenImg": "icons/creatures/magical/humanoid-silhouette-glowing-pink.webp",
|
||||
"tokenRingImg": "icons/svg/mystery-man.svg",
|
||||
"tokenSize": {
|
||||
"height": null,
|
||||
"width": null
|
||||
},
|
||||
"mainTrait": "strength",
|
||||
"advantageOn": {},
|
||||
"features": [],
|
||||
"evolved": {
|
||||
"mainTraitBonus": 0
|
||||
},
|
||||
"hybrid": {
|
||||
"beastformOptions": 3,
|
||||
"advantages": 5,
|
||||
"features": 3,
|
||||
"maximumTier": 3
|
||||
},
|
||||
"examples": ""
|
||||
},
|
||||
"effects": [
|
||||
{
|
||||
"type": "beastform",
|
||||
"name": "Beastform Transformation",
|
||||
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
|
||||
"_id": "dvqS0a0ur8xM2swY",
|
||||
"system": {
|
||||
"characterTokenData": {
|
||||
"tokenImg": null,
|
||||
"tokenRingImg": "icons/svg/mystery-man.svg",
|
||||
"tokenSize": {}
|
||||
},
|
||||
"advantageOn": [],
|
||||
"featureIds": [],
|
||||
"effectIds": []
|
||||
},
|
||||
"changes": [
|
||||
{
|
||||
"key": "system.traits.strength.value",
|
||||
"mode": 2,
|
||||
"value": "3",
|
||||
"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": "<p>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.</p>",
|
||||
"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"
|
||||
}
|
||||
34
src/packs/beastforms/feature_Agile_sef9mwD2eRLZ64oV.json
Normal file
34
src/packs/beastforms/feature_Agile_sef9mwD2eRLZ64oV.json
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"type": "feature",
|
||||
"name": "Agile",
|
||||
"img": "icons/skills/movement/arrow-upward-blue.webp",
|
||||
"system": {
|
||||
"description": "<p>Your movement is silent, and you can spend a Hope to move up to Far range without rolling.</p>",
|
||||
"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"
|
||||
}
|
||||
34
src/packs/beastforms/feature_Carrier_YSolAjtv6Sfnai98.json
Normal file
34
src/packs/beastforms/feature_Carrier_YSolAjtv6Sfnai98.json
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"type": "feature",
|
||||
"name": "Carrier",
|
||||
"img": "icons/creatures/abilities/bull-head-horns-glowing.webp",
|
||||
"system": {
|
||||
"description": "<p>You can carry up to two willing allies with you when you move.</p>",
|
||||
"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"
|
||||
}
|
||||
34
src/packs/beastforms/feature_Companion_0tlnxIxlIw2hl1UE.json
Normal file
34
src/packs/beastforms/feature_Companion_0tlnxIxlIw2hl1UE.json
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"type": "feature",
|
||||
"name": "Companion",
|
||||
"img": "icons/magic/life/heart-hand-gold-green-light.webp",
|
||||
"system": {
|
||||
"description": "<p>When you Help an Ally, you can roll a d8 as your advantage die.</p>",
|
||||
"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"
|
||||
}
|
||||
34
src/packs/beastforms/feature_Evolved_MG21w4u5wXSGZ5WB.json
Normal file
34
src/packs/beastforms/feature_Evolved_MG21w4u5wXSGZ5WB.json
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"type": "feature",
|
||||
"name": "Evolved",
|
||||
"img": "icons/creatures/abilities/dragon-breath-purple.webp",
|
||||
"system": {
|
||||
"description": "<p>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:</p><ul><li>A +6 bonus to damage rolls</li><li>A +1 bonus to the trait used by this form</li><li>A +2 bonus to Evasion</li></ul>",
|
||||
"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": 1752976852417,
|
||||
"modifiedTime": 1752976908700,
|
||||
"lastModifiedBy": "k0gmQFlvrPvlTtbh"
|
||||
},
|
||||
"_id": "MG21w4u5wXSGZ5WB",
|
||||
"sort": 50000,
|
||||
"_key": "!items!MG21w4u5wXSGZ5WB"
|
||||
}
|
||||
34
src/packs/beastforms/feature_Fragile_9ryNrYWjNtOT6DXN.json
Normal file
34
src/packs/beastforms/feature_Fragile_9ryNrYWjNtOT6DXN.json
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"type": "feature",
|
||||
"name": "Fragile",
|
||||
"img": "icons/magic/life/heart-broken-red.webp",
|
||||
"system": {
|
||||
"description": "<p>When you take Major or greater damage, you drop out of Beastform.</p>",
|
||||
"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"
|
||||
}
|
||||
34
src/packs/beastforms/feature_Trample_P6tWFIZzXWyekw6r.json
Normal file
34
src/packs/beastforms/feature_Trample_P6tWFIZzXWyekw6r.json
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"type": "feature",
|
||||
"name": "Trample",
|
||||
"img": "icons/creatures/mammals/ox-bull-horned-glowing-orange.webp",
|
||||
"system": {
|
||||
"description": "<p><strong>Mark a Stress</strong> 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.</p>",
|
||||
"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"
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
9
styles/less/sheets/items/beastform.less
Normal file
9
styles/less/sheets/items/beastform.less
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
.application.sheet.daggerheart.dh-style.beastform {
|
||||
.settings.tab {
|
||||
.advantage-on-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
11
system.json
11
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",
|
||||
|
|
|
|||
73
templates/dialogs/beastform/advanced.hbs
Normal file
73
templates/dialogs/beastform/advanced.hbs
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
<div class="advanced-container">
|
||||
{{#if (eq selected.system.beastformType 'evolved')}}
|
||||
<h2>{{localize "DAGGERHEART.ITEMS.Beastform.evolve"}}</h2>
|
||||
|
||||
<div class="form-features">
|
||||
{{#if selectedBeastformEffect}}
|
||||
<div class="form-feature" data-tooltip="{{concat "#item#" selectedBeastformEffect.uuid}}">
|
||||
<h4>{{localize "DAGGERHEART.ITEMS.Beastform.evolvedFeatureTitle"}}</h4>
|
||||
<div>{{{selectedBeastformEffect.description}}}</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class="advanced-form-container evolved">
|
||||
{{#if evolved.form}}
|
||||
<div class="beastform-title">{{concat (localize "DAGGERHEART.CONFIG.BeastformType.evolved") " " evolved.form.name}}</div>
|
||||
<img src="{{evolved.form.img}}" />
|
||||
{{else}}
|
||||
<div class="empty-form">
|
||||
<i class="fa-solid fa-plus"></i>
|
||||
<label>{{localize "DAGGERHEART.ITEMS.Beastform.evolvedDrag"}}</label>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if (eq selected.system.beastformType 'hybrid')}}
|
||||
<h2>{{localize "DAGGERHEART.ITEMS.Beastform.hybridize"}}</h2>
|
||||
|
||||
<div class="form-features">
|
||||
{{#if selectedBeastformEffect}}
|
||||
<div class="form-feature" data-tooltip="{{concat "#item#" selectedBeastformEffect.uuid}}">
|
||||
<h4>{{localize "DAGGERHEART.ITEMS.Beastform.hybridizeFeatureTitle"}}</h4>
|
||||
<div>{{{selectedBeastformEffect.description}}}</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class="advanced-forms-container">
|
||||
{{#each hybridForms as | form key |}}
|
||||
<div class="advanced-form-container hybridized {{#unless form}}empty{{/unless}}" id="{{key}}">
|
||||
{{#if form}}
|
||||
<div class="beastform-title-wrapper">
|
||||
<div class="beastform-title">{{form.name}}</div>
|
||||
</div>
|
||||
<div class="hybrid-data-wrapper">
|
||||
<div class="hybrid-data-container">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.features"}}</label>
|
||||
<div class="hybrid-data-inner-container">
|
||||
{{#each form.system.features as | feature |}}
|
||||
<a data-action="toggleHybridFeature" id="{{feature.uuid}}" data-form="{{@../key}}"><div class="hybrid-data {{#if feature.selected}}active{{/if}}" data-tooltip="{{concat "#item#" feature.uuid}}">{{feature.name}}</div></a>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="hybrid-data-container">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.Advantage.plural"}}</label>
|
||||
<div class="hybrid-data-inner-container">
|
||||
{{#each form.system.advantageOn as | advantage id |}}
|
||||
<a data-action="toggleHybridAdvantage" id="{{id}}" data-form="{{@../key}}"><div class="hybrid-data {{#if advantage.selected}}active{{/if}}">{{advantage.value}}</div></a>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="empty-form">
|
||||
<i class="fa-solid fa-plus"></i>
|
||||
<label>{{localize "DAGGERHEART.ITEMS.Beastform.hybridizeDrag"}}</label>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
8
templates/dialogs/beastform/beastformTier.hbs
Normal file
8
templates/dialogs/beastform/beastformTier.hbs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<div class="beastforms-tier">
|
||||
{{#each tier.values as |form uuid|}}
|
||||
<div data-action="selectBeastform" data-uuid="{{uuid}}" data-tooltip="{{concat "#item#" uuid}}" class="beastform-container {{#unless form.selected}}inactive{{/unless}} {{#if form.draggable}}draggable{{/if}}">
|
||||
<img src="{{form.value.img}}" />
|
||||
<div class="beastform-title">{{form.value.name}}</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
3
templates/dialogs/beastform/footer.hbs
Normal file
3
templates/dialogs/beastform/footer.hbs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<footer>
|
||||
<button type="button" data-action="submitBeastform" {{#if (not canSubmit)}}disabled{{/if}}>{{localize "DAGGERHEART.ITEMS.Beastform.transform"}}</button>
|
||||
</footer>
|
||||
11
templates/dialogs/beastform/tabs.hbs
Normal file
11
templates/dialogs/beastform/tabs.hbs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<section class='tab-navigation'>
|
||||
<div class='navigation-container beastform-nav'>
|
||||
<nav class='feature-tab sheet-tabs tabs' data-group='primary'>
|
||||
{{#each tabs as |tab|}}
|
||||
<a class='{{tab.id}} {{tab.cssClass}}' data-action='tab' data-group='{{tab.group}}' data-tab='{{tab.id}}'>
|
||||
{{localize tab.label}}
|
||||
</a>
|
||||
{{/each}}
|
||||
</nav>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<div>
|
||||
<div class="beastforms-container">
|
||||
{{#each beastformTiers as |tier tierKey|}}
|
||||
<fieldset class="beastforms-tier">
|
||||
<legend>{{tier.label}}</legend>
|
||||
{{#each tier.values as |form uuid|}}
|
||||
<div data-action="selectBeastform" data-uuid="{{uuid}}" data-tooltip="{{concat "#item#" uuid}}" class="beastform-container {{#if (and @root.canSubmit (not form.selected))}}inactive{{/if}}">
|
||||
<img src="{{form.value.img}}" />
|
||||
<div class="beastform-title">{{form.value.name}}</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</fieldset>
|
||||
{{/each}}
|
||||
</div>
|
||||
<footer>
|
||||
<button type="button" data-action="submitBeastform" {{#if (not canSubmit)}}disabled{{/if}}>{{localize "DAGGERHEART.ITEMS.Beastform.transform"}}</button>
|
||||
</footer>
|
||||
</div>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
<div class="fieldsets-section">
|
||||
<fieldset class="flex">
|
||||
<legend>{{localize "DAGGERHEART.GENERAL.hitPoints.plural"}}</legend>
|
||||
<legend>{{localize "DAGGERHEART.GENERAL.HitPoints.plural"}}</legend>
|
||||
{{formGroup systemFields.resources.fields.hitPoints.fields.value value=document.system.resources.hitPoints.value label=(localize "DAGGERHEART.ACTORS.Adversary.FIELDS.resources.hitPoints.value.label")}}
|
||||
{{formGroup systemFields.resources.fields.hitPoints.fields.max value=document.system.resources.hitPoints.max}}
|
||||
</fieldset>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
{{#if (eq source.system.type 'horde')}}
|
||||
<div class="tag">
|
||||
<span>{{source.system.hordeHp}}</span>
|
||||
<span>/{{localize "DAGGERHEART.GENERAL.hitPoints.short"}}</span>
|
||||
<span>/{{localize "DAGGERHEART.GENERAL.HitPoints.short"}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
<progress class='progress-bar' value='{{source.system.resources.hitPoints.value}}'
|
||||
max='{{source.system.resources.hitPoints.max}}'></progress>
|
||||
<div class="status-label">
|
||||
<h4>{{localize 'DAGGERHEART.GENERAL.attack.hitPoints.short'}}</h4>
|
||||
<h4>{{localize 'DAGGERHEART.GENERAL.HitPoints.short'}}</h4>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
@ -92,7 +92,7 @@
|
|||
<div class="experience-section">
|
||||
<div class="title">
|
||||
<side-line-div class="invert"></side-line-div>
|
||||
<h3>{{localize DAGGERHEART.GENERAL.experience.plural}}</h3>
|
||||
<h3>{{localize "DAGGERHEART.GENERAL.experience.plural"}}</h3>
|
||||
<side-line-div></side-line-div>
|
||||
</div>
|
||||
<div class="experience-list">
|
||||
|
|
@ -113,6 +113,6 @@
|
|||
</div>
|
||||
<line-div></line-div>
|
||||
<div class="reaction-section">
|
||||
<button data-action="reactionRoll">{{localize DAGGERHEART.GENERAL.Roll.reaction}}</button>
|
||||
<button data-action="reactionRoll">{{localize "DAGGERHEART.GENERAL.Roll.reaction"}}</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
|
@ -47,7 +47,7 @@
|
|||
<p>{{document.system.proficiency}}</p>
|
||||
</div>
|
||||
<div class="status-label">
|
||||
<h4>{{localize "DAGGERHEART.GENERAL.proficienc"}}</h4>
|
||||
<h4>{{localize "DAGGERHEART.GENERAL.proficiency"}}</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -95,6 +95,9 @@
|
|||
<side-line-div></side-line-div>
|
||||
</div>
|
||||
<ul class="items-sidebar-list">
|
||||
{{#if document.system.usedUnarmed}}
|
||||
{{> 'daggerheart.inventory-item' item=document.system.usedUnarmed type='attack' isSidebar=true}}
|
||||
{{/if}}
|
||||
{{#each document.items as |item|}}
|
||||
{{#if item.system.equipped}}
|
||||
{{> 'daggerheart.inventory-item'
|
||||
|
|
@ -132,7 +135,7 @@
|
|||
<div class="experience-section">
|
||||
<div class="title">
|
||||
<side-line-div class="invert"></side-line-div>
|
||||
<h3>{{localize "DAGGERHEART.GENERAL.experience.Single"}}</h3>
|
||||
<h3>{{localize "DAGGERHEART.GENERAL.experience.single"}}</h3>
|
||||
<side-line-div></side-line-div>
|
||||
</div>
|
||||
<div class="experience-list">
|
||||
|
|
|
|||
|
|
@ -15,13 +15,20 @@ Parameters:
|
|||
- showActions {boolean} : If true show feature's actions.
|
||||
--}}
|
||||
|
||||
<li class="inventory-item" {{#if (eq type 'action' )}}data-action-id="{{item.id}}" {{/if}}
|
||||
<li class="inventory-item" {{#if (or (eq type 'action' ) (eq type 'attack'))}}data-action-id="{{item.id}}" {{/if}}
|
||||
data-item-uuid="{{item.uuid}}" data-type="{{type}}" draggable="true">
|
||||
<div class="inventory-item-header" {{#unless noExtensible}}data-action="toggleExtended" {{/unless}}>
|
||||
{{!-- Image --}}
|
||||
<div class="img-portait"
|
||||
data-action='{{ifThen (hasProperty item "use") "useItem" (ifThen (hasProperty item "toChat") "toChat" "editDoc") }}'
|
||||
{{#unless hideTooltip}}data-tooltip="#item#{{item.uuid}}" {{/unless}}>
|
||||
data-action='{{ifThen (or (hasProperty item "use") (eq type 'attack')) "useItem" (ifThen (hasProperty item "toChat") "toChat" "editDoc") }}'
|
||||
{{#unless hideTooltip}}
|
||||
{{#if (eq type 'attack')}}
|
||||
data-tooltip="#attack#{{item.actor.uuid}}"
|
||||
{{else}}
|
||||
data-tooltip="#item#{{item.uuid}}"
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
>
|
||||
<img src="{{item.img}}" class="item-img {{#if isActor}}actor-img{{/if}}" />
|
||||
<img class="roll-img" src="systems/daggerheart/assets/icons/dice/default/d20.svg" alt="d20">
|
||||
</div>
|
||||
|
|
@ -30,7 +37,20 @@ Parameters:
|
|||
<div class="item-label {{#if hideResources}}fullWidth{{/if}}">
|
||||
|
||||
{{!-- Item Name --}}
|
||||
<div class="item-name">{{item.name}}</div>
|
||||
<div class="item-name">{{localize item.name}}</div>
|
||||
|
||||
{{!-- Attack Block Start --}}
|
||||
{{#if (eq type 'attack')}}
|
||||
<div class="item-tags">
|
||||
<div class="tag">
|
||||
{{localize 'DAGGERHEART.GENERAL.unarmed'}}
|
||||
</div>
|
||||
<div class="tag">
|
||||
{{localize 'DAGGERHEART.CONFIG.ActionType.action'}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{!-- Attack Block End --}}
|
||||
|
||||
{{!-- Weapon Block Start --}}
|
||||
{{#if (eq type 'weapon')}}
|
||||
|
|
|
|||
207
templates/sheets/global/partials/inventory-item.hbs
Normal file
207
templates/sheets/global/partials/inventory-item.hbs
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
<li class="inventory-item" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}" data-type="{{type}}" draggable="true">
|
||||
<img src="{{item.img}}" class="item-img {{#if isActor}}actor-img{{/if}}" data-action="useItem" {{#if (not noTooltip)}}data-tooltip="{{concat "#item#" item.uuid}}"{{/if}} />
|
||||
<div class="item-label-wrapper">
|
||||
<div class="item-label {{#unless (and (not isSidebar) (or (eq item.system.resource.type 'simple') item.system.quantity))}}fullWidth{{/unless}}">
|
||||
{{#if isCompanion}}
|
||||
<a class="item-name" data-action="attackRoll">{{item.name}}</a>
|
||||
{{else}}
|
||||
<div class="item-name">{{localize item.name}}</div>
|
||||
{{/if}}
|
||||
{{#if (eq type 'weapon')}}
|
||||
<div class="item-tags">
|
||||
{{#if isSidebar}}
|
||||
<div class="item-labels">
|
||||
<div class="label">
|
||||
{{localize (concat 'DAGGERHEART.CONFIG.Traits.' item.system.attack.roll.trait '.short')}}
|
||||
{{localize (concat 'DAGGERHEART.CONFIG.Range.' item.system.attack.range '.short')}}
|
||||
<span> - </span>
|
||||
{{item.system.attack.damage.parts.0.value.dice}}{{#if item.system.attack.damage.parts.0.value.bonus}} + {{item.system.attack.damage.parts.0.value.bonus}}{{/if}}
|
||||
{{#each item.system.attack.damage.parts.0.type as | type | }}
|
||||
{{#with (lookup @root.config.GENERAL.damageTypes type)}}
|
||||
<i class="fa-solid {{icon}}"></i>
|
||||
{{/with}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="tag">
|
||||
{{localize (concat 'DAGGERHEART.CONFIG.Traits.' item.system.attack.roll.trait '.name')}}
|
||||
</div>
|
||||
<div class="tag">
|
||||
{{localize (concat 'DAGGERHEART.CONFIG.Range.' item.system.attack.range '.name')}}
|
||||
</div>
|
||||
<div class="tag">
|
||||
{{item.system.attack.damage.parts.0.value.dice}}{{#if item.system.attack.damage.parts.0.value.bonus}} + {{item.system.attack.damage.parts.0.value.bonus}}{{/if}}
|
||||
(
|
||||
{{#each item.system.attack.damage.parts.0.type}}
|
||||
{{localize (concat 'DAGGERHEART.CONFIG.DamageType.' this '.abbreviation')}}
|
||||
{{/each}}
|
||||
)
|
||||
</div>
|
||||
<div class="tag">
|
||||
{{localize (concat 'DAGGERHEART.CONFIG.Burden.' item.system.burden)}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if (eq type 'armor')}}
|
||||
{{#if isSidebar}}
|
||||
<div class="item-labels">
|
||||
<div class="label">
|
||||
{{localize "DAGGERHEART.ITEMS.Armor.baseScore"}}:
|
||||
{{item.system.baseScore}}
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="item-tags">
|
||||
<div class="tag">
|
||||
{{localize "DAGGERHEART.ITEMS.Armor.baseScore"}}:
|
||||
{{item.system.baseScore}}
|
||||
</div>
|
||||
<div class="tag">
|
||||
{{localize "DAGGERHEART.ITEMS.Armor.baseThresholds.base"}}:
|
||||
{{item.system.baseThresholds.major}}
|
||||
<span>/</span>
|
||||
{{item.system.baseThresholds.severe}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{#if (eq type 'domainCard')}}
|
||||
{{#if isSidebar}}
|
||||
<div class="item-labels">
|
||||
<div class="label">
|
||||
{{localize (concat 'DAGGERHEART.CONFIG.DomainCardTypes.' item.system.type)}}
|
||||
<span> - </span>
|
||||
{{localize (concat 'DAGGERHEART.GENERAL.Domain.' item.system.domain '.label')}}
|
||||
<span> - </span>
|
||||
<span class="recall-value">{{item.system.recallCost}}</span>
|
||||
<i class="fa-solid fa-bolt"></i>
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="item-tags">
|
||||
<div class="tag">
|
||||
{{localize (concat 'DAGGERHEART.CONFIG.DomainCardTypes.' item.system.type)}}
|
||||
</div>
|
||||
<div class="tag">
|
||||
{{localize (concat 'DAGGERHEART.GENERAL.Domain.' item.system.domain '.label')}}
|
||||
</div>
|
||||
<div class="tag">
|
||||
<span class="recall-label">{{localize "DAGGERHEART.ITEMS.DomainCard.recallCost"}}: </span>
|
||||
<span class="recall-value">{{item.system.recallCost}}</span>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{#if (eq type 'effect')}}
|
||||
<div class="item-tags">
|
||||
<div class="tag">
|
||||
{{localize (concat 'TYPES.Item.' item.parent.type)}}
|
||||
<span>: </span>
|
||||
{{item.parent.name}}
|
||||
</div>
|
||||
<div class="tag">
|
||||
{{#if item.duration.duration}}
|
||||
{{localize 'DAGGERHEART.EFFECTS.Duration.temporary'}}
|
||||
{{else}}
|
||||
{{localize 'DAGGERHEART.EFFECTS.Duration.passive'}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{#each item.statuses as |status|}}
|
||||
<div class="tag">
|
||||
{{localize (concat 'DAGGERHEART.CONFIG.Condition.' status '.name')}}
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if (eq type 'action')}}
|
||||
<div class="item-tags">
|
||||
<div class="tag">
|
||||
{{localize (concat 'DAGGERHEART.ACTIONS.TYPES.' item.type '.name')}}
|
||||
</div>
|
||||
<div class="tag">
|
||||
{{localize (concat 'DAGGERHEART.CONFIG.ActionType.' item.actionType)}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if (eq type 'attack')}}
|
||||
<div class="item-tags">
|
||||
<div class="tag">
|
||||
{{localize 'DAGGERHEART.GENERAL.unarmed'}}
|
||||
</div>
|
||||
<div class="tag">
|
||||
{{localize 'DAGGERHEART.CONFIG.ActionType.action'}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{#if (and (not isSidebar) (eq item.system.resource.type 'simple'))}}
|
||||
{{> "systems/daggerheart/templates/sheets/global/partials/item-resource.hbs"}}
|
||||
{{/if}}
|
||||
{{#if (and (not isSidebar) item.system.quantity)}}
|
||||
<div class="item-resource">
|
||||
<input type="number" class="inventory-item-quantity" value="{{item.system.quantity}}" step="1" />
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{#unless hideControls}}
|
||||
{{#if isActor}}
|
||||
<div class="controls">
|
||||
{{#if (eq type 'actor')}}
|
||||
<a data-action="viewActor" data-potential-adversary="{{categoryAdversary}}" data-adversary="{{item.uuid}}" data-tooltip='{{localize "DAGGERHEART.UI.Tooltip.openActorWorld"}}'>
|
||||
<i class="fa-solid fa-globe"></i>
|
||||
</a>
|
||||
{{/if}}
|
||||
{{#if (eq type 'adversary')}}
|
||||
<a data-action="viewAdversary" data-potential-adversary="{{categoryAdversary}}" data-adversary="{{item.uuid}}" data-tooltip='{{localize "DAGGERHEART.UI.Tooltip.openActorWorld"}}'>
|
||||
<i class="fa-solid fa-globe"></i>
|
||||
</a>
|
||||
<a data-action='deleteAdversary' data-potential-adversary="{{categoryAdversary}}" data-adversary="{{item.uuid}}" data-tooltip='{{localize "CONTROLS.CommonDelete"}}'>
|
||||
<i class='fas fa-trash'></i>
|
||||
</a>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="controls">
|
||||
{{#if (eq type 'weapon')}}
|
||||
<a class="{{#unless item.system.equipped}}unequipped{{/unless}}" data-action="toggleEquipItem" data-tooltip="{{#unless item.system.equipped}}{{localize 'DAGGERHEART.UI.Tooltip.equip'}}{{else}}{{localize 'DAGGERHEART.UI.Tooltip.unequip'}}{{/unless}}">
|
||||
<i class="fa-solid fa-hands"></i>
|
||||
</a>
|
||||
{{/if}}
|
||||
{{#if (eq type 'armor')}}
|
||||
<a class="{{#unless item.system.equipped}}unequipped{{/unless}}" data-action="toggleEquipItem" data-tooltip="{{#unless item.system.equipped}}{{localize 'DAGGERHEART.UI.Tooltip.equip'}}{{else}}{{localize 'DAGGERHEART.UI.Tooltip.unequip'}}{{/unless}}">
|
||||
<i class="fa-solid fa-shield"></i>
|
||||
</a>
|
||||
{{/if}}
|
||||
{{#if (eq type 'domainCard')}}
|
||||
{{#unless item.system.inVault}}
|
||||
<a data-action="toggleVault" data-tooltip="{{localize 'DAGGERHEART.UI.Tooltip.sendToVault'}}">
|
||||
<i class="fa-solid fa-arrow-down"></i>
|
||||
</a>
|
||||
{{else}}
|
||||
<a data-action="toggleVault" data-tooltip="{{localize 'DAGGERHEART.UI.Tooltip.sendToLoadout'}}">
|
||||
<i class="fa-solid fa-arrow-up"></i>
|
||||
</a>
|
||||
{{/unless}}
|
||||
|
||||
{{/if}}
|
||||
<a data-action="toChat" data-tooltip="{{localize 'DAGGERHEART.UI.Tooltip.sendToChat'}}"><i class="fa-regular fa-message"></i></a>
|
||||
<a data-action="triggerContextMenu" data-tooltip="{{localize 'DAGGERHEART.UI.Tooltip.moreOptions'}}"><i class="fa-solid fa-ellipsis-vertical"></i></a>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<span></span>
|
||||
{{/unless}}
|
||||
<div class="item-description">{{#unless isSidebar}}{{{item.system.description}}}{{/unless}}</div>
|
||||
{{#if (and (not isSidebar) (eq item.system.resource.type 'diceValue'))}}
|
||||
{{> "systems/daggerheart/templates/sheets/global/partials/item-resource.hbs"}}
|
||||
{{/if}}
|
||||
{{#if featureType}}
|
||||
<div class="item-buttons">
|
||||
{{#each item.system.actions as | action |}}
|
||||
<button type="button" data-action="useAction" data-action-id="{{action.id}}">{{action.name}}</button>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</li>
|
||||
29
templates/sheets/items/beastform/advanced.hbs
Normal file
29
templates/sheets/items/beastform/advanced.hbs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<section
|
||||
class='tab {{tabs.advanced.cssClass}} {{tabs.advanced.id}}'
|
||||
data-tab='{{tabs.advanced.id}}'
|
||||
data-group='{{tabs.advanced.group}}'
|
||||
>
|
||||
{{formGroup systemFields.beastformType value=source.system.beastformType localize=true blank=false}}
|
||||
|
||||
{{#if (eq source.system.beastformType 'evolved')}}
|
||||
<fieldset class="two-columns even">
|
||||
<legend>{{localize "DAGGERHEART.CONFIG.BeastformType.evolved"}}</legend>
|
||||
|
||||
{{formGroup systemFields.evolved.fields.maximumTier value=source.system.evolved.maximumTier localize=true blank=false}}
|
||||
{{formGroup systemFields.evolved.fields.mainTraitBonus value=source.system.evolved.mainTraitBonus localize=true}}
|
||||
</fieldset>
|
||||
{{/if}}
|
||||
|
||||
{{#if (eq source.system.beastformType 'hybrid')}}
|
||||
<fieldset class="one-column">
|
||||
<legend>{{localize "DAGGERHEART.CONFIG.BeastformType.hybrid"}}</legend>
|
||||
|
||||
{{formGroup systemFields.hybrid.fields.maximumTier value=source.system.hybrid.maximumTier localize=true blank=false}}
|
||||
<div class="nest-inputs">
|
||||
{{formGroup systemFields.hybrid.fields.beastformOptions value=source.system.hybrid.beastformOptions localize=true}}
|
||||
{{formGroup systemFields.hybrid.fields.advantages value=source.system.hybrid.advantages localize=true}}
|
||||
{{formGroup systemFields.hybrid.fields.features value=source.system.hybrid.features localize=true}}
|
||||
</div>
|
||||
</fieldset>
|
||||
{{/if}}
|
||||
</section>
|
||||
|
|
@ -3,12 +3,22 @@
|
|||
data-tab='{{tabs.settings.id}}'
|
||||
data-group='{{tabs.settings.group}}'
|
||||
>
|
||||
<div class="two-columns">
|
||||
{{#if (eq source.system.beastformType 'evolved')}}
|
||||
{{formGroup systemFields.tier value=source.system.tier localize=true}}
|
||||
{{formGroup systemFields.examples value=source.system.examples localize=true}}
|
||||
{{else}}
|
||||
<div class="two-columns even">
|
||||
{{formGroup systemFields.tier value=source.system.tier localize=true}}
|
||||
{{formGroup systemFields.mainTrait value=source.system.mainTrait blank=false localize=true}}
|
||||
</div>
|
||||
|
||||
{{formGroup systemFields.advantageOn value=source.system.advantageOn localize=true}}
|
||||
{{#unless (eq source.system.beastformType 'hybrid')}}
|
||||
{{formGroup systemFields.examples value=source.system.examples localize=true}}
|
||||
<div class="advantage-on-section">
|
||||
<label>{{localize "DAGGERHEART.ITEMS.Beastform.FIELDS.advantageOn.label"}}</label>
|
||||
<input class="advantageon-input" value="{{advantageOn}}" />
|
||||
</div>
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
|
||||
<fieldset class="two-columns even">
|
||||
<legend>{{localize "DAGGERHEART.ITEMS.Beastform.tokenTitle"}}</legend>
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@
|
|||
<h2 class="downtime-title-container">
|
||||
<div>{{title}}</div>
|
||||
</h2>
|
||||
{{#each moves}}
|
||||
<strong>{{this.name}}</strong>
|
||||
<img class="downtime-image" src="{{this.img}}" />
|
||||
<div>{{{this.description}}}</div>
|
||||
{{#if (gt this.actions.length 0)}}<button class="action-use-button">{{localize "Action"}}</button>{{/if}}
|
||||
{{#each moves as | move index |}}
|
||||
<strong>{{move.name}}</strong>
|
||||
<img class="downtime-image" src="{{move.img}}" />
|
||||
<div>{{{move.description}}}</div>
|
||||
{{#each move.actions as | action index |}}
|
||||
<button class="action-use-button" data-move-index="{{@../key}}" data-action-index="{{index}}">{{localize action.name}}</button>
|
||||
{{/each}}
|
||||
{{/each}}
|
||||
</div>
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<div class="daggerheart dh-style tooltip">
|
||||
<h2 class="tooltip-title">{{item.name}}</h2>
|
||||
<img class="tooltip-image" src="{{item.img}}" />
|
||||
<div class="tooltip-description">{{{item.description}}}</div>
|
||||
<div class="tooltip-description">{{{description}}}</div>
|
||||
|
||||
{{#if item.uses.max}}
|
||||
<h4 class="tooltip-sub-title">{{localize "DAGGERHEART.GENERAL.uses"}}</h4>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<div class="daggerheart dh-style tooltip">
|
||||
<h2 class="tooltip-title">{{item.name}}</h2>
|
||||
<img class="tooltip-image" src="{{item.img}}" />
|
||||
<div class="tooltip-description">{{{item.system.description}}}</div>
|
||||
<div class="tooltip-description">{{{description}}}</div>
|
||||
|
||||
<div class="tooltip-information-section triple spaced">
|
||||
<div class="tooltip-information">
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
</div>
|
||||
<div class="tooltip-information-section spaced">
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.hitPoints.plural"}}</label>
|
||||
<label>{{localize "DAGGERHEART.GENERAL.HitPoints.plural"}}</label>
|
||||
<div>{{item.system.resources.hitPoints.max}}</div>
|
||||
</div>
|
||||
<div class="tooltip-information">
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<div class="daggerheart dh-style tooltip">
|
||||
<h2 class="tooltip-title">{{item.name}}</h2>
|
||||
<img class="tooltip-image" src="{{item.img}}" />
|
||||
<div class="tooltip-description">{{{item.system.description}}}</div>
|
||||
<div class="tooltip-description">{{{description}}}</div>
|
||||
|
||||
<div class="tooltip-information-section">
|
||||
<div class="tooltip-information full-width">
|
||||
|
|
|
|||
29
templates/ui/tooltip/attack.hbs
Normal file
29
templates/ui/tooltip/attack.hbs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<div class="daggerheart dh-style tooltip">
|
||||
<h2 class="tooltip-title">{{attack.name}}</h2>
|
||||
<img class="tooltip-image" src="{{attack.img}}" />
|
||||
<div class="tooltip-description">{{{description}}}</div>
|
||||
|
||||
<div class="tooltip-information-section spaced">
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.Trait.single"}}</label>
|
||||
{{#with (lookup config.ACTOR.abilities attack.roll.trait) as | trait |}}
|
||||
<div>{{localize trait.label}}</div>
|
||||
{{/with}}
|
||||
</div>
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.range"}}</label>
|
||||
{{#with (lookup config.GENERAL.range attack.range) as | range |}}
|
||||
<div>{{localize range.label}}</div>
|
||||
{{/with}}
|
||||
</div>
|
||||
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.damage"}}</label>
|
||||
<div>{{{damageFormula attack parent}}}</div>
|
||||
</div>
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.damageType"}}</label>
|
||||
<div>{{{damageSymbols attack.damage.parts}}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
8
templates/ui/tooltip/beastform.hbs
Normal file
8
templates/ui/tooltip/beastform.hbs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<div class="daggerheart dh-style tooltip">
|
||||
<h2 class="tooltip-title">{{item.name}}</h2>
|
||||
<img class="tooltip-image" src="{{item.img}}" />
|
||||
<div class="tooltip-description">{{{description}}}</div>
|
||||
|
||||
{{> "systems/daggerheart/templates/ui/tooltip/parts/tooltipChips.hbs" chips=item.system.advantageOn label=(localize "DAGGERHEART.ITEMS.Beastform.FIELDS.advantageOn.label")}}
|
||||
{{> "systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs" features=item.system.features label=(localize "DAGGERHEART.GENERAL.features")}}
|
||||
</div>
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<div class="daggerheart dh-style tooltip">
|
||||
<h2 class="tooltip-title">{{item.name}}</h2>
|
||||
<img class="tooltip-image" src="{{item.img}}" />
|
||||
<div class="tooltip-description">{{{item.system.description}}}</div>
|
||||
<div class="tooltip-description">{{{description}}}</div>
|
||||
|
||||
<div class="tooltip-information-section">
|
||||
<div class="tooltip-information full-width">
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<div class="daggerheart dh-style tooltip">
|
||||
<h2 class="tooltip-title">{{item.name}}</h2>
|
||||
<img class="tooltip-image" src="{{item.img}}" />
|
||||
<div class="tooltip-description">{{{item.system.description}}}</div>
|
||||
<div class="tooltip-description">{{{description}}}</div>
|
||||
|
||||
<div class="tooltip-information-section">
|
||||
<div class="tooltip-information">
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<div class="daggerheart dh-style tooltip">
|
||||
<h2 class="tooltip-title">{{item.name}}</h2>
|
||||
<img class="tooltip-image" src="{{item.img}}" />
|
||||
<div class="tooltip-description">{{{item.system.description}}}</div>
|
||||
<div class="tooltip-description">{{{description}}}</div>
|
||||
|
||||
{{> "systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs" features=item.system.actions label=(localize "DAGGERHEART.GENERAL.Action.plural") }}
|
||||
{{> "systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs" features=item.effects label=(localize "DAGGERHEART.GENERAL.Effect.plural") }}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<div class="daggerheart dh-style tooltip">
|
||||
<h2 class="tooltip-title">{{item.name}}</h2>
|
||||
<img class="tooltip-image" src="{{item.img}}" />
|
||||
<div class="tooltip-description">{{{item.system.description}}}</div>
|
||||
<div class="tooltip-description">{{{description}}}</div>
|
||||
|
||||
{{> "systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs" features=item.system.actions label=(localize "DAGGERHEART.GENERAL.Action.plural") }}
|
||||
</div>
|
||||
6
templates/ui/tooltip/parts/tooltipChips.hbs
Normal file
6
templates/ui/tooltip/parts/tooltipChips.hbs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<h4 class="tooltip-sub-title">{{localize label}}</h4>
|
||||
<div class="tooltip-chips">
|
||||
{{#each chips as | chip |}}
|
||||
<div class="tooltip-chip">{{ifThen chip.value chip.value chip}}</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
<div class="tooltip-tag-label">{{localize feature.name}}</div>
|
||||
{{#if feature.img}}<img class="tooltip-tag-image" src="{{feature.img}}" />{{/if}}
|
||||
</div>
|
||||
<div class="tooltip-tag-description">{{{localize (ifThen feature.description feature.description feature.system.description)}}}</div>
|
||||
<div class="tooltip-tag-description">{{{localize (ifThen feature.description feature.description (ifThen feature.system.enrichedDescription feature.system.enrichedDescription feature.system.description))}}}</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<div class="daggerheart dh-style tooltip">
|
||||
<h2 class="tooltip-title">{{item.name}}</h2>
|
||||
<img class="tooltip-image" src="{{item.img}}" />
|
||||
<div class="tooltip-description">{{{item.system.description}}}</div>
|
||||
<div class="tooltip-description">{{{description}}}</div>
|
||||
|
||||
<div class="tooltip-information-section">
|
||||
<div class="tooltip-information">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue