mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-17 15:39:02 +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.Combat.documentClass = documents.DhpCombat;
|
||||||
CONFIG.ui.combat = applications.ui.DhCombatTracker;
|
CONFIG.ui.combat = applications.ui.DhCombatTracker;
|
||||||
CONFIG.ui.chat = applications.ui.DhChatLog;
|
CONFIG.ui.chat = applications.ui.DhChatLog;
|
||||||
|
CONFIG.ui.hotbar = applications.ui.DhHotbar;
|
||||||
CONFIG.Token.rulerClass = placeables.DhTokenRuler;
|
CONFIG.Token.rulerClass = placeables.DhTokenRuler;
|
||||||
|
|
||||||
CONFIG.ui.resources = applications.ui.DhFearTracker;
|
CONFIG.ui.resources = applications.ui.DhFearTracker;
|
||||||
|
|
|
||||||
63
lang/en.json
63
lang/en.json
|
|
@ -252,8 +252,9 @@
|
||||||
"title": "{actor} - Death Move"
|
"title": "{actor} - Death Move"
|
||||||
},
|
},
|
||||||
"Downtime": {
|
"Downtime": {
|
||||||
"downtimeHeader": "Downtime Moves ({current}/{max})",
|
|
||||||
"longRest": {
|
"longRest": {
|
||||||
|
"title": "Long Rest",
|
||||||
|
"moves": "Long Rest Moves ({current}/{max})",
|
||||||
"clearStress": {
|
"clearStress": {
|
||||||
"description": "Describe how you blow off steam or pull yourself together, and clear all marked Stress.",
|
"description": "Describe how you blow off steam or pull yourself together, and clear all marked Stress.",
|
||||||
"name": "Clear 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.",
|
"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"
|
"name": "Tend to Wounds"
|
||||||
},
|
},
|
||||||
"title": "Long Rest",
|
|
||||||
"workOnAProject": {
|
"workOnAProject": {
|
||||||
"description": "Establish or continue work on a project.",
|
"description": "Establish or continue work on a project.",
|
||||||
"name": "Work on a Project"
|
"name": "Work on a Project"
|
||||||
|
|
@ -278,6 +278,7 @@
|
||||||
},
|
},
|
||||||
"shortRest": {
|
"shortRest": {
|
||||||
"title": "Short Rest",
|
"title": "Short Rest",
|
||||||
|
"moves": "Short Rest Moves ({current}/{max})",
|
||||||
"tendToWounds": {
|
"tendToWounds": {
|
||||||
"name": "Tend to Wounds",
|
"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."
|
"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",
|
"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."
|
"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": {
|
"HUD": {
|
||||||
"tokenHUD": {
|
"tokenHUD": {
|
||||||
|
|
@ -410,7 +412,11 @@
|
||||||
"rerollDice": "Reroll Dice"
|
"rerollDice": "Reroll Dice"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"CLASS": {
|
||||||
|
"Feature": {
|
||||||
|
"rallyDice": "Bardic Rally Dice"
|
||||||
|
}
|
||||||
|
},
|
||||||
"CONFIG": {
|
"CONFIG": {
|
||||||
"ActionType": {
|
"ActionType": {
|
||||||
"passive": "Passive",
|
"passive": "Passive",
|
||||||
|
|
@ -778,7 +784,7 @@
|
||||||
"WeaponFeature": {
|
"WeaponFeature": {
|
||||||
"barrier": {
|
"barrier": {
|
||||||
"name": "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": {
|
"bonded": {
|
||||||
"name": "Bonded",
|
"name": "Bonded",
|
||||||
|
|
@ -894,7 +900,7 @@
|
||||||
},
|
},
|
||||||
"paired": {
|
"paired": {
|
||||||
"name": "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": {
|
"parry": {
|
||||||
"name": "Parry",
|
"name": "Parry",
|
||||||
|
|
@ -914,7 +920,7 @@
|
||||||
},
|
},
|
||||||
"protective": {
|
"protective": {
|
||||||
"name": "Protective",
|
"name": "Protective",
|
||||||
"description": "+{tier} to Armor Score"
|
"description": "Add your character's Tier to your Armor Score"
|
||||||
},
|
},
|
||||||
"quick": {
|
"quick": {
|
||||||
"name": "Quick",
|
"name": "Quick",
|
||||||
|
|
@ -963,10 +969,6 @@
|
||||||
"timebending": {
|
"timebending": {
|
||||||
"name": "Timebending",
|
"name": "Timebending",
|
||||||
"description": "You can choose the target of your attack after making your attack roll."
|
"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",
|
"singular": "Adversary",
|
||||||
"plural": "Adversaries"
|
"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": {
|
"Character": {
|
||||||
"singular": "Character",
|
"singular": "Character",
|
||||||
"plural": "Characters"
|
"plural": "Characters"
|
||||||
|
|
@ -1328,8 +1354,8 @@
|
||||||
},
|
},
|
||||||
"DomainCard": {
|
"DomainCard": {
|
||||||
"type": "Type",
|
"type": "Type",
|
||||||
"foundation": "Foundation",
|
|
||||||
"recallCost": "Recall Cost",
|
"recallCost": "Recall Cost",
|
||||||
|
"foundationTitle": "Foundation",
|
||||||
"specializationTitle": "Specialization",
|
"specializationTitle": "Specialization",
|
||||||
"masteryTitle": "Mastery"
|
"masteryTitle": "Mastery"
|
||||||
},
|
},
|
||||||
|
|
@ -1540,7 +1566,18 @@
|
||||||
"noAvailableArmorMarks": "You have no more available armor marks",
|
"noAvailableArmorMarks": "You have no more available armor marks",
|
||||||
"notEnoughStress": "You don't have enough stress",
|
"notEnoughStress": "You don't have enough stress",
|
||||||
"damageIgnore": "{character} did not take damage",
|
"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": {
|
"Tooltip": {
|
||||||
"openItemWorld": "Open Item World",
|
"openItemWorld": "Open Item World",
|
||||||
|
|
|
||||||
|
|
@ -506,9 +506,7 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
|
||||||
name: this.setup.ancestryName ?? this.setup.primaryAncestry.name,
|
name: this.setup.ancestryName ?? this.setup.primaryAncestry.name,
|
||||||
system: {
|
system: {
|
||||||
...this.setup.primaryAncestry.system,
|
...this.setup.primaryAncestry.system,
|
||||||
features: [primaryAncestryFeature.uuid, secondaryAncestryFeature.uuid],
|
features: [primaryAncestryFeature.uuid, secondaryAncestryFeature.uuid]
|
||||||
primaryFeature: primaryAncestryFeature.uuid,
|
|
||||||
secondaryFeature: secondaryAncestryFeature.uuid
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
||||||
if (this.roll) {
|
if (this.roll) {
|
||||||
context.roll = this.roll;
|
context.roll = this.roll;
|
||||||
context.rollType = this.roll?.constructor.name;
|
context.rollType = this.roll?.constructor.name;
|
||||||
|
context.rallyDie = this.roll.rallyChoices;
|
||||||
context.experiences = Object.keys(this.config.data.experiences).map(id => ({
|
context.experiences = Object.keys(this.config.data.experiences).map(id => ({
|
||||||
id,
|
id,
|
||||||
...this.config.data.experiences[id]
|
...this.config.data.experiences[id]
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,22 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
||||||
this.actor = actor;
|
this.actor = actor;
|
||||||
this.shortrest = shortrest;
|
this.shortrest = shortrest;
|
||||||
|
|
||||||
const options = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).restMoves;
|
this.moveData = foundry.utils.deepClone(
|
||||||
this.moveData = shortrest ? options.shortRest : options.longRest;
|
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() {
|
get title() {
|
||||||
|
|
@ -17,8 +31,8 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
||||||
|
|
||||||
static DEFAULT_OPTIONS = {
|
static DEFAULT_OPTIONS = {
|
||||||
tag: 'form',
|
tag: 'form',
|
||||||
classes: ['daggerheart', 'views', 'downtime'],
|
classes: ['daggerheart', 'views', 'dh-style', 'dialog', 'downtime'],
|
||||||
position: { width: 680, height: 'auto' },
|
position: { width: 'auto', height: 'auto' },
|
||||||
actions: {
|
actions: {
|
||||||
selectMove: this.selectMove,
|
selectMove: this.selectMove,
|
||||||
takeDowntime: this.takeDowntime
|
takeDowntime: this.takeDowntime
|
||||||
|
|
@ -29,7 +43,7 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
||||||
static PARTS = {
|
static PARTS = {
|
||||||
application: {
|
application: {
|
||||||
id: 'downtime',
|
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);
|
super._attachPartListeners(partId, htmlElement, options);
|
||||||
|
|
||||||
htmlElement
|
htmlElement
|
||||||
.querySelectorAll('.activity-image')
|
.querySelectorAll('.activity-container')
|
||||||
.forEach(element => element.addEventListener('contextmenu', this.deselectMove.bind(this)));
|
.forEach(element => element.addEventListener('contextmenu', this.deselectMove.bind(this)));
|
||||||
}
|
}
|
||||||
|
|
||||||
async _prepareContext(_options) {
|
async _prepareContext(_options) {
|
||||||
const context = await super._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.selectedActivity = this.selectedActivity;
|
||||||
context.moveData = this.moveData;
|
context.moveData = this.moveData;
|
||||||
context.nrCurrentChoices = Object.values(this.moveData.moves).reduce((acc, x) => acc + (x.selected ?? 0), 0);
|
context.nrCurrentChoices = Object.values(this.moveData).reduce((acc, category) => {
|
||||||
context.disabledDowntime = context.nrCurrentChoices < context.moveData.nrChoices;
|
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;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
static selectMove(_, button) {
|
static selectMove(_, target) {
|
||||||
const nrSelected = Object.values(this.moveData.moves).reduce((acc, x) => acc + (x.selected ?? 0), 0);
|
const nrSelected = Object.values(this.moveData[target.dataset.category].moves).reduce(
|
||||||
if (nrSelected === this.moveData.nrChoices) {
|
(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'));
|
ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.noMoreMoves'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const move = button.dataset.move;
|
const move = target.dataset.move;
|
||||||
this.moveData.moves[move].selected = this.moveData.moves[move].selected
|
this.moveData[target.dataset.category].moves[move].selected = this.moveData[target.dataset.category].moves[move]
|
||||||
? this.moveData.moves[move].selected + 1
|
.selected
|
||||||
|
? this.moveData[target.dataset.category].moves[move].selected + 1
|
||||||
: 1;
|
: 1;
|
||||||
|
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
deselectMove(event) {
|
deselectMove(event) {
|
||||||
const move = event.currentTarget.dataset.move;
|
const button = event.target.closest('.activity-container');
|
||||||
this.moveData.moves[move].selected = this.moveData.moves[move].selected
|
const move = button.dataset.move;
|
||||||
? this.moveData.moves[move].selected - 1
|
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;
|
: 0;
|
||||||
|
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
static async takeDowntime() {
|
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 cls = getDocumentClass('ChatMessage');
|
||||||
const msg = new cls({
|
const msg = new cls({
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ export default class ResourceDiceDialog extends HandlebarsApplicationMixin(Appli
|
||||||
|
|
||||||
static async rerollDice() {
|
static async rerollDice() {
|
||||||
const max = itemAbleRollParse(this.item.system.resource.max, this.actor, this.item);
|
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();
|
const roll = await new Roll(diceFormula).evaluate();
|
||||||
if (game.modules.get('dice-so-nice')?.active) await game.dice3d.showForRoll(roll, game.user, true);
|
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 }));
|
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;
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
||||||
export default class DhSettingsActionView extends HandlebarsApplicationMixin(ApplicationV2) {
|
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({});
|
super({});
|
||||||
|
|
||||||
this.resolve = resolve;
|
this.resolve = resolve;
|
||||||
this.reject = reject;
|
this.reject = reject;
|
||||||
this.viewTitle = title;
|
this.viewTitle = title;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.icon = icon;
|
||||||
this.img = img;
|
this.img = img;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.actions = actions;
|
this.actions = actions;
|
||||||
|
|
@ -23,7 +24,7 @@ export default class DhSettingsActionView extends HandlebarsApplicationMixin(App
|
||||||
static DEFAULT_OPTIONS = {
|
static DEFAULT_OPTIONS = {
|
||||||
tag: 'form',
|
tag: 'form',
|
||||||
classes: ['daggerheart', 'setting', 'dh-style'],
|
classes: ['daggerheart', 'setting', 'dh-style'],
|
||||||
position: { width: '400', height: 'auto' },
|
position: { width: 440, height: 'auto' },
|
||||||
actions: {
|
actions: {
|
||||||
editImage: this.onEditImage,
|
editImage: this.onEditImage,
|
||||||
addItem: this.addItem,
|
addItem: this.addItem,
|
||||||
|
|
@ -46,6 +47,7 @@ export default class DhSettingsActionView extends HandlebarsApplicationMixin(App
|
||||||
async _prepareContext(_options) {
|
async _prepareContext(_options) {
|
||||||
const context = await super._prepareContext(_options);
|
const context = await super._prepareContext(_options);
|
||||||
context.name = this.name;
|
context.name = this.name;
|
||||||
|
context.icon = this.icon;
|
||||||
context.img = this.img;
|
context.img = this.img;
|
||||||
context.description = this.description;
|
context.description = this.description;
|
||||||
context.enrichedDescription = await foundry.applications.ux.TextEditor.enrichHTML(context.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) {
|
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.name = name;
|
||||||
|
this.icon = icon;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
|
|
||||||
this.render();
|
this.render();
|
||||||
|
|
@ -65,6 +68,7 @@ export default class DhSettingsActionView extends HandlebarsApplicationMixin(App
|
||||||
static async saveForm(event) {
|
static async saveForm(event) {
|
||||||
this.resolve({
|
this.resolve({
|
||||||
name: this.name,
|
name: this.name,
|
||||||
|
icon: this.icon,
|
||||||
img: this.img,
|
img: this.img,
|
||||||
description: this.description,
|
description: this.description,
|
||||||
actions: this.actions
|
actions: this.actions
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
reject,
|
reject,
|
||||||
game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.downtimeMoves'),
|
game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.downtimeMoves'),
|
||||||
move.name,
|
move.name,
|
||||||
|
move.icon,
|
||||||
move.img,
|
move.img,
|
||||||
move.description,
|
move.description,
|
||||||
move.actions
|
move.actions
|
||||||
|
|
@ -87,6 +88,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
|
||||||
await this.settings.updateSource({
|
await this.settings.updateSource({
|
||||||
[`restMoves.${type}.moves.${id}`]: {
|
[`restMoves.${type}.moves.${id}`]: {
|
||||||
name: data.name,
|
name: data.name,
|
||||||
|
icon: data.icon,
|
||||||
img: data.img,
|
img: data.img,
|
||||||
description: data.description
|
description: data.description
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,9 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
||||||
element.value = `system.${item.value}`;
|
element.value = `system.${item.value}`;
|
||||||
},
|
},
|
||||||
click: e => e.fetch(),
|
click: e => e.fetch(),
|
||||||
|
customize: function (_input, _inputRect, container) {
|
||||||
|
container.style.zIndex = foundry.applications.api.ApplicationV2._maxZ;
|
||||||
|
},
|
||||||
minLength: 0
|
minLength: 0
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,8 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
useAction: this.useAction,
|
useAction: this.useAction,
|
||||||
toggleResourceDice: this.toggleResourceDice,
|
toggleResourceDice: this.toggleResourceDice,
|
||||||
handleResourceDice: this.handleResourceDice,
|
handleResourceDice: this.handleResourceDice,
|
||||||
toChat: this.toChat
|
toChat: this.toChat,
|
||||||
|
useDowntime: this.useDowntime
|
||||||
},
|
},
|
||||||
window: {
|
window: {
|
||||||
resizable: true
|
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) {
|
async _onDragStart(event) {
|
||||||
const item = this.getItem(event);
|
const item = this.getItem(event);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
||||||
actions: {
|
actions: {
|
||||||
openSettings: DHBaseActorSheet.#openSettings
|
openSettings: DHBaseActorSheet.#openSettings
|
||||||
},
|
},
|
||||||
dragDrop: []
|
dragDrop: [{ dragSelector: '.inventory-item[data-type="attack"]', dropSelector: null }]
|
||||||
};
|
};
|
||||||
|
|
||||||
/**@type {typeof DHBaseActorSettings}*/
|
/**@type {typeof DHBaseActorSettings}*/
|
||||||
|
|
@ -49,4 +49,27 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
|
||||||
static async #openSettings() {
|
static async #openSettings() {
|
||||||
await this.settingSheet.render({ force: true });
|
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: [
|
dragDrop: [
|
||||||
{ dragSelector: null, dropSelector: '.tab.features .drop-section' },
|
{ 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 };
|
const featureData = { type: 'Item', data: { ...feature.toObject(), _id: null }, fromInternal: true };
|
||||||
event.dataTransfer.setData('text/plain', JSON.stringify(featureData));
|
event.dataTransfer.setData('text/plain', JSON.stringify(featureData));
|
||||||
event.dataTransfer.setDragImage(featureItem.querySelector('img'), 60, 0);
|
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();
|
event.stopPropagation();
|
||||||
const target = button.closest('.feature-item');
|
const target = button.closest('.feature-item');
|
||||||
const feature = this.document.system[`${target.dataset.type}Feature`];
|
const feature = this.document.system[`${target.dataset.type}Feature`];
|
||||||
const featureExists = feature && Object.keys(feature).length > 0;
|
|
||||||
|
|
||||||
if (featureExists) {
|
if (feature) await feature.update({ 'system.subType': null });
|
||||||
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 });
|
|
||||||
await this.document.update({
|
await this.document.update({
|
||||||
'system.features': this.document.system.features.filter(x => x && x.uuid !== feature.uuid).map(x => x.uuid)
|
'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) {
|
async _onDrop(event) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
|
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
|
||||||
|
|
||||||
const item = await fromUuid(data.uuid);
|
const item = await fromUuid(data.uuid);
|
||||||
if (item?.type === 'feature') {
|
if (item?.type === 'feature') {
|
||||||
const subType = event.target.closest('.primary-feature') ? 'primary' : 'secondary';
|
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({
|
await this.document.update({
|
||||||
'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid]
|
'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) {
|
async _onDrop(event) {
|
||||||
|
event.stopPropagation();
|
||||||
const data = TextEditor.getDragEventData(event);
|
const data = TextEditor.getDragEventData(event);
|
||||||
const item = await fromUuid(data.uuid);
|
const item = await fromUuid(data.uuid);
|
||||||
const target = event.target.closest('fieldset.drop-section');
|
const target = event.target.closest('fieldset.drop-section');
|
||||||
|
|
@ -87,12 +88,24 @@ export default class ClassSheet extends DHBaseItemSheet {
|
||||||
});
|
});
|
||||||
} else if (item.type === 'feature') {
|
} else if (item.type === 'feature') {
|
||||||
if (target.classList.contains('hope-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({
|
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')) {
|
} 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({
|
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') {
|
} else if (item.type === 'weapon') {
|
||||||
|
|
@ -177,28 +190,25 @@ export default class ClassSheet extends DHBaseItemSheet {
|
||||||
doc.sheet.render({ force: true });
|
doc.sheet.render({ force: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
getActionPath(type) {
|
|
||||||
return type === 'hope' ? 'hopeFeatures' : 'classFeatures';
|
|
||||||
}
|
|
||||||
|
|
||||||
static async addFeature(_, target) {
|
static async addFeature(_, target) {
|
||||||
const actionPath = this.getActionPath(target.dataset.type);
|
|
||||||
const feature = await game.items.documentClass.create({
|
const feature = await game.items.documentClass.create({
|
||||||
type: 'feature',
|
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({
|
await this.document.update({
|
||||||
[`system.${actionPath}`]: [
|
[`system.features`]: [...this.document.system.features.filter(x => x).map(x => x.uuid), feature.uuid]
|
||||||
...this.document.system[actionPath].filter(x => x).map(x => x.uuid),
|
|
||||||
feature.uuid
|
|
||||||
]
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static async editFeature(_, button) {
|
static async editFeature(_, button) {
|
||||||
const target = button.closest('.feature-item');
|
const target = button.closest('.feature-item');
|
||||||
const actionPath = this.getActionPath(button.dataset.type);
|
const feature = this.document.system.features.find(x => x?.id === target.dataset.featureId);
|
||||||
const feature = this.document.system[actionPath].find(x => x?.id === target.dataset.featureId);
|
|
||||||
if (!feature) {
|
if (!feature) {
|
||||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureIsMissing'));
|
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureIsMissing'));
|
||||||
return;
|
return;
|
||||||
|
|
@ -210,10 +220,16 @@ export default class ClassSheet extends DHBaseItemSheet {
|
||||||
static async deleteFeature(event, button) {
|
static async deleteFeature(event, button) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
const target = button.closest('.feature-item');
|
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({
|
await this.document.update({
|
||||||
[`system.${actionPath}`]: this.document.system[actionPath]
|
[`system.features`]: this.document.system.features
|
||||||
.filter(feature => feature && feature.id !== target.dataset.featureId)
|
.filter(feature => feature && feature.id !== target.dataset.featureId)
|
||||||
.map(x => x.uuid)
|
.map(x => x.uuid)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -40,28 +40,46 @@ export default class SubclassSheet extends DHBaseItemSheet {
|
||||||
static async addFeature(_, target) {
|
static async addFeature(_, target) {
|
||||||
const feature = await game.items.documentClass.create({
|
const feature = await game.items.documentClass.create({
|
||||||
type: 'feature',
|
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({
|
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) {
|
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) {
|
if (!feature) {
|
||||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureIsMissing'));
|
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureIsMissing'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (feature) {
|
||||||
|
await feature.update({ 'system.subType': null });
|
||||||
|
}
|
||||||
|
|
||||||
feature.sheet.render(true);
|
feature.sheet.render(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async deleteFeature(event, button) {
|
static async deleteFeature(event, target) {
|
||||||
event.stopPropagation();
|
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({
|
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) {
|
async _onDrop(event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
|
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
|
||||||
if (data.fromInternal) return;
|
if (data.fromInternal) return;
|
||||||
|
|
||||||
const item = await fromUuid(data.uuid);
|
const item = await fromUuid(data.uuid);
|
||||||
if (item?.type === 'feature') {
|
const target = event.target.closest('fieldset.drop-section');
|
||||||
const dropSection = event.target.closest('.drop-section');
|
if (item.type === 'feature') {
|
||||||
if (this.document.system[dropSection.dataset.type]) {
|
if (target.dataset.type === 'foundation') {
|
||||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.notifications.featureIsFull'));
|
if (item.system.subType && item.system.subType !== CONFIG.DH.ITEM.featureSubTypes.foundation) {
|
||||||
return;
|
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 { default as DhCombatTracker } from './combatTracker.mjs';
|
||||||
export * as DhCountdowns from './countdowns.mjs';
|
export * as DhCountdowns from './countdowns.mjs';
|
||||||
export { default as DhFearTracker } from './fearTracker.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 = {
|
export const subclassFeatureLabels = {
|
||||||
1: 'DAGGERHEART.ITEMS.DomainCard.foundation',
|
1: 'DAGGERHEART.ITEMS.DomainCard.foundationTitle',
|
||||||
2: 'DAGGERHEART.ITEMS.DomainCard.specializationTitle',
|
2: 'DAGGERHEART.ITEMS.DomainCard.specializationTitle',
|
||||||
3: 'DAGGERHEART.ITEMS.DomainCard.masteryTitle'
|
3: 'DAGGERHEART.ITEMS.DomainCard.masteryTitle'
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,7 @@ export const defaultRestOptions = {
|
||||||
tendToWounds: {
|
tendToWounds: {
|
||||||
id: 'tendToWounds',
|
id: 'tendToWounds',
|
||||||
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.tendToWounds.name'),
|
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.tendToWounds.name'),
|
||||||
|
icon: 'fa-solid fa-bandage',
|
||||||
img: 'icons/magic/life/cross-worn-green.webp',
|
img: 'icons/magic/life/cross-worn-green.webp',
|
||||||
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.tendToWounds.description'),
|
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.tendToWounds.description'),
|
||||||
actions: [
|
actions: [
|
||||||
|
|
@ -158,6 +159,7 @@ export const defaultRestOptions = {
|
||||||
clearStress: {
|
clearStress: {
|
||||||
id: 'clearStress',
|
id: 'clearStress',
|
||||||
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.clearStress.name'),
|
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.clearStress.name'),
|
||||||
|
icon: 'fa-regular fa-face-surprise',
|
||||||
img: 'icons/magic/perception/eye-ringed-green.webp',
|
img: 'icons/magic/perception/eye-ringed-green.webp',
|
||||||
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.clearStress.description'),
|
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.clearStress.description'),
|
||||||
actions: [
|
actions: [
|
||||||
|
|
@ -181,6 +183,7 @@ export const defaultRestOptions = {
|
||||||
repairArmor: {
|
repairArmor: {
|
||||||
id: 'repairArmor',
|
id: 'repairArmor',
|
||||||
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.repairArmor.name'),
|
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.repairArmor.name'),
|
||||||
|
icon: 'fa-solid fa-hammer',
|
||||||
img: 'icons/skills/trades/smithing-anvil-silver-red.webp',
|
img: 'icons/skills/trades/smithing-anvil-silver-red.webp',
|
||||||
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.repairArmor.description'),
|
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.repairArmor.description'),
|
||||||
actions: []
|
actions: []
|
||||||
|
|
@ -188,6 +191,7 @@ export const defaultRestOptions = {
|
||||||
prepare: {
|
prepare: {
|
||||||
id: 'prepare',
|
id: 'prepare',
|
||||||
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.prepare.name'),
|
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.prepare.name'),
|
||||||
|
icon: 'fa-solid fa-dumbbell',
|
||||||
img: 'icons/skills/trades/academics-merchant-scribe.webp',
|
img: 'icons/skills/trades/academics-merchant-scribe.webp',
|
||||||
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.prepare.description'),
|
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.prepare.description'),
|
||||||
actions: []
|
actions: []
|
||||||
|
|
@ -197,6 +201,7 @@ export const defaultRestOptions = {
|
||||||
tendToWounds: {
|
tendToWounds: {
|
||||||
id: 'tendToWounds',
|
id: 'tendToWounds',
|
||||||
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.tendToWounds.name'),
|
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.tendToWounds.name'),
|
||||||
|
icon: 'fa-solid fa-bandage',
|
||||||
img: 'icons/magic/life/cross-worn-green.webp',
|
img: 'icons/magic/life/cross-worn-green.webp',
|
||||||
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.tendToWounds.description'),
|
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.tendToWounds.description'),
|
||||||
actions: []
|
actions: []
|
||||||
|
|
@ -204,6 +209,7 @@ export const defaultRestOptions = {
|
||||||
clearStress: {
|
clearStress: {
|
||||||
id: 'clearStress',
|
id: 'clearStress',
|
||||||
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.clearStress.name'),
|
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.clearStress.name'),
|
||||||
|
icon: 'fa-regular fa-face-surprise',
|
||||||
img: 'icons/magic/perception/eye-ringed-green.webp',
|
img: 'icons/magic/perception/eye-ringed-green.webp',
|
||||||
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.clearStress.description'),
|
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.clearStress.description'),
|
||||||
actions: []
|
actions: []
|
||||||
|
|
@ -211,6 +217,7 @@ export const defaultRestOptions = {
|
||||||
repairArmor: {
|
repairArmor: {
|
||||||
id: 'repairArmor',
|
id: 'repairArmor',
|
||||||
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.repairArmor.name'),
|
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.repairArmor.name'),
|
||||||
|
icon: 'fa-solid fa-hammer',
|
||||||
img: 'icons/skills/trades/smithing-anvil-silver-red.webp',
|
img: 'icons/skills/trades/smithing-anvil-silver-red.webp',
|
||||||
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.repairArmor.description'),
|
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.repairArmor.description'),
|
||||||
actions: []
|
actions: []
|
||||||
|
|
@ -218,6 +225,7 @@ export const defaultRestOptions = {
|
||||||
prepare: {
|
prepare: {
|
||||||
id: 'prepare',
|
id: 'prepare',
|
||||||
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.prepare.name'),
|
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.prepare.name'),
|
||||||
|
icon: 'fa-solid fa-dumbbell',
|
||||||
img: 'icons/skills/trades/academics-merchant-scribe.webp',
|
img: 'icons/skills/trades/academics-merchant-scribe.webp',
|
||||||
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.prepare.description'),
|
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.prepare.description'),
|
||||||
actions: []
|
actions: []
|
||||||
|
|
@ -225,19 +233,12 @@ export const defaultRestOptions = {
|
||||||
workOnAProject: {
|
workOnAProject: {
|
||||||
id: 'workOnAProject',
|
id: 'workOnAProject',
|
||||||
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.workOnAProject.name'),
|
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.workOnAProject.name'),
|
||||||
|
icon: 'fa-solid fa-diagram-project',
|
||||||
img: 'icons/skills/social/thumbsup-approval-like.webp',
|
img: 'icons/skills/social/thumbsup-approval-like.webp',
|
||||||
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.workOnAProject.description'),
|
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.workOnAProject.description'),
|
||||||
actions: []
|
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 = {
|
export const deathMoves = {
|
||||||
|
|
|
||||||
|
|
@ -439,7 +439,7 @@ export const weaponFeatures = {
|
||||||
{
|
{
|
||||||
key: 'system.bonuses.damage.primaryWeapon.bonus',
|
key: 'system.bonuses.damage.primaryWeapon.bonus',
|
||||||
mode: 2,
|
mode: 2,
|
||||||
value: '@system.levelData.levels.current'
|
value: '@system.levelData.level.current'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -1261,15 +1261,6 @@ export const weaponFeatures = {
|
||||||
timebending: {
|
timebending: {
|
||||||
label: 'DAGGERHEART.CONFIG.WeaponFeature.timebending.name',
|
label: 'DAGGERHEART.CONFIG.WeaponFeature.timebending.name',
|
||||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.timebending.description'
|
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 = {
|
export const featureSubTypes = {
|
||||||
primary: 'primary',
|
primary: 'primary',
|
||||||
secondary: 'secondary'
|
secondary: 'secondary',
|
||||||
|
hope: 'hope',
|
||||||
|
class: 'class',
|
||||||
|
foundation: 'foundation',
|
||||||
|
specialization: 'specialization',
|
||||||
|
mastery: 'mastery'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const actionTypes = {
|
export const actionTypes = {
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,8 @@ export default class DhCharacter extends BaseDataActor {
|
||||||
value: new ForeignDocumentUUIDField({ type: 'Item', nullable: true }),
|
value: new ForeignDocumentUUIDField({ type: 'Item', nullable: true }),
|
||||||
subclass: 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),
|
levelData: new fields.EmbeddedDataField(DhLevelData),
|
||||||
bonuses: new fields.SchemaField({
|
bonuses: new fields.SchemaField({
|
||||||
roll: new fields.SchemaField({
|
roll: new fields.SchemaField({
|
||||||
|
|
@ -102,7 +104,7 @@ export default class DhCharacter extends BaseDataActor {
|
||||||
physical: bonusField('DAGGERHEART.GENERAL.Damage.physicalDamage'),
|
physical: bonusField('DAGGERHEART.GENERAL.Damage.physicalDamage'),
|
||||||
magical: bonusField('DAGGERHEART.GENERAL.Damage.magicalDamage'),
|
magical: bonusField('DAGGERHEART.GENERAL.Damage.magicalDamage'),
|
||||||
primaryWeapon: bonusField('DAGGERHEART.GENERAL.Damage.primaryWeapon'),
|
primaryWeapon: bonusField('DAGGERHEART.GENERAL.Damage.primaryWeapon'),
|
||||||
secondaryWeapon: bonusField('DAGGERHEART.GENERAL.Damage.primaryWeapon')
|
secondaryWeapon: bonusField('DAGGERHEART.GENERAL.Damage.secondaryWeapon')
|
||||||
}),
|
}),
|
||||||
healing: bonusField('DAGGERHEART.GENERAL.Healing.healingAmount'),
|
healing: bonusField('DAGGERHEART.GENERAL.Healing.healingAmount'),
|
||||||
range: new fields.SchemaField({
|
range: new fields.SchemaField({
|
||||||
|
|
@ -121,6 +123,47 @@ export default class DhCharacter extends BaseDataActor {
|
||||||
initial: 0,
|
initial: 0,
|
||||||
label: 'DAGGERHEART.GENERAL.Range.other'
|
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 }),
|
companion: new ForeignDocumentUUIDField({ type: 'Actor', nullable: true, initial: null }),
|
||||||
|
|
@ -244,23 +287,23 @@ export default class DhCharacter extends BaseDataActor {
|
||||||
features = [];
|
features = [];
|
||||||
|
|
||||||
for (let item of this.parent.items) {
|
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);
|
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);
|
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);
|
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 subclassState = this.class.subclass.system.featureState;
|
||||||
const identifier = item.system.identifier;
|
const subType = item.system.subType;
|
||||||
if (
|
if (
|
||||||
identifier === 'foundationFeature' ||
|
subType === CONFIG.DH.ITEM.featureSubTypes.foundation ||
|
||||||
(identifier === 'specializationFeature' && subclassState >= 2) ||
|
(subType === CONFIG.DH.ITEM.featureSubTypes.specialization && subclassState >= 2) ||
|
||||||
(identifier === 'masterFeature' && subclassState >= 3)
|
(subType === CONFIG.DH.ITEM.featureSubTypes.mastery && subclassState >= 3)
|
||||||
) {
|
) {
|
||||||
subclassFeatures.push(item);
|
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);
|
companionFeatures.push(item);
|
||||||
} else if (item.type === 'feature' && !item.system.type) {
|
} else if (item.type === 'feature' && !item.system.type) {
|
||||||
features.push(item);
|
features.push(item);
|
||||||
|
|
|
||||||
|
|
@ -135,8 +135,8 @@ export const registerCountdownHooks = () => {
|
||||||
if (application) {
|
if (application) {
|
||||||
foundry.applications.instances.get(application)?.render();
|
foundry.applications.instances.get(application)?.render();
|
||||||
} else {
|
} else {
|
||||||
foundry.applications.instances.get('narrative-countdowns').render();
|
foundry.applications.instances.get('narrative-countdowns')?.render();
|
||||||
foundry.applications.instances.get('encounter-countdowns').render();
|
foundry.applications.instances.get('encounter-countdowns')?.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,11 @@ const stressDamageReductionRule = localizationPath =>
|
||||||
|
|
||||||
const bonusField = label =>
|
const bonusField = label =>
|
||||||
new fields.SchemaField({
|
new fields.SchemaField({
|
||||||
bonus: new fields.NumberField({ integer: true, initial: 0, label }),
|
bonus: new fields.NumberField({ integer: true, initial: 0, label: `${game.i18n.localize(label)} Value` }),
|
||||||
dice: new fields.ArrayField(new fields.StringField())
|
dice: new fields.ArrayField(
|
||||||
|
new fields.StringField(),
|
||||||
|
{ label: `${game.i18n.localize(label)} Dice` }
|
||||||
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
export { attributeField, resourceField, stressDamageReductionRule, bonusField };
|
export { attributeField, resourceField, stressDamageReductionRule, bonusField };
|
||||||
|
|
|
||||||
|
|
@ -53,11 +53,14 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
||||||
}),
|
}),
|
||||||
diceStates: new fields.TypedObjectField(
|
diceStates: new fields.TypedObjectField(
|
||||||
new fields.SchemaField({
|
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 })
|
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 }
|
{ nullable: true, initial: null }
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ export default class DHBeastform extends BaseDataItem {
|
||||||
|
|
||||||
const beastformEffect = this.parent.effects.find(x => x.type === 'beastform');
|
const beastformEffect = this.parent.effects.find(x => x.type === 'beastform');
|
||||||
await beastformEffect.updateSource({
|
await beastformEffect.updateSource({
|
||||||
|
changes: [...beastformEffect.changes, { key: 'system.advantageSources', mode: 2, value: this.advantageOn }],
|
||||||
system: {
|
system: {
|
||||||
characterTokenData: {
|
characterTokenData: {
|
||||||
tokenImg: this.parent.parent.prototypeToken.texture.src,
|
tokenImg: this.parent.parent.prototypeToken.texture.src,
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,7 @@ export default class DHClass extends BaseDataItem {
|
||||||
label: 'DAGGERHEART.GENERAL.hitPoints.plural'
|
label: 'DAGGERHEART.GENERAL.hitPoints.plural'
|
||||||
}),
|
}),
|
||||||
evasion: new fields.NumberField({ initial: 0, integer: true, label: 'DAGGERHEART.GENERAL.evasion' }),
|
evasion: new fields.NumberField({ initial: 0, integer: true, label: 'DAGGERHEART.GENERAL.evasion' }),
|
||||||
hopeFeatures: new ForeignDocumentUUIDArrayField({ type: 'Item' }),
|
features: new ForeignDocumentUUIDArrayField({ type: 'Item' }),
|
||||||
classFeatures: new ForeignDocumentUUIDArrayField({ type: 'Item' }),
|
|
||||||
subclasses: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false }),
|
subclasses: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false }),
|
||||||
inventory: new fields.SchemaField({
|
inventory: new fields.SchemaField({
|
||||||
take: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false }),
|
take: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false }),
|
||||||
|
|
@ -52,12 +51,18 @@ export default class DHClass extends BaseDataItem {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
get hopeFeature() {
|
get hopeFeatures() {
|
||||||
return this.hopeFeatures.length > 0 ? this.hopeFeatures[0] : null;
|
return (
|
||||||
|
this.features.filter(x => x?.system?.subType === CONFIG.DH.ITEM.featureSubTypes.hope) ??
|
||||||
|
(this.features.filter(x => !x).length > 0 ? {} : null)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get features() {
|
get classFeatures() {
|
||||||
return [...this.hopeFeatures.filter(x => x), ...this.classFeatures.filter(x => x)];
|
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) {
|
async _preCreate(data, options, user) {
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ export default class DHDomainCard extends BaseDataItem {
|
||||||
required: true,
|
required: true,
|
||||||
initial: CONFIG.DH.DOMAIN.cardTypes.ability.id
|
initial: CONFIG.DH.DOMAIN.cardTypes.ability.id
|
||||||
}),
|
}),
|
||||||
foundation: new fields.BooleanField({ initial: false }),
|
|
||||||
inVault: new fields.BooleanField({ initial: false }),
|
inVault: new fields.BooleanField({ initial: false }),
|
||||||
actions: new fields.ArrayField(new ActionField())
|
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';
|
import BaseDataItem from './base.mjs';
|
||||||
|
|
||||||
export default class DHSubclass extends BaseDataItem {
|
export default class DHSubclass extends BaseDataItem {
|
||||||
|
|
@ -22,20 +22,22 @@ export default class DHSubclass extends BaseDataItem {
|
||||||
nullable: true,
|
nullable: true,
|
||||||
initial: null
|
initial: null
|
||||||
}),
|
}),
|
||||||
foundationFeature: new ForeignDocumentUUIDField({ type: 'Item' }),
|
features: new ForeignDocumentUUIDArrayField({ type: 'Item' }),
|
||||||
specializationFeature: new ForeignDocumentUUIDField({ type: 'Item' }),
|
|
||||||
masteryFeature: new ForeignDocumentUUIDField({ type: 'Item' }),
|
|
||||||
featureState: new fields.NumberField({ required: true, initial: 1, min: 1 }),
|
featureState: new fields.NumberField({ required: true, initial: 1, min: 1 }),
|
||||||
isMulticlass: new fields.BooleanField({ initial: false })
|
isMulticlass: new fields.BooleanField({ initial: false })
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
get features() {
|
get foundationFeatures() {
|
||||||
return [
|
return this.features.filter(x => x.system.subType === CONFIG.DH.ITEM.featureSubTypes.foundation);
|
||||||
{ ...this.foundationFeature?.toObject(), identifier: 'foundationFeature' },
|
}
|
||||||
{ ...this.specializationFeature?.toObject(), identifier: 'specializationFeature' },
|
|
||||||
{ ...this.masteryFeature?.toObject(), identifier: 'masteryFeature' }
|
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) {
|
async _preCreate(data, options, user) {
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
|
||||||
moves: new fields.TypedObjectField(
|
moves: new fields.TypedObjectField(
|
||||||
new fields.SchemaField({
|
new fields.SchemaField({
|
||||||
name: new fields.StringField({ required: true }),
|
name: new fields.StringField({ required: true }),
|
||||||
|
icon: new fields.StringField({ required: true }),
|
||||||
img: new fields.FilePathField({
|
img: new fields.FilePathField({
|
||||||
initial: 'icons/magic/life/cross-worn-green.webp',
|
initial: 'icons/magic/life/cross-worn-green.webp',
|
||||||
categories: ['IMAGE'],
|
categories: ['IMAGE'],
|
||||||
|
|
@ -70,6 +71,7 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
|
||||||
moves: new fields.TypedObjectField(
|
moves: new fields.TypedObjectField(
|
||||||
new fields.SchemaField({
|
new fields.SchemaField({
|
||||||
name: new fields.StringField({ required: true }),
|
name: new fields.StringField({ required: true }),
|
||||||
|
icon: new fields.StringField({ required: true }),
|
||||||
img: new fields.FilePathField({
|
img: new fields.FilePathField({
|
||||||
initial: 'icons/magic/life/cross-worn-green.webp',
|
initial: 'icons/magic/life/cross-worn-green.webp',
|
||||||
categories: ['IMAGE'],
|
categories: ['IMAGE'],
|
||||||
|
|
|
||||||
|
|
@ -39,11 +39,13 @@ export default class D20Roll extends DHRoll {
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasAdvantage() {
|
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() {
|
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) {
|
static applyKeybindings(config) {
|
||||||
|
|
@ -90,8 +92,8 @@ export default class D20Roll extends DHRoll {
|
||||||
|
|
||||||
configureModifiers() {
|
configureModifiers() {
|
||||||
this.applyAdvantage();
|
this.applyAdvantage();
|
||||||
|
|
||||||
this.baseTerms = foundry.utils.deepClone(this.terms);
|
this.baseTerms = foundry.utils.deepClone(this.dice);
|
||||||
|
|
||||||
this.options.roll.modifiers = this.applyBaseBonus();
|
this.options.roll.modifiers = this.applyBaseBonus();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,12 @@ import { setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs';
|
||||||
|
|
||||||
export default class DualityRoll extends D20Roll {
|
export default class DualityRoll extends D20Roll {
|
||||||
_advantageFaces = 6;
|
_advantageFaces = 6;
|
||||||
|
_advantageNumber = 1;
|
||||||
|
_rallyIndex;
|
||||||
|
|
||||||
constructor(formula, data = {}, options = {}) {
|
constructor(formula, data = {}, options = {}) {
|
||||||
super(formula, data, options);
|
super(formula, data, options);
|
||||||
|
this.rallyChoices = this.setRallyChoices();
|
||||||
}
|
}
|
||||||
|
|
||||||
static messageType = 'dualityRoll';
|
static messageType = 'dualityRoll';
|
||||||
|
|
@ -51,6 +54,35 @@ export default class DualityRoll extends D20Roll {
|
||||||
this._advantageFaces = this.getFaces(faces);
|
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() {
|
get isCritical() {
|
||||||
if (!this.dHope._evaluated || !this.dFear._evaluated) return;
|
if (!this.dHope._evaluated || !this.dFear._evaluated) return;
|
||||||
return this.dHope.total === this.dFear.total;
|
return this.dHope.total === this.dFear.total;
|
||||||
|
|
@ -66,10 +98,6 @@ export default class DualityRoll extends D20Roll {
|
||||||
return this.dHope.total < this.dFear.total;
|
return this.dHope.total < this.dFear.total;
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasBarRally() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
get totalLabel() {
|
get totalLabel() {
|
||||||
const label = this.withHope
|
const label = this.withHope
|
||||||
? 'DAGGERHEART.GENERAL.hope'
|
? 'DAGGERHEART.GENERAL.hope'
|
||||||
|
|
@ -98,24 +126,20 @@ export default class DualityRoll extends D20Roll {
|
||||||
}
|
}
|
||||||
|
|
||||||
applyAdvantage() {
|
applyAdvantage() {
|
||||||
const dieFaces = this.advantageFaces,
|
if (this.hasAdvantage || this.hasDisadvantage) {
|
||||||
bardRallyFaces = this.hasBarRally,
|
const dieFaces = this.advantageFaces,
|
||||||
advDie = new foundry.dice.terms.Die({ faces: dieFaces });
|
advDie = new foundry.dice.terms.Die({ faces: dieFaces, number: this.advantageNumber });
|
||||||
if (this.hasAdvantage || this.hasDisadvantage || bardRallyFaces)
|
if(this.advantageNumber > 1) advDie.modifiers = ['kh'];
|
||||||
this.terms.push(new foundry.dice.terms.OperatorTerm({ operator: this.hasDisadvantage ? '-' : '+' }));
|
this.terms.push(
|
||||||
if (bardRallyFaces) {
|
new foundry.dice.terms.OperatorTerm({ operator: this.hasDisadvantage ? '-' : '+' }),
|
||||||
const rallyDie = new foundry.dice.terms.Die({ faces: bardRallyFaces });
|
advDie
|
||||||
if (this.hasAdvantage) {
|
);
|
||||||
this.terms.push(
|
}
|
||||||
new foundry.dice.terms.PoolTerm({
|
if(this.rallyFaces)
|
||||||
terms: [advDie.formula, rallyDie.formula],
|
this.terms.push(
|
||||||
modifiers: ['kh']
|
new foundry.dice.terms.OperatorTerm({ operator: this.hasDisadvantage ? '-' : '+' }),
|
||||||
})
|
new foundry.dice.terms.Die({ faces: this.rallyFaces })
|
||||||
);
|
);
|
||||||
} 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
applyBaseBonus() {
|
applyBaseBonus() {
|
||||||
|
|
@ -138,6 +162,7 @@ export default class DualityRoll extends D20Roll {
|
||||||
|
|
||||||
static postEvaluate(roll, config = {}) {
|
static postEvaluate(roll, config = {}) {
|
||||||
super.postEvaluate(roll, config);
|
super.postEvaluate(roll, config);
|
||||||
|
|
||||||
config.roll.hope = {
|
config.roll.hope = {
|
||||||
dice: roll.dHope.denomination,
|
dice: roll.dHope.denomination,
|
||||||
value: roll.dHope.total
|
value: roll.dHope.total
|
||||||
|
|
@ -146,12 +171,19 @@ export default class DualityRoll extends D20Roll {
|
||||||
dice: roll.dFear.denomination,
|
dice: roll.dFear.denomination,
|
||||||
value: roll.dFear.total
|
value: roll.dFear.total
|
||||||
};
|
};
|
||||||
|
config.roll.rally = {
|
||||||
|
dice: roll.dRally?.denomination,
|
||||||
|
value: roll.dRally?.total
|
||||||
|
};
|
||||||
config.roll.result = {
|
config.roll.result = {
|
||||||
duality: roll.withHope ? 1 : roll.withFear ? -1 : 0,
|
duality: roll.withHope ? 1 : roll.withFear ? -1 : 0,
|
||||||
total: roll.dHope.total + roll.dFear.total,
|
total: roll.dHope.total + roll.dFear.total,
|
||||||
label: roll.totalLabel
|
label: roll.totalLabel
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if(roll._rallyIndex && roll.data?.parent)
|
||||||
|
roll.data.parent.deleteEmbeddedDocuments('ActiveEffect', [roll._rallyIndex]);
|
||||||
|
|
||||||
setDiceSoNiceForDualityRoll(roll, config.roll.advantage.type);
|
setDiceSoNiceForDualityRoll(roll, config.roll.advantage.type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,10 +55,24 @@ export default class DhActiveEffect extends ActiveEffect {
|
||||||
}
|
}
|
||||||
|
|
||||||
static applyField(model, change, field) {
|
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);
|
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) {
|
async toChat(origin) {
|
||||||
const cls = getDocumentClass('ChatMessage');
|
const cls = getDocumentClass('ChatMessage');
|
||||||
const systemData = {
|
const systemData = {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ export default class DHToken extends TokenDocument {
|
||||||
});
|
});
|
||||||
bars.sort((a, b) => a.label.compare(b.label));
|
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 values = attributes.value.reduce((acc, v) => {
|
||||||
const a = v.join('.');
|
const a = v.join('.');
|
||||||
if (invalidAttributes.some(x => a.startsWith(x))) return acc;
|
if (invalidAttributes.some(x => a.startsWith(x))) return acc;
|
||||||
|
|
@ -32,4 +32,26 @@ export default class DHToken extends TokenDocument {
|
||||||
|
|
||||||
return bars.concat(values);
|
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;
|
this.tooltip.innerHTML = html;
|
||||||
options.direction = this._determineItemTooltipDirection(element);
|
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 });
|
super.activate(element, { ...options, html: html });
|
||||||
}
|
}
|
||||||
|
|
||||||
_determineItemTooltipDirection(element) {
|
_determineItemTooltipDirection(element, prefered = this.constructor.TOOLTIP_DIRECTIONS.LEFT) {
|
||||||
const pos = element.getBoundingClientRect();
|
const pos = element.getBoundingClientRect();
|
||||||
const dirs = this.constructor.TOOLTIP_DIRECTIONS;
|
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/settings/components/settings-item-line.hbs',
|
||||||
'systems/daggerheart/templates/ui/chat/parts/damage-chat.hbs',
|
'systems/daggerheart/templates/ui/chat/parts/damage-chat.hbs',
|
||||||
'systems/daggerheart/templates/ui/chat/parts/target-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;
|
font-size: 14px;
|
||||||
line-height: 17px;
|
line-height: 17px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.advantage-chip-tooltip {
|
||||||
|
pointer-events: all;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.advantage-chip {
|
.advantage-chip {
|
||||||
|
|
|
||||||
|
|
@ -1,81 +1,75 @@
|
||||||
@import '../../utils/spacing.less';
|
@import '../../utils/spacing.less';
|
||||||
@import '../../utils/colors.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-container {
|
||||||
.downtime-header {
|
.activities-grouping {
|
||||||
margin: 0;
|
width: 280px;
|
||||||
color: light-dark(@dark-blue, @golden);
|
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.activity-container {
|
.activities-container {
|
||||||
display: flex;
|
width: 100%;
|
||||||
align-items: center;
|
|
||||||
padding: 8px;
|
|
||||||
|
|
||||||
.activity-title {
|
.activity-container {
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 8px;
|
||||||
|
|
||||||
.activity-title-text {
|
.activity-inner-container {
|
||||||
font-size: 24px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-image {
|
|
||||||
width: 80px;
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
align-items: center;
|
||||||
margin-right: 8px;
|
gap: 4px;
|
||||||
border: 2px solid black;
|
|
||||||
border-radius: 50%;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.activity-select-label {
|
.activity-marker {
|
||||||
position: absolute;
|
font-size: 8px;
|
||||||
top: -9px;
|
flex: none;
|
||||||
font-size: 14px;
|
color: light-dark(#18162e, #f3c267);
|
||||||
border: 1px solid light-dark(@dark-blue, @golden);
|
margin-right: 4px;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
.activity-select-section {
|
||||||
border-radius: 50%;
|
display: flex;
|
||||||
}
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
&:hover,
|
.activity-icon {
|
||||||
&.selected {
|
min-width: 24px;
|
||||||
filter: drop-shadow(0 0 6px gold);
|
text-align: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-name-input {
|
.activity-selected-marker {
|
||||||
font-size: 24px;
|
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;
|
font-weight: bold;
|
||||||
padding: 0;
|
|
||||||
background: transparent;
|
|
||||||
color: rgb(239, 230, 216);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.activity-body {
|
|
||||||
flex: 1;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.downtime {
|
footer {
|
||||||
.activity-text-area {
|
margin-top: 8px;
|
||||||
resize: none;
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
button {
|
||||||
|
flex: 1;
|
||||||
|
font-family: 'Montserrat', sans-serif;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,22 @@
|
||||||
height: 40px;
|
height: 40px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
.feature-tab {
|
.navigation-container {
|
||||||
border: none;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
a {
|
.navigation-inner-container {
|
||||||
color: light-dark(@dark-blue, @golden);
|
flex: 1;
|
||||||
font-family: @font-body;
|
|
||||||
|
.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-color: light-dark(@dark, @beige);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||||
z-index: 200;
|
|
||||||
max-height: 400px !important;
|
max-height: 400px !important;
|
||||||
width: fit-content !important;
|
width: fit-content !important;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,20 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 4px;
|
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 {
|
.tooltip-title {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,14 @@
|
||||||
{{#times (rollParsed item.system.resource.max actor item numerical=true)}}
|
{{#times (rollParsed item.system.resource.max actor item numerical=true)}}
|
||||||
{{#with (ifThen (lookup ../diceStates this) (lookup ../diceStates this) this) as | state |}}
|
{{#with (ifThen (lookup ../diceStates this) (lookup ../diceStates this) this) as | state |}}
|
||||||
<div class="resource-item" data-dice="{{#if ../../this}}{{../this}}{{else}}{{state}}{{/if}}">
|
<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}}" />
|
<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/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"}}" />
|
||||||
</div>
|
</div>
|
||||||
{{/with}}
|
{{/with}}
|
||||||
{{/times}}
|
{{/times}}
|
||||||
</div>
|
</div>
|
||||||
<footer>
|
<footer>
|
||||||
<button data-action="save">{{localize 'Save'}}</button>
|
<button type="button" data-action="save">{{localize 'Save'}}</button>
|
||||||
<button data-action="rerollDice">{{localize "DAGGERHEART.APPLICATIONS.ResourceDice.rerollDice"}}</button>
|
<button type="button" data-action="rerollDice">{{localize "DAGGERHEART.APPLICATIONS.ResourceDice.rerollDice"}}</button>
|
||||||
</footer>
|
</footer>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -88,7 +88,7 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</fieldset>
|
</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>
|
<legend>Modifiers</legend>
|
||||||
<div class="nest-inputs">
|
<div class="nest-inputs">
|
||||||
<button class="advantage-chip flex1 {{#if (eq advantage 1)}}selected{{/if}}" data-action="updateIsAdvantage" data-advantage="1">
|
<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>
|
<span><i class="fa-regular fa-circle"></i></span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<span class="label">{{localize "DAGGERHEART.GENERAL.Advantage.full"}}</span>
|
<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>
|
||||||
<button class="disadvantage-chip flex1 {{#if (eq advantage -1)}}selected{{/if}}" data-action="updateIsAdvantage" data-advantage="-1">
|
<button class="disadvantage-chip flex1 {{#if (eq advantage -1)}}selected{{/if}}" data-action="updateIsAdvantage" data-advantage="-1">
|
||||||
{{#if (eq advantage -1)}}
|
{{#if (eq advantage -1)}}
|
||||||
|
|
@ -106,14 +109,31 @@
|
||||||
<span><i class="fa-regular fa-circle"></i></span>
|
<span><i class="fa-regular fa-circle"></i></span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<span class="label">{{localize "DAGGERHEART.GENERAL.Disadvantage.full"}}</span>
|
<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>
|
</button>
|
||||||
{{#unless (eq @root.rollType 'D20Roll')}}
|
</div>
|
||||||
<select name="roll.dice.advantageFaces">
|
{{#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}}
|
{{selectOptions diceOptions selected=@root.roll.dAdvantage.denomination}}
|
||||||
</select>
|
</select>
|
||||||
{{/unless}}
|
</div>
|
||||||
</div>
|
{{/unless}}
|
||||||
<input type="text" value="{{extraFormula}}" name="extraFormula" placeholder="Situational Bonus">
|
{{#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>
|
</fieldset>
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
<span class="formula-label"><b>Formula:</b> {{@root.formula}}</span>
|
<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>
|
</div>
|
||||||
{{formGroup settingFields.schema.fields.actionPoints value=settingFields._source.actionPoints localize=true}}
|
{{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">
|
<footer class="form-footer">
|
||||||
<button data-action="reset">
|
<button data-action="reset">
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,17 @@
|
||||||
<div>
|
<div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{localize "Icon"}}</label>
|
||||||
|
|
||||||
|
<div class="form-field">
|
||||||
|
<input type="text" name="icon" value="{{icon}}" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>{{localize "Description"}}</legend>
|
<legend>{{localize "Description"}}</legend>
|
||||||
|
|
||||||
<prose-mirror name="description" value="{{description}}" toggled=true>
|
<prose-mirror name="description" value="{{description}}">
|
||||||
{{{ enrichedDescription }}}
|
{{{ enrichedDescription }}}
|
||||||
</prose-mirror>
|
</prose-mirror>
|
||||||
</fieldset>
|
</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>
|
</div>
|
||||||
|
|
@ -116,5 +116,14 @@
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</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>
|
</header>
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
<a class="item-resource" data-action="toggleResourceDice" data-dice="{{#if ../../this}}{{../this}}{{else}}{{state}}{{/if}}">
|
<a class="item-resource" data-action="toggleResourceDice" data-dice="{{#if ../../this}}{{../this}}{{else}}{{state}}{{/if}}">
|
||||||
<div class="item-dice-resource">
|
<div class="item-dice-resource">
|
||||||
<label>{{ifThen state.value state.value '?'}}</label>
|
<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}}
|
{{#if state.used}}<i class="fa-solid fa-x"></i>{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
{{formGroup systemFields.resource.fields.value value=source.system.resource.value localize=true}}
|
{{formGroup systemFields.resource.fields.value value=source.system.resource.value localize=true}}
|
||||||
{{formGroup systemFields.resource.fields.max value=source.system.resource.max localize=true}}
|
{{formGroup systemFields.resource.fields.max value=source.system.resource.max localize=true}}
|
||||||
{{else}}
|
{{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}}
|
{{formGroup systemFields.resource.fields.max value=source.system.resource.max label="DAGGERHEART.ITEMS.FIELDS.resource.amount.label" localize=true}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,18 @@
|
||||||
<section class='tab-navigation'>
|
<section class='tab-navigation'>
|
||||||
<line-div></line-div>
|
<div class='navigation-container'>
|
||||||
<nav class='feature-tab sheet-tabs tabs' data-group='primary'>
|
<div class="navigation-inner-container">
|
||||||
{{#each tabs as |tab|}}
|
<line-div></line-div>
|
||||||
<a class='{{tab.id}} {{tab.cssClass}}' data-action='tab' data-group='{{tab.group}}' data-tab='{{tab.id}}'>
|
<nav class='feature-tab sheet-tabs tabs' data-group='primary'>
|
||||||
{{localize tab.label}}
|
{{#each tabs as |tab|}}
|
||||||
</a>
|
<a class='{{tab.id}} {{tab.cssClass}}' data-action='tab' data-group='{{tab.group}}' data-tab='{{tab.id}}'>
|
||||||
{{/each}}
|
{{localize tab.label}}
|
||||||
</nav>
|
</a>
|
||||||
<line-div></line-div>
|
{{/each}}
|
||||||
|
</nav>
|
||||||
|
<line-div></line-div>
|
||||||
|
</div>
|
||||||
|
{{#if @partial-block}}
|
||||||
|
{{> @partial-block }}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -8,8 +8,6 @@
|
||||||
|
|
||||||
<span>{{localize "DAGGERHEART.GENERAL.type"}}</span>
|
<span>{{localize "DAGGERHEART.GENERAL.type"}}</span>
|
||||||
{{formField systemFields.type value=source.system.type localize=true}}
|
{{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>
|
<span>{{localize "DAGGERHEART.GENERAL.Domain.single"}}</span>
|
||||||
{{formField systemFields.domain value=source.system.domain localize=true}}
|
{{formField systemFields.domain value=source.system.domain localize=true}}
|
||||||
<span>{{localize "DAGGERHEART.GENERAL.level"}}</span>
|
<span>{{localize "DAGGERHEART.GENERAL.level"}}</span>
|
||||||
|
|
|
||||||
|
|
@ -3,42 +3,42 @@
|
||||||
data-tab='{{tabs.features.id}}'
|
data-tab='{{tabs.features.id}}'
|
||||||
data-group='{{tabs.features.group}}'
|
data-group='{{tabs.features.group}}'
|
||||||
>
|
>
|
||||||
<fieldset class="drop-section" data-type="foundationFeature">
|
<fieldset class="drop-section" data-type="foundation">
|
||||||
<legend>
|
<legend>
|
||||||
{{localize "DAGGERHEART.GENERAL.Tabs.foundation"}}
|
{{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>
|
</legend>
|
||||||
|
|
||||||
<div class="feature-list">
|
<div class="feature-list">
|
||||||
{{#if source.system.foundationFeature}}
|
{{#each source.system.foundationFeatures as | feature | }}
|
||||||
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' type='foundationFeature' feature=source.system.foundationFeature}}
|
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' type='foundation' feature=feature}}
|
||||||
{{/if}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<fieldset class="drop-section" data-type="specializationFeature">
|
<fieldset class="drop-section" data-type="specialization">
|
||||||
<legend>
|
<legend>
|
||||||
{{localize "DAGGERHEART.GENERAL.Tabs.specialization"}}
|
{{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>
|
</legend>
|
||||||
|
|
||||||
<div class="feature-list">
|
<div class="feature-list">
|
||||||
{{#if source.system.specializationFeature}}
|
{{#each source.system.specializationFeatures as | feature |}}
|
||||||
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' type='specializationFeature' feature=source.system.specializationFeature}}
|
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' type='specialization' feature=feature}}
|
||||||
{{/if}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<fieldset class="drop-section" data-type="masteryFeature">
|
<fieldset class="drop-section" data-type="mastery">
|
||||||
<legend>
|
<legend>
|
||||||
{{localize "DAGGERHEART.GENERAL.Tabs.mastery"}}
|
{{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>
|
</legend>
|
||||||
|
|
||||||
<div class="feature-list">
|
<div class="feature-list">
|
||||||
{{#if source.system.masteryFeature}}
|
{{#each source.system.masteryFeatures as | feature |}}
|
||||||
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' type='masteryFeature' feature=source.system.masteryFeature}}
|
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' type='mastery' feature=feature}}
|
||||||
{{/if}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -16,6 +16,11 @@
|
||||||
{{localize "DAGGERHEART.GENERAL.Disadvantage.full"}}
|
{{localize "DAGGERHEART.GENERAL.Disadvantage.full"}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{#if roll.rally.dice}}
|
||||||
|
<div class="duality-modifier">
|
||||||
|
{{localize "DAGGERHEART.CLASS.Feature.rallyDice"}} {{roll.rally.dice}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<div class="dice-result">
|
<div class="dice-result">
|
||||||
<div class="dice-formula">{{roll.formula}}</div>
|
<div class="dice-formula">{{roll.formula}}</div>
|
||||||
|
|
@ -38,7 +43,7 @@
|
||||||
<div class="dice-title">{{localize "DAGGERHEART.GENERAL.hope"}}</div>
|
<div class="dice-title">{{localize "DAGGERHEART.GENERAL.hope"}}</div>
|
||||||
<div class="dice-inner-container hope" title="{{localize "DAGGERHEART.GENERAL.hope"}}">
|
<div class="dice-inner-container hope" title="{{localize "DAGGERHEART.GENERAL.hope"}}">
|
||||||
<div class="dice-wrapper">
|
<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>
|
||||||
<div class="dice-value">{{roll.hope.value}}</div>
|
<div class="dice-value">{{roll.hope.value}}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -49,7 +54,7 @@
|
||||||
<div class="dice-title">{{localize "DAGGERHEART.GENERAL.fear"}}</div>
|
<div class="dice-title">{{localize "DAGGERHEART.GENERAL.fear"}}</div>
|
||||||
<div class="dice-inner-container fear" title="{{localize "DAGGERHEART.GENERAL.fear"}}">
|
<div class="dice-inner-container fear" title="{{localize "DAGGERHEART.GENERAL.fear"}}">
|
||||||
<div class="dice-wrapper">
|
<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>
|
||||||
<div class="dice-value">{{roll.fear.value}}</div>
|
<div class="dice-value">{{roll.fear.value}}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -72,7 +77,7 @@
|
||||||
<div class="dice-container">
|
<div class="dice-container">
|
||||||
<div class="dice-inner-container {{#if (eq roll.advantage.type 1)}}advantage{{else}}disadvantage{{/if}}">
|
<div class="dice-inner-container {{#if (eq roll.advantage.type 1)}}advantage{{else}}disadvantage{{/if}}">
|
||||||
<div class="dice-wrapper">
|
<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>
|
||||||
<div class="dice-value">{{roll.advantage.value}}</div>
|
<div class="dice-value">{{roll.advantage.value}}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -82,6 +87,30 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/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 | }}
|
{{#each roll.extra as | extra | }}
|
||||||
<div class="dice">
|
<div class="dice">
|
||||||
<header class="part-header flexrow">
|
<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