mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-17 07:36:26 +01:00
Merge branch 'main' into feature/336-damage-targeted-resources
This commit is contained in:
commit
b3984a8e5f
57 changed files with 937 additions and 309 deletions
|
|
@ -140,6 +140,7 @@ Hooks.once('init', () => {
|
|||
CONFIG.Combat.documentClass = documents.DhpCombat;
|
||||
CONFIG.ui.combat = applications.ui.DhCombatTracker;
|
||||
CONFIG.ui.chat = applications.ui.DhChatLog;
|
||||
CONFIG.ui.hotbar = applications.ui.DhHotbar;
|
||||
CONFIG.Token.rulerClass = placeables.DhTokenRuler;
|
||||
|
||||
CONFIG.ui.resources = applications.ui.DhFearTracker;
|
||||
|
|
|
|||
63
lang/en.json
63
lang/en.json
|
|
@ -252,8 +252,9 @@
|
|||
"title": "{actor} - Death Move"
|
||||
},
|
||||
"Downtime": {
|
||||
"downtimeHeader": "Downtime Moves ({current}/{max})",
|
||||
"longRest": {
|
||||
"title": "Long Rest",
|
||||
"moves": "Long Rest Moves ({current}/{max})",
|
||||
"clearStress": {
|
||||
"description": "Describe how you blow off steam or pull yourself together, and clear all marked Stress.",
|
||||
"name": "Clear Stress"
|
||||
|
|
@ -270,7 +271,6 @@
|
|||
"description": "Describe how you patch yourself up and remove all marked Hit Points. You may also do this on an ally instead.",
|
||||
"name": "Tend to Wounds"
|
||||
},
|
||||
"title": "Long Rest",
|
||||
"workOnAProject": {
|
||||
"description": "Establish or continue work on a project.",
|
||||
"name": "Work on a Project"
|
||||
|
|
@ -278,6 +278,7 @@
|
|||
},
|
||||
"shortRest": {
|
||||
"title": "Short Rest",
|
||||
"moves": "Short Rest Moves ({current}/{max})",
|
||||
"tendToWounds": {
|
||||
"name": "Tend to Wounds",
|
||||
"description": "Describe how you hastily patch yourself up, then clear a number of Hit Points equal to 1d4 + your tier. You can do this to an ally instead."
|
||||
|
|
@ -294,7 +295,8 @@
|
|||
"name": "Prepare",
|
||||
"description": "Describe how you prepare yourself for the path ahead, then gain a Hope. If you choose to Prepare with one or more members of your party, you each gain 2 Hope."
|
||||
}
|
||||
}
|
||||
},
|
||||
"takeDowntime": "Take Downtime"
|
||||
},
|
||||
"HUD": {
|
||||
"tokenHUD": {
|
||||
|
|
@ -410,7 +412,11 @@
|
|||
"rerollDice": "Reroll Dice"
|
||||
}
|
||||
},
|
||||
|
||||
"CLASS": {
|
||||
"Feature": {
|
||||
"rallyDice": "Bardic Rally Dice"
|
||||
}
|
||||
},
|
||||
"CONFIG": {
|
||||
"ActionType": {
|
||||
"passive": "Passive",
|
||||
|
|
@ -778,7 +784,7 @@
|
|||
"WeaponFeature": {
|
||||
"barrier": {
|
||||
"name": "Barrier",
|
||||
"description": "+{armorScore} to Armor Score; -1 to Evasion"
|
||||
"description": "Gain your character's Tier + 1 to Armor Score; -1 to Evasion"
|
||||
},
|
||||
"bonded": {
|
||||
"name": "Bonded",
|
||||
|
|
@ -894,7 +900,7 @@
|
|||
},
|
||||
"paired": {
|
||||
"name": "Paired",
|
||||
"description": "+{bonusDamage} to primary weapon damage to targets within Melee range"
|
||||
"description": "Add your character's Tier + 1 to primary weapon damage against targets within Melee range"
|
||||
},
|
||||
"parry": {
|
||||
"name": "Parry",
|
||||
|
|
@ -914,7 +920,7 @@
|
|||
},
|
||||
"protective": {
|
||||
"name": "Protective",
|
||||
"description": "+{tier} to Armor Score"
|
||||
"description": "Add your character's Tier to your Armor Score"
|
||||
},
|
||||
"quick": {
|
||||
"name": "Quick",
|
||||
|
|
@ -963,10 +969,6 @@
|
|||
"timebending": {
|
||||
"name": "Timebending",
|
||||
"description": "You can choose the target of your attack after making your attack roll."
|
||||
},
|
||||
"versatile": {
|
||||
"name": "Versatile",
|
||||
"description": "This weapon can also be used with these statistics—{characterTrait}, {range}, {damage}."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -1015,6 +1017,30 @@
|
|||
"singular": "Adversary",
|
||||
"plural": "Adversaries"
|
||||
},
|
||||
"Bonuses": {
|
||||
"rest": {
|
||||
"shortRest": {
|
||||
"shortRestMoves": {
|
||||
"label": "Short Rest: Bonus Short Rest Moves",
|
||||
"hint": "The number of extra Short Rest Moves the character can take during a Short Rest."
|
||||
},
|
||||
"longRestMoves": {
|
||||
"label": "Short Rest: Bonus Long Rest Moves",
|
||||
"hint": "The number of extra Long Rest Moves the character can take during a Short Rest."
|
||||
}
|
||||
},
|
||||
"longRest": {
|
||||
"shortRestMoves": {
|
||||
"label": "Long Rest: Bonus Short Rest Moves",
|
||||
"hint": "The number of extra Short Rest Moves the character can take during a Long Rest."
|
||||
},
|
||||
"longRestMoves": {
|
||||
"label": "Long Rest: Bonus Long Rest Moves",
|
||||
"hint": "The number of extra Long Rest Moves the character can take during a Long Rest."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Character": {
|
||||
"singular": "Character",
|
||||
"plural": "Characters"
|
||||
|
|
@ -1328,8 +1354,8 @@
|
|||
},
|
||||
"DomainCard": {
|
||||
"type": "Type",
|
||||
"foundation": "Foundation",
|
||||
"recallCost": "Recall Cost",
|
||||
"foundationTitle": "Foundation",
|
||||
"specializationTitle": "Specialization",
|
||||
"masteryTitle": "Mastery"
|
||||
},
|
||||
|
|
@ -1540,7 +1566,18 @@
|
|||
"noAvailableArmorMarks": "You have no more available armor marks",
|
||||
"notEnoughStress": "You don't have enough stress",
|
||||
"damageIgnore": "{character} did not take damage",
|
||||
"featureIsMissing": "Feature is missing"
|
||||
"featureIsMissing": "Feature is missing",
|
||||
"actionIsMissing": "Action is missing",
|
||||
"attackIsMissing": "Attack is missing",
|
||||
"unownedActionMacro": "Cannot make a Use macro for an Action not on your character",
|
||||
"unownedAttackMacro": "Cannot make a Use macro for an Attack that doesn't belong to one of your characters",
|
||||
"featureNotHope": "This feature is used as something else than a Hope feature and cannot be used here.",
|
||||
"featureNotClass": "This feature is used as something else than a Class feature and cannot be used here.",
|
||||
"featureNotPrimary": "This feature is used as something else than a Primary feature and cannot be used here.",
|
||||
"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."
|
||||
},
|
||||
"Tooltip": {
|
||||
"openItemWorld": "Open Item World",
|
||||
|
|
|
|||
|
|
@ -506,9 +506,7 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
|
|||
name: this.setup.ancestryName ?? this.setup.primaryAncestry.name,
|
||||
system: {
|
||||
...this.setup.primaryAncestry.system,
|
||||
features: [primaryAncestryFeature.uuid, secondaryAncestryFeature.uuid],
|
||||
primaryFeature: primaryAncestryFeature.uuid,
|
||||
secondaryFeature: secondaryAncestryFeature.uuid
|
||||
features: [primaryAncestryFeature.uuid, secondaryAncestryFeature.uuid]
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
if (this.roll) {
|
||||
context.roll = this.roll;
|
||||
context.rollType = this.roll?.constructor.name;
|
||||
context.rallyDie = this.roll.rallyChoices;
|
||||
context.experiences = Object.keys(this.config.data.experiences).map(id => ({
|
||||
id,
|
||||
...this.config.data.experiences[id]
|
||||
|
|
|
|||
|
|
@ -7,8 +7,22 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
|||
this.actor = actor;
|
||||
this.shortrest = shortrest;
|
||||
|
||||
const options = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).restMoves;
|
||||
this.moveData = shortrest ? options.shortRest : options.longRest;
|
||||
this.moveData = foundry.utils.deepClone(
|
||||
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).restMoves
|
||||
);
|
||||
this.nrChoices = {
|
||||
shortRest: {
|
||||
max:
|
||||
(shortrest ? this.moveData.shortRest.nrChoices : 0) +
|
||||
actor.system.bonuses.rest[`${shortrest ? 'short' : 'long'}Rest`].shortMoves
|
||||
},
|
||||
longRest: {
|
||||
max:
|
||||
(!shortrest ? this.moveData.longRest.nrChoices : 0) +
|
||||
actor.system.bonuses.rest[`${shortrest ? 'short' : 'long'}Rest`].longMoves
|
||||
}
|
||||
};
|
||||
this.nrChoices.total = { max: this.nrChoices.shortRest.max + this.nrChoices.longRest.max };
|
||||
}
|
||||
|
||||
get title() {
|
||||
|
|
@ -17,8 +31,8 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
|||
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
classes: ['daggerheart', 'views', 'downtime'],
|
||||
position: { width: 680, height: 'auto' },
|
||||
classes: ['daggerheart', 'views', 'dh-style', 'dialog', 'downtime'],
|
||||
position: { width: 'auto', height: 'auto' },
|
||||
actions: {
|
||||
selectMove: this.selectMove,
|
||||
takeDowntime: this.takeDowntime
|
||||
|
|
@ -29,7 +43,7 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
|||
static PARTS = {
|
||||
application: {
|
||||
id: 'downtime',
|
||||
template: 'systems/daggerheart/templates/dialogs/downtime.hbs'
|
||||
template: 'systems/daggerheart/templates/dialogs/downtime/downtime.hbs'
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -37,46 +51,83 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
|||
super._attachPartListeners(partId, htmlElement, options);
|
||||
|
||||
htmlElement
|
||||
.querySelectorAll('.activity-image')
|
||||
.querySelectorAll('.activity-container')
|
||||
.forEach(element => element.addEventListener('contextmenu', this.deselectMove.bind(this)));
|
||||
}
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.title = game.i18n.localize(
|
||||
`DAGGERHEART.APPLICATIONS.Downtime.${this.shortrest ? 'shortRest' : 'longRest'}.title`
|
||||
);
|
||||
context.selectedActivity = this.selectedActivity;
|
||||
context.moveData = this.moveData;
|
||||
context.nrCurrentChoices = Object.values(this.moveData.moves).reduce((acc, x) => acc + (x.selected ?? 0), 0);
|
||||
context.disabledDowntime = context.nrCurrentChoices < context.moveData.nrChoices;
|
||||
context.nrCurrentChoices = Object.values(this.moveData).reduce((acc, category) => {
|
||||
acc += Object.values(category.moves).reduce((acc, x) => acc + (x.selected ?? 0), 0);
|
||||
return acc;
|
||||
}, 0);
|
||||
|
||||
context.nrChoices = {
|
||||
...this.nrChoices,
|
||||
shortRest: {
|
||||
...this.nrChoices.shortRest,
|
||||
current: Object.values(this.moveData.shortRest.moves).reduce((acc, x) => acc + (x.selected ?? 0), 0)
|
||||
},
|
||||
longRest: {
|
||||
...this.nrChoices.longRest,
|
||||
current: Object.values(this.moveData.longRest.moves).reduce((acc, x) => acc + (x.selected ?? 0), 0)
|
||||
}
|
||||
};
|
||||
context.nrChoices.total = {
|
||||
...this.nrChoices.total,
|
||||
current: context.nrChoices.shortRest.current + context.nrChoices.longRest.current
|
||||
};
|
||||
|
||||
context.shortRestMoves = this.nrChoices.shortRest.max > 0 ? this.moveData.shortRest : null;
|
||||
context.longRestMoves = this.nrChoices.longRest.max > 0 ? this.moveData.longRest : null;
|
||||
|
||||
context.disabledDowntime = context.nrChoices.total.current < context.nrChoices.total.max;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static selectMove(_, button) {
|
||||
const nrSelected = Object.values(this.moveData.moves).reduce((acc, x) => acc + (x.selected ?? 0), 0);
|
||||
if (nrSelected === this.moveData.nrChoices) {
|
||||
static selectMove(_, target) {
|
||||
const nrSelected = Object.values(this.moveData[target.dataset.category].moves).reduce(
|
||||
(acc, x) => acc + (x.selected ?? 0),
|
||||
0
|
||||
);
|
||||
|
||||
if (nrSelected === this.nrChoices[target.dataset.category].max) {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.noMoreMoves'));
|
||||
return;
|
||||
}
|
||||
|
||||
const move = button.dataset.move;
|
||||
this.moveData.moves[move].selected = this.moveData.moves[move].selected
|
||||
? this.moveData.moves[move].selected + 1
|
||||
const move = target.dataset.move;
|
||||
this.moveData[target.dataset.category].moves[move].selected = this.moveData[target.dataset.category].moves[move]
|
||||
.selected
|
||||
? this.moveData[target.dataset.category].moves[move].selected + 1
|
||||
: 1;
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
deselectMove(event) {
|
||||
const move = event.currentTarget.dataset.move;
|
||||
this.moveData.moves[move].selected = this.moveData.moves[move].selected
|
||||
? this.moveData.moves[move].selected - 1
|
||||
const button = event.target.closest('.activity-container');
|
||||
const move = button.dataset.move;
|
||||
this.moveData[button.dataset.category].moves[move].selected = this.moveData[button.dataset.category].moves[move]
|
||||
.selected
|
||||
? this.moveData[button.dataset.category].moves[move].selected - 1
|
||||
: 0;
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async takeDowntime() {
|
||||
const moves = Object.values(this.moveData.moves).filter(x => x.selected);
|
||||
const moves = Object.values(this.moveData).flatMap(category => {
|
||||
return Object.values(category.moves)
|
||||
.filter(x => x.selected)
|
||||
.flatMap(move => [...Array(move.selected).keys()].map(_ => move));
|
||||
});
|
||||
|
||||
const cls = getDocumentClass('ChatMessage');
|
||||
const msg = new cls({
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ export default class ResourceDiceDialog extends HandlebarsApplicationMixin(Appli
|
|||
|
||||
static async rerollDice() {
|
||||
const max = itemAbleRollParse(this.item.system.resource.max, this.actor, this.item);
|
||||
const diceFormula = `${max}d${this.item.system.resource.dieFaces}`;
|
||||
const diceFormula = `${max}${this.item.system.resource.dieFaces}`;
|
||||
const roll = await new Roll(diceFormula).evaluate();
|
||||
if (game.modules.get('dice-so-nice')?.active) await game.dice3d.showForRoll(roll, game.user, true);
|
||||
this.rollValues = roll.terms[0].results.map(x => ({ value: x.result, used: false }));
|
||||
|
|
|
|||
|
|
@ -4,13 +4,14 @@ import DHActionConfig from '../../sheets-configs/action-config.mjs';
|
|||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
export default class DhSettingsActionView extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor(resolve, reject, title, name, img, description, actions) {
|
||||
constructor(resolve, reject, title, name, icon, img, description, actions) {
|
||||
super({});
|
||||
|
||||
this.resolve = resolve;
|
||||
this.reject = reject;
|
||||
this.viewTitle = title;
|
||||
this.name = name;
|
||||
this.icon = icon;
|
||||
this.img = img;
|
||||
this.description = description;
|
||||
this.actions = actions;
|
||||
|
|
@ -23,7 +24,7 @@ export default class DhSettingsActionView extends HandlebarsApplicationMixin(App
|
|||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
classes: ['daggerheart', 'setting', 'dh-style'],
|
||||
position: { width: '400', height: 'auto' },
|
||||
position: { width: 440, height: 'auto' },
|
||||
actions: {
|
||||
editImage: this.onEditImage,
|
||||
addItem: this.addItem,
|
||||
|
|
@ -46,6 +47,7 @@ export default class DhSettingsActionView extends HandlebarsApplicationMixin(App
|
|||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.name = this.name;
|
||||
context.icon = this.icon;
|
||||
context.img = this.img;
|
||||
context.description = this.description;
|
||||
context.enrichedDescription = await foundry.applications.ux.TextEditor.enrichHTML(context.description);
|
||||
|
|
@ -55,8 +57,9 @@ export default class DhSettingsActionView extends HandlebarsApplicationMixin(App
|
|||
}
|
||||
|
||||
static async updateData(event, element, formData) {
|
||||
const { name, img, description } = foundry.utils.expandObject(formData.object);
|
||||
const { name, icon, description } = foundry.utils.expandObject(formData.object);
|
||||
this.name = name;
|
||||
this.icon = icon;
|
||||
this.description = description;
|
||||
|
||||
this.render();
|
||||
|
|
@ -65,6 +68,7 @@ export default class DhSettingsActionView extends HandlebarsApplicationMixin(App
|
|||
static async saveForm(event) {
|
||||
this.resolve({
|
||||
name: this.name,
|
||||
icon: this.icon,
|
||||
img: this.img,
|
||||
description: this.description,
|
||||
actions: this.actions
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
|||
reject,
|
||||
game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.downtimeMoves'),
|
||||
move.name,
|
||||
move.icon,
|
||||
move.img,
|
||||
move.description,
|
||||
move.actions
|
||||
|
|
@ -87,6 +88,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
|||
await this.settings.updateSource({
|
||||
[`restMoves.${type}.moves.${id}`]: {
|
||||
name: data.name,
|
||||
icon: data.icon,
|
||||
img: data.img,
|
||||
description: data.description
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,6 +88,9 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
|||
element.value = `system.${item.value}`;
|
||||
},
|
||||
click: e => e.fetch(),
|
||||
customize: function (_input, _inputRect, container) {
|
||||
container.style.zIndex = foundry.applications.api.ApplicationV2._maxZ;
|
||||
},
|
||||
minLength: 0
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
useAction: this.useAction,
|
||||
toggleResourceDice: this.toggleResourceDice,
|
||||
handleResourceDice: this.handleResourceDice,
|
||||
toChat: this.toChat
|
||||
toChat: this.toChat,
|
||||
useDowntime: this.useDowntime
|
||||
},
|
||||
window: {
|
||||
resizable: true
|
||||
|
|
@ -752,6 +753,12 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
}
|
||||
}
|
||||
|
||||
static useDowntime(_, button) {
|
||||
new game.system.api.applications.dialogs.Downtime(this.document, button.dataset.type === 'shortRest').render(
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
async _onDragStart(event) {
|
||||
const item = this.getItem(event);
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
|||
actions: {
|
||||
openSettings: DHBaseActorSheet.#openSettings
|
||||
},
|
||||
dragDrop: []
|
||||
dragDrop: [{ dragSelector: '.inventory-item[data-type="attack"]', dropSelector: null }]
|
||||
};
|
||||
|
||||
/**@type {typeof DHBaseActorSettings}*/
|
||||
|
|
@ -49,4 +49,27 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
|||
static async #openSettings() {
|
||||
await this.settingSheet.render({ force: true });
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Application Drag/Drop */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* On dragStart on the item.
|
||||
* @param {DragEvent} event - The drag event
|
||||
*/
|
||||
async _onDragStart(event) {
|
||||
const attackItem = event.currentTarget.closest('.inventory-item[data-type="attack"]');
|
||||
|
||||
if (attackItem) {
|
||||
const attackData = {
|
||||
type: 'Attack',
|
||||
actorUuid: this.document.uuid,
|
||||
img: this.document.system.attack.img,
|
||||
fromInternal: true
|
||||
};
|
||||
event.dataTransfer.setData('text/plain', JSON.stringify(attackData));
|
||||
event.dataTransfer.setDragImage(attackItem.querySelector('img'), 60, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,8 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
|||
},
|
||||
dragDrop: [
|
||||
{ dragSelector: null, dropSelector: '.tab.features .drop-section' },
|
||||
{ dragSelector: '.feature-item', dropSelector: null }
|
||||
{ dragSelector: '.feature-item', dropSelector: null },
|
||||
{ dragSelector: '.action-item', dropSelector: null }
|
||||
]
|
||||
};
|
||||
|
||||
|
|
@ -258,6 +259,23 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
|||
const featureData = { type: 'Item', data: { ...feature.toObject(), _id: null }, fromInternal: true };
|
||||
event.dataTransfer.setData('text/plain', JSON.stringify(featureData));
|
||||
event.dataTransfer.setDragImage(featureItem.querySelector('img'), 60, 0);
|
||||
} else {
|
||||
const actionItem = event.currentTarget.closest('.action-item');
|
||||
if (actionItem) {
|
||||
const action = this.document.system.actions[actionItem.dataset.index];
|
||||
if (!action) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.actionIsMissing'));
|
||||
return;
|
||||
}
|
||||
|
||||
const actionData = {
|
||||
type: 'Action',
|
||||
data: { ...action.toObject(), id: action.id, itemUuid: this.document.uuid },
|
||||
fromInternal: true
|
||||
};
|
||||
event.dataTransfer.setData('text/plain', JSON.stringify(actionData));
|
||||
event.dataTransfer.setDragImage(actionItem.querySelector('img'), 60, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -63,22 +63,8 @@ export default class AncestrySheet extends DHHeritageSheet {
|
|||
event.stopPropagation();
|
||||
const target = button.closest('.feature-item');
|
||||
const feature = this.document.system[`${target.dataset.type}Feature`];
|
||||
const featureExists = feature && Object.keys(feature).length > 0;
|
||||
|
||||
if (featureExists) {
|
||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||
window: {
|
||||
title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', {
|
||||
type: game.i18n.localize(`TYPES.Item.feature`),
|
||||
name: feature.name
|
||||
})
|
||||
},
|
||||
content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { name: feature.name })
|
||||
});
|
||||
if (!confirmed) return;
|
||||
}
|
||||
|
||||
if (featureExists && target.dataset.type === 'primary') await feature.update({ 'system.primary': null });
|
||||
if (feature) await feature.update({ 'system.subType': null });
|
||||
await this.document.update({
|
||||
'system.features': this.document.system.features.filter(x => x && x.uuid !== feature.uuid).map(x => x.uuid)
|
||||
});
|
||||
|
|
@ -94,15 +80,18 @@ export default class AncestrySheet extends DHHeritageSheet {
|
|||
*/
|
||||
async _onDrop(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
|
||||
|
||||
const item = await fromUuid(data.uuid);
|
||||
if (item?.type === 'feature') {
|
||||
const subType = event.target.closest('.primary-feature') ? 'primary' : 'secondary';
|
||||
await item.update({ 'system.subType': subType });
|
||||
if (item.system.subType && item.system.subType !== CONFIG.DH.ITEM.featureSubTypes[subType]) {
|
||||
const error = subType === 'primary' ? 'featureNotPrimary' : 'featureNotSecondary';
|
||||
ui.notifications.warn(game.i18n.localize(`DAGGERHEART.UI.Notifications.${error}`));
|
||||
return;
|
||||
}
|
||||
|
||||
await item.update({ 'system.subType': subType });
|
||||
await this.document.update({
|
||||
'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid]
|
||||
});
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ export default class ClassSheet extends DHBaseItemSheet {
|
|||
/* -------------------------------------------- */
|
||||
|
||||
async _onDrop(event) {
|
||||
event.stopPropagation();
|
||||
const data = TextEditor.getDragEventData(event);
|
||||
const item = await fromUuid(data.uuid);
|
||||
const target = event.target.closest('fieldset.drop-section');
|
||||
|
|
@ -87,12 +88,24 @@ export default class ClassSheet extends DHBaseItemSheet {
|
|||
});
|
||||
} else if (item.type === 'feature') {
|
||||
if (target.classList.contains('hope-feature')) {
|
||||
if (item.system.subType && item.system.subType !== CONFIG.DH.ITEM.featureSubTypes.hope) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureNotHope'));
|
||||
return;
|
||||
}
|
||||
|
||||
await item.update({ 'system.subType': CONFIG.DH.ITEM.featureSubTypes.hope });
|
||||
await this.document.update({
|
||||
'system.hopeFeatures': [...this.document.system.hopeFeatures.map(x => x.uuid), item.uuid]
|
||||
'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid]
|
||||
});
|
||||
} else if (target.classList.contains('class-feature')) {
|
||||
if (item.system.subType && item.system.subType !== CONFIG.DH.ITEM.featureSubTypes.class) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureNotClass'));
|
||||
return;
|
||||
}
|
||||
|
||||
await item.update({ 'system.subType': CONFIG.DH.ITEM.featureSubTypes.class });
|
||||
await this.document.update({
|
||||
'system.classFeatures': [...this.document.system.classFeatures.map(x => x.uuid), item.uuid]
|
||||
'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid]
|
||||
});
|
||||
}
|
||||
} else if (item.type === 'weapon') {
|
||||
|
|
@ -177,28 +190,25 @@ export default class ClassSheet extends DHBaseItemSheet {
|
|||
doc.sheet.render({ force: true });
|
||||
}
|
||||
|
||||
getActionPath(type) {
|
||||
return type === 'hope' ? 'hopeFeatures' : 'classFeatures';
|
||||
}
|
||||
|
||||
static async addFeature(_, target) {
|
||||
const actionPath = this.getActionPath(target.dataset.type);
|
||||
const feature = await game.items.documentClass.create({
|
||||
type: 'feature',
|
||||
name: game.i18n.format('DOCUMENT.New', { type: game.i18n.localize('TYPES.Item.feature') })
|
||||
name: game.i18n.format('DOCUMENT.New', { type: game.i18n.localize('TYPES.Item.feature') }),
|
||||
system: {
|
||||
subType:
|
||||
target.dataset.type === 'hope'
|
||||
? CONFIG.DH.ITEM.featureSubTypes.hope
|
||||
: CONFIG.DH.ITEM.featureSubTypes.class
|
||||
}
|
||||
});
|
||||
await this.document.update({
|
||||
[`system.${actionPath}`]: [
|
||||
...this.document.system[actionPath].filter(x => x).map(x => x.uuid),
|
||||
feature.uuid
|
||||
]
|
||||
[`system.features`]: [...this.document.system.features.filter(x => x).map(x => x.uuid), feature.uuid]
|
||||
});
|
||||
}
|
||||
|
||||
static async editFeature(_, button) {
|
||||
const target = button.closest('.feature-item');
|
||||
const actionPath = this.getActionPath(button.dataset.type);
|
||||
const feature = this.document.system[actionPath].find(x => x?.id === target.dataset.featureId);
|
||||
const feature = this.document.system.features.find(x => x?.id === target.dataset.featureId);
|
||||
if (!feature) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureIsMissing'));
|
||||
return;
|
||||
|
|
@ -210,10 +220,16 @@ export default class ClassSheet extends DHBaseItemSheet {
|
|||
static async deleteFeature(event, button) {
|
||||
event.stopPropagation();
|
||||
const target = button.closest('.feature-item');
|
||||
const actionPath = this.getActionPath(button.dataset.type);
|
||||
|
||||
const feature = this.document.system.features.find(
|
||||
feature => feature && feature.id === target.dataset.featureId
|
||||
);
|
||||
if (feature) {
|
||||
await feature.update({ 'system.subType': null });
|
||||
}
|
||||
|
||||
await this.document.update({
|
||||
[`system.${actionPath}`]: this.document.system[actionPath]
|
||||
[`system.features`]: this.document.system.features
|
||||
.filter(feature => feature && feature.id !== target.dataset.featureId)
|
||||
.map(x => x.uuid)
|
||||
});
|
||||
|
|
|
|||
|
|
@ -40,28 +40,46 @@ export default class SubclassSheet extends DHBaseItemSheet {
|
|||
static async addFeature(_, target) {
|
||||
const feature = await game.items.documentClass.create({
|
||||
type: 'feature',
|
||||
name: game.i18n.format('DOCUMENT.New', { type: game.i18n.localize('TYPES.Item.feature') })
|
||||
name: game.i18n.format('DOCUMENT.New', { type: game.i18n.localize('TYPES.Item.feature') }),
|
||||
system: {
|
||||
subType:
|
||||
target.dataset.type === 'foundation'
|
||||
? CONFIG.DH.ITEM.featureSubTypes.foundation
|
||||
: target.dataset.type === 'specialization'
|
||||
? CONFIG.DH.ITEM.featureSubTypes.specialization
|
||||
: CONFIG.DH.ITEM.featureSubTypes.mastery
|
||||
}
|
||||
});
|
||||
await this.document.update({
|
||||
[`system.${target.dataset.type}`]: feature.uuid
|
||||
[`system.features`]: [...this.document.system.features.map(x => x.uuid), feature.uuid]
|
||||
});
|
||||
}
|
||||
|
||||
static async editFeature(_, button) {
|
||||
const feature = this.document.system[button.dataset.type];
|
||||
const feature = this.document.system.features.find(x => x.id === button.dataset.feature);
|
||||
if (!feature) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureIsMissing'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (feature) {
|
||||
await feature.update({ 'system.subType': null });
|
||||
}
|
||||
|
||||
feature.sheet.render(true);
|
||||
}
|
||||
|
||||
static async deleteFeature(event, button) {
|
||||
static async deleteFeature(event, target) {
|
||||
event.stopPropagation();
|
||||
const feature = this.document.system.features.find(feature => feature.id === target.dataset.feature);
|
||||
if (feature) {
|
||||
await feature.update({ 'system.subType': null });
|
||||
}
|
||||
|
||||
await this.document.update({
|
||||
[`system.${button.dataset.type}`]: null
|
||||
[`system.features`]: this.document.system.features
|
||||
.filter(feature => feature && feature.id !== target.dataset.feature)
|
||||
.map(x => x.uuid)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -82,18 +100,45 @@ export default class SubclassSheet extends DHBaseItemSheet {
|
|||
}
|
||||
|
||||
async _onDrop(event) {
|
||||
event.stopPropagation();
|
||||
|
||||
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
|
||||
if (data.fromInternal) return;
|
||||
|
||||
const item = await fromUuid(data.uuid);
|
||||
if (item?.type === 'feature') {
|
||||
const dropSection = event.target.closest('.drop-section');
|
||||
if (this.document.system[dropSection.dataset.type]) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.notifications.featureIsFull'));
|
||||
return;
|
||||
}
|
||||
const target = event.target.closest('fieldset.drop-section');
|
||||
if (item.type === 'feature') {
|
||||
if (target.dataset.type === 'foundation') {
|
||||
if (item.system.subType && item.system.subType !== CONFIG.DH.ITEM.featureSubTypes.foundation) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureNotFoundation'));
|
||||
return;
|
||||
}
|
||||
|
||||
await this.document.update({ [`system.${dropSection.dataset.type}`]: item.uuid });
|
||||
await item.update({ 'system.subType': CONFIG.DH.ITEM.featureSubTypes.foundation });
|
||||
await this.document.update({
|
||||
'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid]
|
||||
});
|
||||
} else if (target.dataset.type === 'specialization') {
|
||||
if (item.system.subType && item.system.subType !== CONFIG.DH.ITEM.featureSubTypes.specialization) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureNotSpecialization'));
|
||||
return;
|
||||
}
|
||||
|
||||
await item.update({ 'system.subType': CONFIG.DH.ITEM.featureSubTypes.specialization });
|
||||
await this.document.update({
|
||||
'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid]
|
||||
});
|
||||
} else if (target.dataset.type === 'mastery') {
|
||||
if (item.system.subType && item.system.subType !== CONFIG.DH.ITEM.featureSubTypes.mastery) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureNotMastery'));
|
||||
return;
|
||||
}
|
||||
|
||||
await item.update({ 'system.subType': CONFIG.DH.ITEM.featureSubTypes.mastery });
|
||||
await this.document.update({
|
||||
'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid]
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,3 +2,4 @@ export { default as DhChatLog } from './chatLog.mjs';
|
|||
export { default as DhCombatTracker } from './combatTracker.mjs';
|
||||
export * as DhCountdowns from './countdowns.mjs';
|
||||
export { default as DhFearTracker } from './fearTracker.mjs';
|
||||
export { default as DhHotbar } from './hotbar.mjs';
|
||||
|
|
|
|||
129
module/applications/ui/hotbar.mjs
Normal file
129
module/applications/ui/hotbar.mjs
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
export default class DhHotbar extends foundry.applications.ui.Hotbar {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
|
||||
this.setupHooks();
|
||||
}
|
||||
|
||||
static async useItem(uuid) {
|
||||
const item = await fromUuid(uuid);
|
||||
if (!item) {
|
||||
return ui.notifications.warn('WARNING.ObjectDoesNotExist', {
|
||||
format: {
|
||||
name: game.i18n.localize('Document'),
|
||||
identifier: uuid
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
await item.use({});
|
||||
}
|
||||
|
||||
static async useAction(itemUuid, actionId) {
|
||||
const item = await foundry.utils.fromUuid(itemUuid);
|
||||
if (!item) {
|
||||
return ui.notifications.warn('WARNING.ObjectDoesNotExist', {
|
||||
format: {
|
||||
name: game.i18n.localize('Document'),
|
||||
identifier: itemUuid
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const action = item.system.actions.find(x => x.id === actionId);
|
||||
if (!action) {
|
||||
return ui.notifications.warn('DAGGERHEART.UI.Notifications.actionIsMissing');
|
||||
}
|
||||
|
||||
await action.use({});
|
||||
}
|
||||
|
||||
static async useAttack(actorUuid) {
|
||||
const actor = await foundry.utils.fromUuid(actorUuid);
|
||||
if (!actor) {
|
||||
return ui.notifications.warn('WARNING.ObjectDoesNotExist', {
|
||||
format: {
|
||||
name: game.i18n.localize('Document'),
|
||||
identifier: actorUuid
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const attack = actor.system.attack;
|
||||
if (!attack) {
|
||||
return ui.notifications.warn('DAGGERHEART.UI.Notifications.attackIsMissing');
|
||||
}
|
||||
|
||||
await attack.use({});
|
||||
}
|
||||
|
||||
setupHooks() {
|
||||
Hooks.on('hotbarDrop', (bar, data, slot) => {
|
||||
if (data.type === 'Item') {
|
||||
const item = foundry.utils.fromUuidSync(data.uuid);
|
||||
if (item.uuid.startsWith('Compendium') || !item.isOwned || !item.isOwner) return true;
|
||||
|
||||
switch (item.type) {
|
||||
case 'ancestry':
|
||||
case 'community':
|
||||
case 'class':
|
||||
case 'subclass':
|
||||
return true;
|
||||
default:
|
||||
this.createItemMacro(item, slot);
|
||||
return false;
|
||||
}
|
||||
} else if (data.type === 'Action') {
|
||||
const item = foundry.utils.fromUuidSync(data.data.itemUuid);
|
||||
if (item.uuid.startsWith('Compendium')) return true;
|
||||
if (!item.isOwned || !item.isOwner) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.unownedActionMacro'));
|
||||
return false;
|
||||
}
|
||||
|
||||
this.createActionMacro(data, slot);
|
||||
return false;
|
||||
} else if (data.type === 'Attack') {
|
||||
const actor = foundry.utils.fromUuidSync(data.actorUuid);
|
||||
if (actor.uuid.startsWith('Compendium')) return true;
|
||||
if (!actor.isOwner) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.unownedAttackMacro'));
|
||||
return false;
|
||||
}
|
||||
|
||||
this.createAttackMacro(data, slot);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async createItemMacro(data, slot) {
|
||||
const macro = await Macro.implementation.create({
|
||||
name: `${game.i18n.localize('Display')} ${name}`,
|
||||
type: CONST.MACRO_TYPES.SCRIPT,
|
||||
img: data.img,
|
||||
command: `await game.system.api.applications.ui.DhHotbar.useItem("${data.uuid}");`
|
||||
});
|
||||
await game.user.assignHotbarMacro(macro, slot);
|
||||
}
|
||||
|
||||
async createActionMacro(data, slot) {
|
||||
const macro = await Macro.implementation.create({
|
||||
name: `${game.i18n.localize('Display')} ${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}");`
|
||||
});
|
||||
await game.user.assignHotbarMacro(macro, slot);
|
||||
}
|
||||
|
||||
async createAttackMacro(data, slot) {
|
||||
const macro = await Macro.implementation.create({
|
||||
name: `${game.i18n.localize('Display')} ${name}`,
|
||||
type: CONST.MACRO_TYPES.SCRIPT,
|
||||
img: data.img,
|
||||
command: `await game.system.api.applications.ui.DhHotbar.useAttack("${data.actorUuid}");`
|
||||
});
|
||||
await game.user.assignHotbarMacro(macro, slot);
|
||||
}
|
||||
}
|
||||
|
|
@ -411,7 +411,7 @@ export const levelupData = {
|
|||
};
|
||||
|
||||
export const subclassFeatureLabels = {
|
||||
1: 'DAGGERHEART.ITEMS.DomainCard.foundation',
|
||||
1: 'DAGGERHEART.ITEMS.DomainCard.foundationTitle',
|
||||
2: 'DAGGERHEART.ITEMS.DomainCard.specializationTitle',
|
||||
3: 'DAGGERHEART.ITEMS.DomainCard.masteryTitle'
|
||||
};
|
||||
|
|
|
|||
|
|
@ -135,6 +135,7 @@ export const defaultRestOptions = {
|
|||
tendToWounds: {
|
||||
id: 'tendToWounds',
|
||||
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.tendToWounds.name'),
|
||||
icon: 'fa-solid fa-bandage',
|
||||
img: 'icons/magic/life/cross-worn-green.webp',
|
||||
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.tendToWounds.description'),
|
||||
actions: [
|
||||
|
|
@ -158,6 +159,7 @@ export const defaultRestOptions = {
|
|||
clearStress: {
|
||||
id: 'clearStress',
|
||||
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.clearStress.name'),
|
||||
icon: 'fa-regular fa-face-surprise',
|
||||
img: 'icons/magic/perception/eye-ringed-green.webp',
|
||||
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.clearStress.description'),
|
||||
actions: [
|
||||
|
|
@ -181,6 +183,7 @@ export const defaultRestOptions = {
|
|||
repairArmor: {
|
||||
id: 'repairArmor',
|
||||
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.repairArmor.name'),
|
||||
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: []
|
||||
|
|
@ -188,6 +191,7 @@ export const defaultRestOptions = {
|
|||
prepare: {
|
||||
id: 'prepare',
|
||||
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.prepare.name'),
|
||||
icon: 'fa-solid fa-dumbbell',
|
||||
img: 'icons/skills/trades/academics-merchant-scribe.webp',
|
||||
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.prepare.description'),
|
||||
actions: []
|
||||
|
|
@ -197,6 +201,7 @@ export const defaultRestOptions = {
|
|||
tendToWounds: {
|
||||
id: 'tendToWounds',
|
||||
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.tendToWounds.name'),
|
||||
icon: 'fa-solid fa-bandage',
|
||||
img: 'icons/magic/life/cross-worn-green.webp',
|
||||
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.tendToWounds.description'),
|
||||
actions: []
|
||||
|
|
@ -204,6 +209,7 @@ export const defaultRestOptions = {
|
|||
clearStress: {
|
||||
id: 'clearStress',
|
||||
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.clearStress.name'),
|
||||
icon: 'fa-regular fa-face-surprise',
|
||||
img: 'icons/magic/perception/eye-ringed-green.webp',
|
||||
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.clearStress.description'),
|
||||
actions: []
|
||||
|
|
@ -211,6 +217,7 @@ export const defaultRestOptions = {
|
|||
repairArmor: {
|
||||
id: 'repairArmor',
|
||||
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.repairArmor.name'),
|
||||
icon: 'fa-solid fa-hammer',
|
||||
img: 'icons/skills/trades/smithing-anvil-silver-red.webp',
|
||||
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.repairArmor.description'),
|
||||
actions: []
|
||||
|
|
@ -218,6 +225,7 @@ export const defaultRestOptions = {
|
|||
prepare: {
|
||||
id: 'prepare',
|
||||
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.prepare.name'),
|
||||
icon: 'fa-solid fa-dumbbell',
|
||||
img: 'icons/skills/trades/academics-merchant-scribe.webp',
|
||||
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.prepare.description'),
|
||||
actions: []
|
||||
|
|
@ -225,19 +233,12 @@ export const defaultRestOptions = {
|
|||
workOnAProject: {
|
||||
id: 'workOnAProject',
|
||||
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.workOnAProject.name'),
|
||||
icon: 'fa-solid fa-diagram-project',
|
||||
img: 'icons/skills/social/thumbsup-approval-like.webp',
|
||||
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.workOnAProject.description'),
|
||||
actions: []
|
||||
}
|
||||
}),
|
||||
custom: {
|
||||
id: 'customActivity',
|
||||
name: '',
|
||||
img: 'icons/skills/trades/academics-investigation-puzzles.webp',
|
||||
description: '',
|
||||
namePlaceholder: 'DAGGERHEART.APPLICATIONS.Downtime.custom.namePlaceholder',
|
||||
placeholder: 'DAGGERHEART.APPLICATIONS.Downtime.custom.placeholder'
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
export const deathMoves = {
|
||||
|
|
|
|||
|
|
@ -439,7 +439,7 @@ export const weaponFeatures = {
|
|||
{
|
||||
key: 'system.bonuses.damage.primaryWeapon.bonus',
|
||||
mode: 2,
|
||||
value: '@system.levelData.levels.current'
|
||||
value: '@system.levelData.level.current'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1261,15 +1261,6 @@ export const weaponFeatures = {
|
|||
timebending: {
|
||||
label: 'DAGGERHEART.CONFIG.WeaponFeature.timebending.name',
|
||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.timebending.description'
|
||||
},
|
||||
versatile: {
|
||||
label: 'DAGGERHEART.CONFIG.WeaponFeature.versatile.name',
|
||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.versatile.description'
|
||||
// versatile: {
|
||||
// characterTrait: '',
|
||||
// range: '',
|
||||
// damage: ''
|
||||
// }
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -1322,7 +1313,12 @@ export const featureTypes = {
|
|||
|
||||
export const featureSubTypes = {
|
||||
primary: 'primary',
|
||||
secondary: 'secondary'
|
||||
secondary: 'secondary',
|
||||
hope: 'hope',
|
||||
class: 'class',
|
||||
foundation: 'foundation',
|
||||
specialization: 'specialization',
|
||||
mastery: 'mastery'
|
||||
};
|
||||
|
||||
export const actionTypes = {
|
||||
|
|
|
|||
|
|
@ -87,6 +87,8 @@ 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()),
|
||||
levelData: new fields.EmbeddedDataField(DhLevelData),
|
||||
bonuses: new fields.SchemaField({
|
||||
roll: new fields.SchemaField({
|
||||
|
|
@ -102,7 +104,7 @@ export default class DhCharacter extends BaseDataActor {
|
|||
physical: bonusField('DAGGERHEART.GENERAL.Damage.physicalDamage'),
|
||||
magical: bonusField('DAGGERHEART.GENERAL.Damage.magicalDamage'),
|
||||
primaryWeapon: bonusField('DAGGERHEART.GENERAL.Damage.primaryWeapon'),
|
||||
secondaryWeapon: bonusField('DAGGERHEART.GENERAL.Damage.primaryWeapon')
|
||||
secondaryWeapon: bonusField('DAGGERHEART.GENERAL.Damage.secondaryWeapon')
|
||||
}),
|
||||
healing: bonusField('DAGGERHEART.GENERAL.Healing.healingAmount'),
|
||||
range: new fields.SchemaField({
|
||||
|
|
@ -121,6 +123,47 @@ export default class DhCharacter extends BaseDataActor {
|
|||
initial: 0,
|
||||
label: 'DAGGERHEART.GENERAL.Range.other'
|
||||
})
|
||||
}),
|
||||
rally: new fields.ArrayField(new fields.StringField(), {
|
||||
label: 'DAGGERHEART.CLASS.Feature.rallyDice'
|
||||
}),
|
||||
rest: new fields.SchemaField({
|
||||
shortRest: new fields.SchemaField({
|
||||
shortMoves: new fields.NumberField({
|
||||
required: true,
|
||||
integer: true,
|
||||
min: 0,
|
||||
initial: 0,
|
||||
label: 'DAGGERHEART.GENERAL.Bonuses.rest.shortRest.shortRestMoves.label',
|
||||
hint: 'DAGGERHEART.GENERAL.Bonuses.rest.shortRest.shortRestMoves.hint'
|
||||
}),
|
||||
longMoves: new fields.NumberField({
|
||||
required: true,
|
||||
integer: true,
|
||||
min: 0,
|
||||
initial: 0,
|
||||
label: 'DAGGERHEART.GENERAL.Bonuses.rest.shortRest.longRestMoves.label',
|
||||
hint: 'DAGGERHEART.GENERAL.Bonuses.rest.shortRest.longRestMoves.hint'
|
||||
})
|
||||
}),
|
||||
longRest: new fields.SchemaField({
|
||||
shortMoves: new fields.NumberField({
|
||||
required: true,
|
||||
integer: true,
|
||||
min: 0,
|
||||
initial: 0,
|
||||
label: 'DAGGERHEART.GENERAL.Bonuses.rest.longRest.shortRestMoves.label',
|
||||
hint: 'DAGGERHEART.GENERAL.Bonuses.rest.longRest.shortRestMoves.hint'
|
||||
}),
|
||||
longMoves: new fields.NumberField({
|
||||
required: true,
|
||||
integer: true,
|
||||
min: 0,
|
||||
initial: 0,
|
||||
label: 'DAGGERHEART.GENERAL.Bonuses.rest.longRest.longRestMoves.label',
|
||||
hint: 'DAGGERHEART.GENERAL.Bonuses.rest.longRest.longRestMoves.hint'
|
||||
})
|
||||
})
|
||||
})
|
||||
}),
|
||||
companion: new ForeignDocumentUUIDField({ type: 'Actor', nullable: true, initial: null }),
|
||||
|
|
@ -244,23 +287,23 @@ export default class DhCharacter extends BaseDataActor {
|
|||
features = [];
|
||||
|
||||
for (let item of this.parent.items) {
|
||||
if (item.system.type === CONFIG.DH.ITEM.featureTypes.ancestry.id) {
|
||||
if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.ancestry.id) {
|
||||
ancestryFeatures.push(item);
|
||||
} else if (item.system.type === CONFIG.DH.ITEM.featureTypes.community.id) {
|
||||
} else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.community.id) {
|
||||
communityFeatures.push(item);
|
||||
} else if (item.system.type === CONFIG.DH.ITEM.featureTypes.class.id) {
|
||||
} else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.class.id) {
|
||||
classFeatures.push(item);
|
||||
} else if (item.system.type === CONFIG.DH.ITEM.featureTypes.subclass.id) {
|
||||
} else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.subclass.id) {
|
||||
const subclassState = this.class.subclass.system.featureState;
|
||||
const identifier = item.system.identifier;
|
||||
const subType = item.system.subType;
|
||||
if (
|
||||
identifier === 'foundationFeature' ||
|
||||
(identifier === 'specializationFeature' && subclassState >= 2) ||
|
||||
(identifier === 'masterFeature' && subclassState >= 3)
|
||||
subType === CONFIG.DH.ITEM.featureSubTypes.foundation ||
|
||||
(subType === CONFIG.DH.ITEM.featureSubTypes.specialization && subclassState >= 2) ||
|
||||
(subType === CONFIG.DH.ITEM.featureSubTypes.mastery && subclassState >= 3)
|
||||
) {
|
||||
subclassFeatures.push(item);
|
||||
}
|
||||
} else if (item.system.type === CONFIG.DH.ITEM.featureTypes.companion.id) {
|
||||
} else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.companion.id) {
|
||||
companionFeatures.push(item);
|
||||
} else if (item.type === 'feature' && !item.system.type) {
|
||||
features.push(item);
|
||||
|
|
|
|||
|
|
@ -135,8 +135,8 @@ export const registerCountdownHooks = () => {
|
|||
if (application) {
|
||||
foundry.applications.instances.get(application)?.render();
|
||||
} else {
|
||||
foundry.applications.instances.get('narrative-countdowns').render();
|
||||
foundry.applications.instances.get('encounter-countdowns').render();
|
||||
foundry.applications.instances.get('narrative-countdowns')?.render();
|
||||
foundry.applications.instances.get('encounter-countdowns')?.render();
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -25,8 +25,11 @@ const stressDamageReductionRule = localizationPath =>
|
|||
|
||||
const bonusField = label =>
|
||||
new fields.SchemaField({
|
||||
bonus: new fields.NumberField({ integer: true, initial: 0, label }),
|
||||
dice: new fields.ArrayField(new fields.StringField())
|
||||
bonus: new fields.NumberField({ integer: true, initial: 0, label: `${game.i18n.localize(label)} Value` }),
|
||||
dice: new fields.ArrayField(
|
||||
new fields.StringField(),
|
||||
{ label: `${game.i18n.localize(label)} Dice` }
|
||||
)
|
||||
});
|
||||
|
||||
export { attributeField, resourceField, stressDamageReductionRule, bonusField };
|
||||
|
|
|
|||
|
|
@ -53,11 +53,14 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
|||
}),
|
||||
diceStates: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
value: new fields.NumberField({ integer: true, nullable: true, initial: null }),
|
||||
value: new fields.NumberField({ integer: true, initial: 1, min: 1 }),
|
||||
used: new fields.BooleanField({ initial: false })
|
||||
})
|
||||
),
|
||||
dieFaces: new fields.StringField({ initial: '4' })
|
||||
dieFaces: new fields.StringField({
|
||||
choices: CONFIG.DH.GENERAL.diceTypes,
|
||||
initial: CONFIG.DH.GENERAL.diceTypes.d4
|
||||
})
|
||||
},
|
||||
{ nullable: true, initial: null }
|
||||
);
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ 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 }],
|
||||
system: {
|
||||
characterTokenData: {
|
||||
tokenImg: this.parent.parent.prototypeToken.texture.src,
|
||||
|
|
|
|||
|
|
@ -27,8 +27,7 @@ export default class DHClass extends BaseDataItem {
|
|||
label: 'DAGGERHEART.GENERAL.hitPoints.plural'
|
||||
}),
|
||||
evasion: new fields.NumberField({ initial: 0, integer: true, label: 'DAGGERHEART.GENERAL.evasion' }),
|
||||
hopeFeatures: new ForeignDocumentUUIDArrayField({ type: 'Item' }),
|
||||
classFeatures: new ForeignDocumentUUIDArrayField({ type: 'Item' }),
|
||||
features: new ForeignDocumentUUIDArrayField({ type: 'Item' }),
|
||||
subclasses: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false }),
|
||||
inventory: new fields.SchemaField({
|
||||
take: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false }),
|
||||
|
|
@ -52,12 +51,18 @@ export default class DHClass extends BaseDataItem {
|
|||
};
|
||||
}
|
||||
|
||||
get hopeFeature() {
|
||||
return this.hopeFeatures.length > 0 ? this.hopeFeatures[0] : null;
|
||||
get hopeFeatures() {
|
||||
return (
|
||||
this.features.filter(x => x?.system?.subType === CONFIG.DH.ITEM.featureSubTypes.hope) ??
|
||||
(this.features.filter(x => !x).length > 0 ? {} : null)
|
||||
);
|
||||
}
|
||||
|
||||
get features() {
|
||||
return [...this.hopeFeatures.filter(x => x), ...this.classFeatures.filter(x => x)];
|
||||
get classFeatures() {
|
||||
return (
|
||||
this.features.filter(x => x?.system?.subType === CONFIG.DH.ITEM.featureSubTypes.class) ??
|
||||
(this.features.filter(x => !x).length > 0 ? {} : null)
|
||||
);
|
||||
}
|
||||
|
||||
async _preCreate(data, options, user) {
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ export default class DHDomainCard extends BaseDataItem {
|
|||
required: true,
|
||||
initial: CONFIG.DH.DOMAIN.cardTypes.ability.id
|
||||
}),
|
||||
foundation: new fields.BooleanField({ initial: false }),
|
||||
inVault: new fields.BooleanField({ initial: false }),
|
||||
actions: new fields.ArrayField(new ActionField())
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
||||
import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs';
|
||||
import BaseDataItem from './base.mjs';
|
||||
|
||||
export default class DHSubclass extends BaseDataItem {
|
||||
|
|
@ -22,20 +22,22 @@ export default class DHSubclass extends BaseDataItem {
|
|||
nullable: true,
|
||||
initial: null
|
||||
}),
|
||||
foundationFeature: new ForeignDocumentUUIDField({ type: 'Item' }),
|
||||
specializationFeature: new ForeignDocumentUUIDField({ type: 'Item' }),
|
||||
masteryFeature: new ForeignDocumentUUIDField({ type: 'Item' }),
|
||||
features: new ForeignDocumentUUIDArrayField({ type: 'Item' }),
|
||||
featureState: new fields.NumberField({ required: true, initial: 1, min: 1 }),
|
||||
isMulticlass: new fields.BooleanField({ initial: false })
|
||||
};
|
||||
}
|
||||
|
||||
get features() {
|
||||
return [
|
||||
{ ...this.foundationFeature?.toObject(), identifier: 'foundationFeature' },
|
||||
{ ...this.specializationFeature?.toObject(), identifier: 'specializationFeature' },
|
||||
{ ...this.masteryFeature?.toObject(), identifier: 'masteryFeature' }
|
||||
];
|
||||
get foundationFeatures() {
|
||||
return this.features.filter(x => x.system.subType === CONFIG.DH.ITEM.featureSubTypes.foundation);
|
||||
}
|
||||
|
||||
get specializationFeatures() {
|
||||
return this.features.filter(x => x.system.subType === CONFIG.DH.ITEM.featureSubTypes.specialization);
|
||||
}
|
||||
|
||||
get masteryFeatures() {
|
||||
return this.features.filter(x => x.system.subType === CONFIG.DH.ITEM.featureSubTypes.mastery);
|
||||
}
|
||||
|
||||
async _preCreate(data, options, user) {
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
|
|||
moves: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true }),
|
||||
icon: new fields.StringField({ required: true }),
|
||||
img: new fields.FilePathField({
|
||||
initial: 'icons/magic/life/cross-worn-green.webp',
|
||||
categories: ['IMAGE'],
|
||||
|
|
@ -70,6 +71,7 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
|
|||
moves: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true }),
|
||||
icon: new fields.StringField({ required: true }),
|
||||
img: new fields.FilePathField({
|
||||
initial: 'icons/magic/life/cross-worn-green.webp',
|
||||
categories: ['IMAGE'],
|
||||
|
|
|
|||
|
|
@ -39,11 +39,13 @@ export default class D20Roll extends DHRoll {
|
|||
}
|
||||
|
||||
get hasAdvantage() {
|
||||
return this.options.roll.advantage === this.constructor.ADV_MODE.ADVANTAGE;
|
||||
const adv = this.options.roll.advantage.type ?? this.options.roll.advantage;
|
||||
return adv === this.constructor.ADV_MODE.ADVANTAGE;
|
||||
}
|
||||
|
||||
get hasDisadvantage() {
|
||||
return this.options.roll.advantage === this.constructor.ADV_MODE.DISADVANTAGE;
|
||||
const adv = this.options.roll.advantage.type ?? this.options.roll.advantage;
|
||||
return adv === this.constructor.ADV_MODE.DISADVANTAGE;
|
||||
}
|
||||
|
||||
static applyKeybindings(config) {
|
||||
|
|
@ -91,7 +93,7 @@ export default class D20Roll extends DHRoll {
|
|||
configureModifiers() {
|
||||
this.applyAdvantage();
|
||||
|
||||
this.baseTerms = foundry.utils.deepClone(this.terms);
|
||||
this.baseTerms = foundry.utils.deepClone(this.dice);
|
||||
|
||||
this.options.roll.modifiers = this.applyBaseBonus();
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,12 @@ import { setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs';
|
|||
|
||||
export default class DualityRoll extends D20Roll {
|
||||
_advantageFaces = 6;
|
||||
_advantageNumber = 1;
|
||||
_rallyIndex;
|
||||
|
||||
constructor(formula, data = {}, options = {}) {
|
||||
super(formula, data, options);
|
||||
this.rallyChoices = this.setRallyChoices();
|
||||
}
|
||||
|
||||
static messageType = 'dualityRoll';
|
||||
|
|
@ -51,6 +54,35 @@ export default class DualityRoll extends D20Roll {
|
|||
this._advantageFaces = this.getFaces(faces);
|
||||
}
|
||||
|
||||
get advantageNumber() {
|
||||
return this._advantageNumber;
|
||||
}
|
||||
|
||||
set advantageNumber(value) {
|
||||
this._advantageNumber = Number(value);
|
||||
}
|
||||
|
||||
setRallyChoices() {
|
||||
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 });
|
||||
return a;
|
||||
}, []);
|
||||
}
|
||||
|
||||
get dRally() {
|
||||
if(!this.rallyFaces) return null;
|
||||
if(this.hasDisadvantage || this.hasAdvantage)
|
||||
return this.dice[3];
|
||||
else
|
||||
return this.dice[2];
|
||||
}
|
||||
|
||||
get rallyFaces() {
|
||||
const rallyChoice = this.rallyChoices?.find(r => r.value === this._rallyIndex)?.label;
|
||||
return rallyChoice ? this.getFaces(rallyChoice) : null;
|
||||
}
|
||||
|
||||
get isCritical() {
|
||||
if (!this.dHope._evaluated || !this.dFear._evaluated) return;
|
||||
return this.dHope.total === this.dFear.total;
|
||||
|
|
@ -66,10 +98,6 @@ export default class DualityRoll extends D20Roll {
|
|||
return this.dHope.total < this.dFear.total;
|
||||
}
|
||||
|
||||
get hasBarRally() {
|
||||
return null;
|
||||
}
|
||||
|
||||
get totalLabel() {
|
||||
const label = this.withHope
|
||||
? 'DAGGERHEART.GENERAL.hope'
|
||||
|
|
@ -98,24 +126,20 @@ export default class DualityRoll extends D20Roll {
|
|||
}
|
||||
|
||||
applyAdvantage() {
|
||||
const dieFaces = this.advantageFaces,
|
||||
bardRallyFaces = this.hasBarRally,
|
||||
advDie = new foundry.dice.terms.Die({ faces: dieFaces });
|
||||
if (this.hasAdvantage || this.hasDisadvantage || bardRallyFaces)
|
||||
this.terms.push(new foundry.dice.terms.OperatorTerm({ operator: this.hasDisadvantage ? '-' : '+' }));
|
||||
if (bardRallyFaces) {
|
||||
const rallyDie = new foundry.dice.terms.Die({ faces: bardRallyFaces });
|
||||
if (this.hasAdvantage) {
|
||||
this.terms.push(
|
||||
new foundry.dice.terms.PoolTerm({
|
||||
terms: [advDie.formula, rallyDie.formula],
|
||||
modifiers: ['kh']
|
||||
})
|
||||
);
|
||||
} else if (this.hasDisadvantage) {
|
||||
this.terms.push(advDie, new foundry.dice.terms.OperatorTerm({ operator: '+' }), rallyDie);
|
||||
}
|
||||
} else if (this.hasAdvantage || this.hasDisadvantage) this.terms.push(advDie);
|
||||
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'];
|
||||
this.terms.push(
|
||||
new foundry.dice.terms.OperatorTerm({ operator: this.hasDisadvantage ? '-' : '+' }),
|
||||
advDie
|
||||
);
|
||||
}
|
||||
if(this.rallyFaces)
|
||||
this.terms.push(
|
||||
new foundry.dice.terms.OperatorTerm({ operator: this.hasDisadvantage ? '-' : '+' }),
|
||||
new foundry.dice.terms.Die({ faces: this.rallyFaces })
|
||||
);
|
||||
}
|
||||
|
||||
applyBaseBonus() {
|
||||
|
|
@ -138,6 +162,7 @@ export default class DualityRoll extends D20Roll {
|
|||
|
||||
static postEvaluate(roll, config = {}) {
|
||||
super.postEvaluate(roll, config);
|
||||
|
||||
config.roll.hope = {
|
||||
dice: roll.dHope.denomination,
|
||||
value: roll.dHope.total
|
||||
|
|
@ -146,12 +171,19 @@ export default class DualityRoll extends D20Roll {
|
|||
dice: roll.dFear.denomination,
|
||||
value: roll.dFear.total
|
||||
};
|
||||
config.roll.rally = {
|
||||
dice: roll.dRally?.denomination,
|
||||
value: roll.dRally?.total
|
||||
};
|
||||
config.roll.result = {
|
||||
duality: roll.withHope ? 1 : roll.withFear ? -1 : 0,
|
||||
total: roll.dHope.total + roll.dFear.total,
|
||||
label: roll.totalLabel
|
||||
};
|
||||
|
||||
if(roll._rallyIndex && roll.data?.parent)
|
||||
roll.data.parent.deleteEmbeddedDocuments('ActiveEffect', [roll._rallyIndex]);
|
||||
|
||||
setDiceSoNiceForDualityRoll(roll, config.roll.advantage.type);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,10 +55,24 @@ export default class DhActiveEffect extends ActiveEffect {
|
|||
}
|
||||
|
||||
static applyField(model, change, field) {
|
||||
change.value = itemAbleRollParse(change.value, model, change.effect.parent);
|
||||
change.value = this.effectSafeEval(itemAbleRollParse(change.value, model, change.effect.parent));
|
||||
super.applyField(model, change, field);
|
||||
}
|
||||
|
||||
/* Altered Foundry safeEval to allow non-numeric returns */
|
||||
static effectSafeEval(expression) {
|
||||
let result;
|
||||
try {
|
||||
// eslint-disable-next-line no-new-func
|
||||
const evl = new Function('sandbox', `with (sandbox) { return ${expression}}`);
|
||||
result = evl(Roll.MATH_PROXY);
|
||||
} catch (err) {
|
||||
return expression;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async toChat(origin) {
|
||||
const cls = getDocumentClass('ChatMessage');
|
||||
const systemData = {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ export default class DHToken extends TokenDocument {
|
|||
});
|
||||
bars.sort((a, b) => a.label.compare(b.label));
|
||||
|
||||
const invalidAttributes = ['gold', 'levelData', 'rules.damageReduction.maxArmorMarked.value'];
|
||||
const invalidAttributes = ['gold', 'levelData', 'actions', 'rules.damageReduction.maxArmorMarked.value'];
|
||||
const values = attributes.value.reduce((acc, v) => {
|
||||
const a = v.join('.');
|
||||
if (invalidAttributes.some(x => a.startsWith(x))) return acc;
|
||||
|
|
@ -32,4 +32,26 @@ export default class DHToken extends TokenDocument {
|
|||
|
||||
return bars.concat(values);
|
||||
}
|
||||
|
||||
static _getTrackedAttributesFromSchema(schema, _path = []) {
|
||||
const attributes = { bar: [], value: [] };
|
||||
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.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');
|
||||
if (isBar) attributes.bar.push(p);
|
||||
else {
|
||||
const inner = this.getTrackedAttributes(schema, p);
|
||||
attributes.bar.push(...inner.bar);
|
||||
attributes.value.push(...inner.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return attributes;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,14 +21,79 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
|
|||
this.tooltip.innerHTML = html;
|
||||
options.direction = this._determineItemTooltipDirection(element);
|
||||
}
|
||||
} else {
|
||||
const shortRest = element.dataset.tooltip?.startsWith('#shortRest#');
|
||||
const longRest = element.dataset.tooltip?.startsWith('#longRest#');
|
||||
if (shortRest || longRest) {
|
||||
const key = element.dataset.tooltip.slice(shortRest ? 11 : 10);
|
||||
const downtimeOptions = shortRest
|
||||
? CONFIG.DH.GENERAL.defaultRestOptions.shortRest()
|
||||
: CONFIG.DH.GENERAL.defaultRestOptions.longRest();
|
||||
const move = downtimeOptions[key];
|
||||
html = await foundry.applications.handlebars.renderTemplate(
|
||||
`systems/daggerheart/templates/ui/tooltip/downtime.hbs`,
|
||||
{
|
||||
move: move
|
||||
}
|
||||
);
|
||||
|
||||
this.tooltip.innerHTML = html;
|
||||
options.direction = this._determineItemTooltipDirection(
|
||||
element,
|
||||
this.constructor.TOOLTIP_DIRECTIONS.UP
|
||||
);
|
||||
}
|
||||
|
||||
const isAdvantage = element.dataset.tooltip?.startsWith('#advantage#');
|
||||
const isDisadvantage = element.dataset.tooltip?.startsWith('#disadvantage#');
|
||||
if (isAdvantage || isDisadvantage) {
|
||||
const actorUuid = element.dataset.tooltip.slice(isAdvantage ? 11 : 14);
|
||||
const actor = await foundry.utils.fromUuid(actorUuid);
|
||||
|
||||
if (actor) {
|
||||
html = await foundry.applications.handlebars.renderTemplate(
|
||||
`systems/daggerheart/templates/ui/tooltip/advantage.hbs`,
|
||||
{
|
||||
sources: isAdvantage ? actor.system.advantageSources : actor.system.disadvantageSources
|
||||
}
|
||||
);
|
||||
|
||||
this.tooltip.innerHTML = html;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.activate(element, { ...options, html: html });
|
||||
}
|
||||
|
||||
_determineItemTooltipDirection(element) {
|
||||
_determineItemTooltipDirection(element, prefered = this.constructor.TOOLTIP_DIRECTIONS.LEFT) {
|
||||
const pos = element.getBoundingClientRect();
|
||||
const dirs = this.constructor.TOOLTIP_DIRECTIONS;
|
||||
return dirs[pos.x - this.tooltip.offsetWidth < 0 ? 'DOWN' : 'LEFT'];
|
||||
switch (prefered) {
|
||||
case this.constructor.TOOLTIP_DIRECTIONS.LEFT:
|
||||
return dirs[
|
||||
pos.x - this.tooltip.offsetWidth < 0
|
||||
? this.constructor.TOOLTIP_DIRECTIONS.DOWN
|
||||
: this.constructor.TOOLTIP_DIRECTIONS.LEFT
|
||||
];
|
||||
case this.constructor.TOOLTIP_DIRECTIONS.UP:
|
||||
return dirs[
|
||||
pos.y - this.tooltip.offsetHeight < 0
|
||||
? this.constructor.TOOLTIP_DIRECTIONS.RIGHT
|
||||
: this.constructor.TOOLTIP_DIRECTIONS.UP
|
||||
];
|
||||
case this.constructor.TOOLTIP_DIRECTIONS.RIGHT:
|
||||
return dirs[
|
||||
pos.x + this.tooltip.offsetWidth > document.body.clientWidth
|
||||
? this.constructor.TOOLTIP_DIRECTIONS.DOWN
|
||||
: this.constructor.TOOLTIP_DIRECTIONS.RIGHT
|
||||
];
|
||||
case this.constructor.TOOLTIP_DIRECTIONS.DOWN:
|
||||
return dirs[
|
||||
pos.y + this.tooltip.offsetHeight > document.body.clientHeight
|
||||
? this.constructor.TOOLTIP_DIRECTIONS.LEFT
|
||||
: this.constructor.TOOLTIP_DIRECTIONS.DOWN
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,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/tooltipTags.hbs'
|
||||
'systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs',
|
||||
'systems/daggerheart/templates/dialogs/downtime/activities.hbs'
|
||||
]);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -100,6 +100,10 @@
|
|||
font-size: 14px;
|
||||
line-height: 17px;
|
||||
}
|
||||
|
||||
.advantage-chip-tooltip {
|
||||
pointer-events: all;
|
||||
}
|
||||
}
|
||||
|
||||
.advantage-chip {
|
||||
|
|
|
|||
|
|
@ -1,81 +1,75 @@
|
|||
@import '../../utils/spacing.less';
|
||||
@import '../../utils/colors.less';
|
||||
|
||||
.daggerheart.views {
|
||||
.theme-light .daggerheart.dh-style.views.downtime {
|
||||
.downtime-container .activity-container .activity-selected-marker {
|
||||
background-image: url(../assets/parchments/dh-parchment-light.png);
|
||||
}
|
||||
}
|
||||
|
||||
.daggerheart.dh-style.views.downtime {
|
||||
font-family: @font-body;
|
||||
|
||||
.downtime-container {
|
||||
.downtime-header {
|
||||
margin: 0;
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
text-align: center;
|
||||
.activities-grouping {
|
||||
width: 280px;
|
||||
}
|
||||
|
||||
.activity-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px;
|
||||
.activities-container {
|
||||
width: 100%;
|
||||
|
||||
.activity-title {
|
||||
flex: 1;
|
||||
.activity-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 8px;
|
||||
|
||||
.activity-title-text {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.activity-image {
|
||||
width: 80px;
|
||||
position: relative;
|
||||
.activity-inner-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-right: 8px;
|
||||
border: 2px solid black;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
|
||||
.activity-select-label {
|
||||
position: absolute;
|
||||
top: -9px;
|
||||
font-size: 14px;
|
||||
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);
|
||||
padding: 0 8px;
|
||||
line-height: 1;
|
||||
font-weight: bold;
|
||||
.activity-marker {
|
||||
font-size: 8px;
|
||||
flex: none;
|
||||
color: light-dark(#18162e, #f3c267);
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
img {
|
||||
border-radius: 50%;
|
||||
}
|
||||
.activity-select-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
|
||||
&:hover,
|
||||
&.selected {
|
||||
filter: drop-shadow(0 0 6px gold);
|
||||
.activity-icon {
|
||||
min-width: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.custom-name-input {
|
||||
font-size: 24px;
|
||||
.activity-selected-marker {
|
||||
font-size: 14px;
|
||||
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);
|
||||
padding: 0 8px;
|
||||
line-height: 1;
|
||||
font-weight: bold;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
color: rgb(239, 230, 216);
|
||||
}
|
||||
}
|
||||
|
||||
.activity-body {
|
||||
flex: 1;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.downtime {
|
||||
.activity-text-area {
|
||||
resize: none;
|
||||
footer {
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
||||
button {
|
||||
flex: 1;
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,12 +7,22 @@
|
|||
height: 40px;
|
||||
width: 100%;
|
||||
|
||||
.feature-tab {
|
||||
border: none;
|
||||
.navigation-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
a {
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
font-family: @font-body;
|
||||
.navigation-inner-container {
|
||||
flex: 1;
|
||||
|
||||
.feature-tab {
|
||||
border: none;
|
||||
|
||||
a {
|
||||
color: light-dark(@dark-blue, @golden);
|
||||
font-family: @font-body;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -193,5 +193,14 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.character-downtime-container {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
|
||||
button {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
border-color: light-dark(@dark, @beige);
|
||||
border-radius: 6px;
|
||||
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||
z-index: 200;
|
||||
max-height: 400px !important;
|
||||
width: fit-content !important;
|
||||
overflow-y: auto;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,20 @@
|
|||
align-items: center;
|
||||
gap: 4px;
|
||||
|
||||
.tooltip-title-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
|
||||
.tooltip-image {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid @golden;
|
||||
}
|
||||
}
|
||||
|
||||
.tooltip-title {
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
|
|
|
|||
|
|
@ -3,14 +3,14 @@
|
|||
{{#times (rollParsed item.system.resource.max actor item numerical=true)}}
|
||||
{{#with (ifThen (lookup ../diceStates this) (lookup ../diceStates this) this) as | state |}}
|
||||
<div class="resource-item" data-dice="{{#if ../../this}}{{../this}}{{else}}{{state}}{{/if}}">
|
||||
<input type="number" data-dtype="Number" name={{concat "diceStates." (ifThen ../../this ../this state) ".value" }} value="{{state.value}}" />
|
||||
<img src="{{concat "systems/daggerheart/assets/icons/dice/hope/d" (ifThen ../../item.system.resource.dieFaces ../../item.system.resource.dieFaces ../item.system.resource.dieFaces) ".svg"}}" />
|
||||
<input type="number" data-dtype="Number" name={{concat "diceStates." (ifThen ../../this ../this state) ".value" }} value="{{state.value}}" min="1" />
|
||||
<img src="{{concat "systems/daggerheart/assets/icons/dice/hope/" (ifThen ../../item.system.resource.dieFaces ../../item.system.resource.dieFaces ../item.system.resource.dieFaces) ".svg"}}" />
|
||||
</div>
|
||||
{{/with}}
|
||||
{{/times}}
|
||||
</div>
|
||||
<footer>
|
||||
<button data-action="save">{{localize 'Save'}}</button>
|
||||
<button data-action="rerollDice">{{localize "DAGGERHEART.APPLICATIONS.ResourceDice.rerollDice"}}</button>
|
||||
<button type="button" data-action="save">{{localize 'Save'}}</button>
|
||||
<button type="button" data-action="rerollDice">{{localize "DAGGERHEART.APPLICATIONS.ResourceDice.rerollDice"}}</button>
|
||||
</footer>
|
||||
</section>
|
||||
|
|
@ -88,7 +88,7 @@
|
|||
{{/if}}
|
||||
{{/each}}
|
||||
</fieldset>
|
||||
<fieldset class="modifier-container one-column">
|
||||
<fieldset class="modifier-container {{#if (eq @root.rollType 'DualityRoll')}}two-columns{{else}}one-column{{/if}}">
|
||||
<legend>Modifiers</legend>
|
||||
<div class="nest-inputs">
|
||||
<button class="advantage-chip flex1 {{#if (eq advantage 1)}}selected{{/if}}" data-action="updateIsAdvantage" data-advantage="1">
|
||||
|
|
@ -98,6 +98,9 @@
|
|||
<span><i class="fa-regular fa-circle"></i></span>
|
||||
{{/if}}
|
||||
<span class="label">{{localize "DAGGERHEART.GENERAL.Advantage.full"}}</span>
|
||||
{{#if @root.rollConfig.data.advantageSources.length}}
|
||||
<span class="advantage-chip-tooltip" data-tooltip="{{concat "#advantage#" @root.rollConfig.source.actor}}"><i class="fa-solid fa-circle-info"></i></span>
|
||||
{{/if}}
|
||||
</button>
|
||||
<button class="disadvantage-chip flex1 {{#if (eq advantage -1)}}selected{{/if}}" data-action="updateIsAdvantage" data-advantage="-1">
|
||||
{{#if (eq advantage -1)}}
|
||||
|
|
@ -106,14 +109,31 @@
|
|||
<span><i class="fa-regular fa-circle"></i></span>
|
||||
{{/if}}
|
||||
<span class="label">{{localize "DAGGERHEART.GENERAL.Disadvantage.full"}}</span>
|
||||
{{#if @root.rollConfig.data.disadvantageSources.length}}
|
||||
<span class="advantage-chip-tooltip" data-tooltip="{{concat "#disadvantage#" @root.rollConfig.source.actor}}"><i class="fa-solid fa-circle-info"></i></span>
|
||||
{{/if}}
|
||||
</button>
|
||||
{{#unless (eq @root.rollType 'D20Roll')}}
|
||||
<select name="roll.dice.advantageFaces">
|
||||
</div>
|
||||
{{#unless (eq @root.rollType 'D20Roll')}}
|
||||
<div class="nest-inputs">
|
||||
<select name="roll.dice.advantageNumber"{{#unless advantage}} disabled{{/unless}}>
|
||||
{{#times 10}}
|
||||
<option value="{{add this 1}}" {{#if (eq @root.roll.advantageNumber (add this 1))}} selected{{/if}}>{{add this 1}}</option>
|
||||
{{/times}}
|
||||
</select>
|
||||
<select name="roll.dice.advantageFaces"{{#unless advantage}} disabled{{/unless}}>
|
||||
{{selectOptions diceOptions selected=@root.roll.dAdvantage.denomination}}
|
||||
</select>
|
||||
{{/unless}}
|
||||
</div>
|
||||
<input type="text" value="{{extraFormula}}" name="extraFormula" placeholder="Situational Bonus">
|
||||
</div>
|
||||
{{/unless}}
|
||||
{{#if @root.rallyDie.length}}
|
||||
<span class="formula-label">{{localize "DAGGERHEART.CLASS.Feature.rallyDice"}}</span>
|
||||
<select name="roll.dice._rallyIndex">
|
||||
{{selectOptions @root.rallyDie blank="" selected=@root.roll._rallyIndex}}
|
||||
</select>
|
||||
{{/if}}
|
||||
{{#if (eq @root.rollType 'DualityRoll')}}<span class="formula-label">Situational Bonus</span>{{/if}}
|
||||
<input type="text" value="{{extraFormula}}" name="extraFormula" placeholder="{{#if (eq @root.rollType 'DualityRoll')}}Ex: 1d6 + 5{{else}}Situational Bonus{{/if}}">
|
||||
</fieldset>
|
||||
{{/unless}}
|
||||
<span class="formula-label"><b>Formula:</b> {{@root.formula}}</span>
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
<div>
|
||||
<div class="downtime-container">
|
||||
<h2 class="downtime-header">{{localize "DAGGERHEART.APPLICATIONS.Downtime.downtimeHeader" current=nrCurrentChoices max=moveData.nrChoices}}</h2>
|
||||
{{#each moveData.moves as |move key|}}
|
||||
<div class="activity-container">
|
||||
<div class="activity-title">
|
||||
<div class="activity-image {{#if this.selected}}selected{{/if}}" data-action="selectMove" data-move="{{key}}">
|
||||
{{#if this.selected}}<div class="activity-select-label">{{move.selected}}</div>{{/if}}
|
||||
<img src="{{move.img}}" />
|
||||
</div>
|
||||
|
||||
<span class="activity-title-text">{{localize this.name}}</span>
|
||||
</div>
|
||||
<div class="activity-body">
|
||||
{{localize this.description}}
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
<footer class="flexrow">
|
||||
<button type="button" data-action="close">{{localize "DAGGERHEART.Application.Cancel"}}</button>
|
||||
<button type="button" data-action="takeDowntime" {{#if this.disabledDowntime}}disabled{{/if}}>{{localize "DAGGERHEART.Application.Downtime.TakeDowntime"}}</button>
|
||||
</footer>
|
||||
</div>
|
||||
18
templates/dialogs/downtime/activities.hbs
Normal file
18
templates/dialogs/downtime/activities.hbs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<fieldset class="one-column activities-grouping">
|
||||
<legend>{{localize (concat "DAGGERHEART.APPLICATIONS.Downtime." category ".moves") max=nrChoices.max current=nrChoices.current}}</legend>
|
||||
|
||||
<div class="activities-container">
|
||||
{{#each moves as |move key|}}
|
||||
<a class="activity-container" data-action="selectMove" data-category="{{../category}}" data-move="{{key}}" data-tooltip="{{concat "#" ../category "#" key}}">
|
||||
<div class="activity-inner-container">
|
||||
<i class="{{#if move.selected}}fa-solid{{else}}fa-regular{{/if}} fa-circle activity-marker"></i>
|
||||
<div class="activity-select-section">
|
||||
<i class="{{move.icon}} activity-icon"></i>
|
||||
<div>{{move.name}}</div>
|
||||
</div>
|
||||
</div>
|
||||
{{#if move.selected}}<div class="activity-selected-marker">{{move.selected}}</div>{{/if}}
|
||||
</a>
|
||||
{{/each}}
|
||||
</div>
|
||||
</fieldset>
|
||||
14
templates/dialogs/downtime/downtime.hbs
Normal file
14
templates/dialogs/downtime/downtime.hbs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<div>
|
||||
<header class="dialog-header">
|
||||
<h1>{{title}}</h1>
|
||||
</header>
|
||||
|
||||
<div class="downtime-container {{#if (and shortRestMoves.moves longRestMoves.moves)}}two-columns even{{else}}one-column{{/if}}">
|
||||
{{#if shortRestMoves.moves}}{{> "systems/daggerheart/templates/dialogs/downtime/activities.hbs" moves=shortRestMoves.moves category='shortRest' nrChoices=nrChoices.shortRest}}{{/if}}
|
||||
{{#if longRestMoves.moves}}{{> "systems/daggerheart/templates/dialogs/downtime/activities.hbs" moves=longRestMoves.moves category='longRest' nrChoices=nrChoices.longRest}}{{/if}}
|
||||
</div>
|
||||
<footer>
|
||||
<button type="button" data-action="close">{{localize "Cancel"}}</button>
|
||||
<button type="button" data-action="takeDowntime" {{#if disabledDowntime}}disabled{{/if}}>{{localize "DAGGERHEART.APPLICATIONS.Downtime.takeDowntime"}}</button>
|
||||
</footer>
|
||||
</div>
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
</div>
|
||||
{{formGroup settingFields.schema.fields.actionPoints value=settingFields._source.actionPoints localize=true}}
|
||||
s {{formGroup settingFields.schema.fields.hordeDamage value=settingFields._source.hordeDamage localize=true}}
|
||||
{{formGroup settingFields.schema.fields.hordeDamage value=settingFields._source.hordeDamage localize=true}}
|
||||
|
||||
<footer class="form-footer">
|
||||
<button data-action="reset">
|
||||
|
|
|
|||
|
|
@ -1,18 +1,17 @@
|
|||
<div>
|
||||
<div class="form-group">
|
||||
<label>{{localize "Icon"}}</label>
|
||||
|
||||
<div class="form-field">
|
||||
<input type="text" name="icon" value="{{icon}}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<fieldset>
|
||||
<legend>{{localize "Description"}}</legend>
|
||||
|
||||
<prose-mirror name="description" value="{{description}}" toggled=true>
|
||||
<prose-mirror name="description" value="{{description}}">
|
||||
{{{ enrichedDescription }}}
|
||||
</prose-mirror>
|
||||
</fieldset>
|
||||
{{!-- <fieldset>
|
||||
<legend>{{localize "Actions"}} <a data-action="addItem"><i class="fa-solid fa-plus"></i></a></legend>
|
||||
|
||||
<div class="settings-items">
|
||||
{{#each this.actions as |action index|}}
|
||||
{{> "systems/daggerheart/templates/settings/components/settings-item-line.hbs" action type="actions" id=index }}
|
||||
{{/each}}
|
||||
</div>
|
||||
</fieldset> --}}
|
||||
</div>
|
||||
|
|
@ -116,5 +116,14 @@
|
|||
{{/each}}
|
||||
</div>
|
||||
|
||||
{{> 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs'}}
|
||||
{{#> 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs'}}
|
||||
<div class="character-downtime-container">
|
||||
<button data-action="useDowntime" data-type="shortRest" data-tooltip="{{localize "DAGGERHEART.APPLICATIONS.Downtime.shortRest.title"}}">
|
||||
<i class="fa-solid fa-chair"></i>
|
||||
</button>
|
||||
<button data-action="useDowntime" data-type="longRest" data-tooltip="{{localize "DAGGERHEART.APPLICATIONS.Downtime.longRest.title"}}">
|
||||
<i class="fa-solid fa-bed"></i>
|
||||
</button>
|
||||
</div>
|
||||
{{/'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs'}}
|
||||
</header>
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
<a class="item-resource" data-action="toggleResourceDice" data-dice="{{#if ../../this}}{{../this}}{{else}}{{state}}{{/if}}">
|
||||
<div class="item-dice-resource">
|
||||
<label>{{ifThen state.value state.value '?'}}</label>
|
||||
<img src="{{concat "systems/daggerheart/assets/icons/dice/hope/d" (ifThen ../../item.system.resource.dieFaces ../../item.system.resource.dieFaces ../item.system.resource.dieFaces) ".svg"}}" />
|
||||
<img src="{{concat "systems/daggerheart/assets/icons/dice/hope/" (ifThen ../../item.system.resource.dieFaces ../../item.system.resource.dieFaces ../item.system.resource.dieFaces) ".svg"}}" />
|
||||
{{#if state.used}}<i class="fa-solid fa-x"></i>{{/if}}
|
||||
</div>
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
{{formGroup systemFields.resource.fields.value value=source.system.resource.value localize=true}}
|
||||
{{formGroup systemFields.resource.fields.max value=source.system.resource.max localize=true}}
|
||||
{{else}}
|
||||
{{formGroup systemFields.resource.fields.dieFaces value=source.system.resource.dieFaces localize=true}}
|
||||
{{formGroup systemFields.resource.fields.dieFaces value=source.system.resource.dieFaces localize=true blank=false}}
|
||||
{{formGroup systemFields.resource.fields.max value=source.system.resource.max label="DAGGERHEART.ITEMS.FIELDS.resource.amount.label" localize=true}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,18 @@
|
|||
<section class='tab-navigation'>
|
||||
<line-div></line-div>
|
||||
<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>
|
||||
<line-div></line-div>
|
||||
<div class='navigation-container'>
|
||||
<div class="navigation-inner-container">
|
||||
<line-div></line-div>
|
||||
<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>
|
||||
<line-div></line-div>
|
||||
</div>
|
||||
{{#if @partial-block}}
|
||||
{{> @partial-block }}
|
||||
{{/if}}
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -8,8 +8,6 @@
|
|||
|
||||
<span>{{localize "DAGGERHEART.GENERAL.type"}}</span>
|
||||
{{formField systemFields.type value=source.system.type localize=true}}
|
||||
<span>{{localize "DAGGERHEART.ITEMS.DomainCard.foundation"}}</span>
|
||||
{{formField systemFields.foundation value=source.system.foundation }}
|
||||
<span>{{localize "DAGGERHEART.GENERAL.Domain.single"}}</span>
|
||||
{{formField systemFields.domain value=source.system.domain localize=true}}
|
||||
<span>{{localize "DAGGERHEART.GENERAL.level"}}</span>
|
||||
|
|
|
|||
|
|
@ -3,42 +3,42 @@
|
|||
data-tab='{{tabs.features.id}}'
|
||||
data-group='{{tabs.features.group}}'
|
||||
>
|
||||
<fieldset class="drop-section" data-type="foundationFeature">
|
||||
<fieldset class="drop-section" data-type="foundation">
|
||||
<legend>
|
||||
{{localize "DAGGERHEART.GENERAL.Tabs.foundation"}}
|
||||
<a {{#if source.system.foundationFeature}}disabled{{/if}}><i data-action="addFeature" data-type="foundationFeature" class="fa-solid fa-plus icon-button {{#if source.system.foundationFeature}}disabled{{/if}}"></i></a>
|
||||
<a><i data-action="addFeature" data-type="foundation" class="fa-solid fa-plus icon-button"></i></a>
|
||||
</legend>
|
||||
|
||||
<div class="feature-list">
|
||||
{{#if source.system.foundationFeature}}
|
||||
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' type='foundationFeature' feature=source.system.foundationFeature}}
|
||||
{{/if}}
|
||||
{{#each source.system.foundationFeatures as | feature | }}
|
||||
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' type='foundation' feature=feature}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="drop-section" data-type="specializationFeature">
|
||||
<fieldset class="drop-section" data-type="specialization">
|
||||
<legend>
|
||||
{{localize "DAGGERHEART.GENERAL.Tabs.specialization"}}
|
||||
<a {{#if source.system.specializationFeature}}disabled{{/if}}><i data-action="addFeature" data-type="specializationFeature" class="fa-solid fa-plus icon-button {{#if source.system.specializationFeature}}disabled{{/if}}"></i></a>
|
||||
<a><i data-action="addFeature" data-type="specialization" class="fa-solid fa-plus icon-button"></i></a>
|
||||
</legend>
|
||||
|
||||
<div class="feature-list">
|
||||
{{#if source.system.specializationFeature}}
|
||||
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' type='specializationFeature' feature=source.system.specializationFeature}}
|
||||
{{/if}}
|
||||
{{#each source.system.specializationFeatures as | feature |}}
|
||||
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' type='specialization' feature=feature}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="drop-section" data-type="masteryFeature">
|
||||
<fieldset class="drop-section" data-type="mastery">
|
||||
<legend>
|
||||
{{localize "DAGGERHEART.GENERAL.Tabs.mastery"}}
|
||||
<a {{#if source.system.masteryFeature}}disabled{{/if}}><i data-action="addFeature" data-type="masteryFeature" class="fa-solid fa-plus icon-button {{#if source.system.masteryFeature}}disabled{{/if}}"></i></a>
|
||||
<a><i data-action="addFeature" data-type="mastery" class="fa-solid fa-plus icon-button"></i></a>
|
||||
</legend>
|
||||
|
||||
<div class="feature-list">
|
||||
{{#if source.system.masteryFeature}}
|
||||
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' type='masteryFeature' feature=source.system.masteryFeature}}
|
||||
{{/if}}
|
||||
{{#each source.system.masteryFeatures as | feature |}}
|
||||
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' type='mastery' feature=feature}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</fieldset>
|
||||
</section>
|
||||
|
|
@ -16,6 +16,11 @@
|
|||
{{localize "DAGGERHEART.GENERAL.Disadvantage.full"}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if roll.rally.dice}}
|
||||
<div class="duality-modifier">
|
||||
{{localize "DAGGERHEART.CLASS.Feature.rallyDice"}} {{roll.rally.dice}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="dice-result">
|
||||
<div class="dice-formula">{{roll.formula}}</div>
|
||||
|
|
@ -38,7 +43,7 @@
|
|||
<div class="dice-title">{{localize "DAGGERHEART.GENERAL.hope"}}</div>
|
||||
<div class="dice-inner-container hope" title="{{localize "DAGGERHEART.GENERAL.hope"}}">
|
||||
<div class="dice-wrapper">
|
||||
<img class="dice" src="../icons/svg/d12-grey.svg"/>
|
||||
<img class="dice" src="../icons/svg/{{roll.hope.dice}}-grey.svg"/>
|
||||
</div>
|
||||
<div class="dice-value">{{roll.hope.value}}</div>
|
||||
</div>
|
||||
|
|
@ -49,7 +54,7 @@
|
|||
<div class="dice-title">{{localize "DAGGERHEART.GENERAL.fear"}}</div>
|
||||
<div class="dice-inner-container fear" title="{{localize "DAGGERHEART.GENERAL.fear"}}">
|
||||
<div class="dice-wrapper">
|
||||
<img class="dice" src="../icons/svg/d12-grey.svg"/>
|
||||
<img class="dice" src="../icons/svg/{{roll.fear.dice}}-grey.svg"/>
|
||||
</div>
|
||||
<div class="dice-value">{{roll.fear.value}}</div>
|
||||
</div>
|
||||
|
|
@ -72,7 +77,7 @@
|
|||
<div class="dice-container">
|
||||
<div class="dice-inner-container {{#if (eq roll.advantage.type 1)}}advantage{{else}}disadvantage{{/if}}">
|
||||
<div class="dice-wrapper">
|
||||
<img class="dice" src="../icons/svg/d6-grey.svg"/>
|
||||
<img class="dice" src="../icons/svg/{{roll.advantage.dice}}-grey.svg"/>
|
||||
</div>
|
||||
<div class="dice-value">{{roll.advantage.value}}</div>
|
||||
</div>
|
||||
|
|
@ -82,6 +87,30 @@
|
|||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if roll.rally.dice}}
|
||||
<div class="dice">
|
||||
<header class="part-header flexrow">
|
||||
<span class="part-formula">
|
||||
<span>1{{roll.rally.dice}}</span>
|
||||
</span>
|
||||
<span class="part-total">{{roll.rally.value}}</span>
|
||||
</header>
|
||||
<div class="flexrow">
|
||||
<ol class="dice-rolls">
|
||||
<li class="roll die {{roll.rally.dice}}">
|
||||
<div class="dice-container">
|
||||
<div class="dice-inner-container">
|
||||
<div class="dice-wrapper">
|
||||
<img class="dice" src="../icons/svg/{{roll.rally.dice}}-grey.svg"/>
|
||||
</div>
|
||||
<div class="dice-value">{{roll.rally.value}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#each roll.extra as | extra | }}
|
||||
<div class="dice">
|
||||
<header class="part-header flexrow">
|
||||
|
|
|
|||
5
templates/ui/tooltip/advantage.hbs
Normal file
5
templates/ui/tooltip/advantage.hbs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<div class="daggerheart dh-style tooltip">
|
||||
{{#each sources as | source |}}
|
||||
<div>{{{source}}}</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
7
templates/ui/tooltip/downtime.hbs
Normal file
7
templates/ui/tooltip/downtime.hbs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<div class="daggerheart dh-style tooltip">
|
||||
<div class="tooltip-title-container">
|
||||
<img class="tooltip-image" src="{{move.img}}" />
|
||||
<h2 class="tooltip-title">{{move.name}}</h2>
|
||||
</div>
|
||||
<div class="tooltip-description">{{{move.description}}}</div>
|
||||
</div>
|
||||
Loading…
Add table
Add a link
Reference in a new issue