mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-17 15:39:02 +01:00
Merge branch 'main' into iss4-attach-items
This commit is contained in:
commit
f61cc23daa
93 changed files with 3803 additions and 2390 deletions
|
|
@ -41,10 +41,14 @@ Hooks.once('init', () => {
|
|||
]
|
||||
);
|
||||
|
||||
CONFIG.statusEffects = Object.values(SYSTEM.GENERAL.conditions).map(x => ({
|
||||
...x,
|
||||
name: game.i18n.localize(x.name)
|
||||
}));
|
||||
CONFIG.statusEffects = [
|
||||
...CONFIG.statusEffects,
|
||||
...Object.values(SYSTEM.GENERAL.conditions).map(x => ({
|
||||
...x,
|
||||
name: game.i18n.localize(x.name),
|
||||
systemEffect: true
|
||||
}))
|
||||
];
|
||||
|
||||
CONFIG.Dice.daggerheart = {
|
||||
DualityDie: DualityDie,
|
||||
|
|
@ -108,6 +112,8 @@ Hooks.once('init', () => {
|
|||
}
|
||||
);
|
||||
|
||||
CONFIG.Token.hudClass = applications.hud.DHTokenHUD;
|
||||
|
||||
CONFIG.Combat.dataModels = {
|
||||
base: models.DhCombat
|
||||
};
|
||||
|
|
|
|||
41
lang/en.json
41
lang/en.json
|
|
@ -279,6 +279,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"HUD": {
|
||||
"tokenHUD": {
|
||||
"genericEffects": "Foundry Effects"
|
||||
}
|
||||
},
|
||||
"Levelup": {
|
||||
"actions": {
|
||||
"creatureComfort": {
|
||||
|
|
@ -487,14 +492,10 @@
|
|||
"name": "Impenetrable",
|
||||
"description": "Once per short rest, when you would mark your last Hit Point, you can instead mark a Stress."
|
||||
},
|
||||
"magic": {
|
||||
"name": "Magic",
|
||||
"magical": {
|
||||
"name": "Magical",
|
||||
"description": "You can't mark an Armor Slot to reduce physical damage."
|
||||
},
|
||||
"painful": {
|
||||
"name": "Painful",
|
||||
"description": "Each time you mark an Armor Slot, you must mark a Stress."
|
||||
},
|
||||
"physical": {
|
||||
"name": "Physical",
|
||||
"description": "You can't mark an Armor Slot to reduce magic damage."
|
||||
|
|
@ -898,6 +899,10 @@
|
|||
"name": "Scary",
|
||||
"description": "On a successful attack, the target must mark a Stress."
|
||||
},
|
||||
"selfCorrecting": {
|
||||
"name": "Self Correcting",
|
||||
"description": "When you roll a 1 on a damage die, it deals 6 damage instead."
|
||||
},
|
||||
"serrated": {
|
||||
"name": "Serrated",
|
||||
"description": "When you roll a 1 on a damage die, it deals 8 damage instead."
|
||||
|
|
@ -973,6 +978,10 @@
|
|||
"singular": "Character",
|
||||
"plural": "Characters"
|
||||
},
|
||||
"Cost": {
|
||||
"single": "Cost",
|
||||
"plural": "Costs"
|
||||
},
|
||||
"Damage": {
|
||||
"severe": "Severe",
|
||||
"major": "Major",
|
||||
|
|
@ -1078,6 +1087,7 @@
|
|||
"specialization": "Specialization",
|
||||
"mastery": "Mastery",
|
||||
"optional": "Optional",
|
||||
"recovery": "Recovery",
|
||||
"setup": "Setup",
|
||||
"equipment": "Equipment",
|
||||
"attachments": "Attachments"
|
||||
|
|
@ -1100,6 +1110,8 @@
|
|||
"burden": "Burden",
|
||||
"check": "{check} Check",
|
||||
"criticalSuccess": "Critical Success",
|
||||
"damage": "Damage",
|
||||
"damageType": "Damage Type",
|
||||
"description": "Description",
|
||||
"duality": "Duality",
|
||||
"dualityRoll": "Duality Roll",
|
||||
|
|
@ -1112,17 +1124,25 @@
|
|||
"inactiveEffects": "Inactive Effects",
|
||||
"inventory": "Inventory",
|
||||
"level": "Level",
|
||||
"max": "Max",
|
||||
"modifier": "Modifier",
|
||||
"multiclass": "Multiclass",
|
||||
"none": "None",
|
||||
"quantity": "Quantity",
|
||||
"range": "Range",
|
||||
"recovery": "Recovery",
|
||||
"scalable": "Scalable",
|
||||
"stress": "Stress",
|
||||
"take": "Take",
|
||||
"target": "Target",
|
||||
"title": "Title",
|
||||
"true": "True",
|
||||
"type": "Type",
|
||||
"unarmored": "Unarmored",
|
||||
"use": "Use"
|
||||
"use": "Use",
|
||||
"used": "Used",
|
||||
"uses": "Uses",
|
||||
"value": "Value"
|
||||
},
|
||||
"ITEMS": {
|
||||
"Armor": {
|
||||
|
|
@ -1179,6 +1199,7 @@
|
|||
"spellcastingTrait": "Spellcasting Trait"
|
||||
},
|
||||
"Weapon": {
|
||||
"weaponType": "Weapon Type",
|
||||
"primaryWeapon": "Primary Weapon",
|
||||
"secondaryWeapon": "Secondary Weapon"
|
||||
}
|
||||
|
|
@ -1186,7 +1207,8 @@
|
|||
"SETTINGS": {
|
||||
"Appearance": {
|
||||
"FIELDS": {
|
||||
"displayFear": { "label": "Fear Display" }
|
||||
"displayFear": { "label": "Fear Display" },
|
||||
"showGenericStatusEffects": { "label": "Show Foundry Status Effects" }
|
||||
},
|
||||
"fearDisplay": {
|
||||
"token": "Tokens",
|
||||
|
|
@ -1386,7 +1408,8 @@
|
|||
"unequip": "Unequip",
|
||||
"sendToVault": "Send to Vault",
|
||||
"sendToLoadout": "Send to Loadout",
|
||||
"makeDeathMove": "Make a Death Move"
|
||||
"makeDeathMove": "Make a Death Move",
|
||||
"rangeAndTarget": "Range & Target"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
export * as characterCreation from './characterCreation/_module.mjs';
|
||||
export * as dialogs from './dialogs/_module.mjs';
|
||||
export * as hud from './hud/_module.mjs';
|
||||
export * as levelup from './levelup/_module.mjs';
|
||||
export * as settings from './settings/_module.mjs';
|
||||
export * as sheets from './sheets/_module.mjs';
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
const context = await super._prepareContext(_options);
|
||||
context.rollConfig = this.config;
|
||||
context.hasRoll = !!this.config.roll;
|
||||
context.canRoll = true;
|
||||
if (this.config.costs?.length) {
|
||||
const updatedCosts = this.action.calcCosts(this.config.costs);
|
||||
context.costs = updatedCosts;
|
||||
|
|
@ -84,7 +85,6 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
context.advantage = this.config.roll?.advantage;
|
||||
context.disadvantage = this.config.roll?.disadvantage;
|
||||
context.diceOptions = CONFIG.DH.GENERAL.diceTypes;
|
||||
context.canRoll = true;
|
||||
context.isLite = this.config.roll?.lite;
|
||||
context.extraFormula = this.config.extraFormula;
|
||||
context.formula = this.roll.constructFormula(this.config);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { damageKeyToNumber, getDamageLabel } from '../../helpers/utils.mjs';
|
|||
const { DialogV2, ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
|
||||
export default class DamageReductionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor(resolve, reject, actor, damage) {
|
||||
constructor(resolve, reject, actor, damage, damageType) {
|
||||
super({});
|
||||
|
||||
this.resolve = resolve;
|
||||
|
|
@ -11,37 +11,46 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
|
|||
this.actor = actor;
|
||||
this.damage = damage;
|
||||
|
||||
const maxArmorMarks = Math.min(
|
||||
actor.system.armorScore - actor.system.armor.system.marks.value,
|
||||
actor.system.rules.maxArmorMarked.total
|
||||
);
|
||||
const canApplyArmor = actor.system.armorApplicableDamageTypes[damageType];
|
||||
const maxArmorMarks = canApplyArmor
|
||||
? Math.min(
|
||||
actor.system.armorScore - actor.system.armor.system.marks.value,
|
||||
actor.system.rules.damageReduction.maxArmorMarked.total
|
||||
)
|
||||
: 0;
|
||||
|
||||
const armor = [...Array(maxArmorMarks).keys()].reduce((acc, _) => {
|
||||
acc[foundry.utils.randomID()] = { selected: false };
|
||||
return acc;
|
||||
}, {});
|
||||
const stress = [...Array(actor.system.rules.maxArmorMarked.stressExtra ?? 0).keys()].reduce((acc, _) => {
|
||||
acc[foundry.utils.randomID()] = { selected: false };
|
||||
return acc;
|
||||
}, {});
|
||||
const stress = [...Array(actor.system.rules.damageReduction.maxArmorMarked.stressExtra ?? 0).keys()].reduce(
|
||||
(acc, _) => {
|
||||
acc[foundry.utils.randomID()] = { selected: false };
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
this.marks = { armor, stress };
|
||||
|
||||
this.availableStressReductions = Object.keys(actor.system.rules.stressDamageReduction).reduce((acc, key) => {
|
||||
const dr = actor.system.rules.stressDamageReduction[key];
|
||||
if (dr.enabled) {
|
||||
if (acc === null) acc = {};
|
||||
this.availableStressReductions = Object.keys(actor.system.rules.damageReduction.stressDamageReduction).reduce(
|
||||
(acc, key) => {
|
||||
const dr = actor.system.rules.damageReduction.stressDamageReduction[key];
|
||||
if (dr.enabled) {
|
||||
if (acc === null) acc = {};
|
||||
|
||||
const damage = damageKeyToNumber(key);
|
||||
acc[damage] = {
|
||||
cost: dr.cost,
|
||||
selected: false,
|
||||
from: getDamageLabel(damage),
|
||||
to: getDamageLabel(damage - 1)
|
||||
};
|
||||
}
|
||||
const damage = damageKeyToNumber(key);
|
||||
acc[damage] = {
|
||||
cost: dr.cost,
|
||||
selected: false,
|
||||
from: getDamageLabel(damage),
|
||||
to: getDamageLabel(damage - 1)
|
||||
};
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, null);
|
||||
return acc;
|
||||
},
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
get title() {
|
||||
|
|
@ -90,7 +99,8 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
|
|||
|
||||
context.armorScore = this.actor.system.armorScore;
|
||||
context.armorMarks = currentMarks;
|
||||
context.basicMarksUsed = selectedArmorMarks.length === this.actor.system.rules.maxArmorMarked.total;
|
||||
context.basicMarksUsed =
|
||||
selectedArmorMarks.length === this.actor.system.rules.damageReduction.maxArmorMarked.total;
|
||||
|
||||
const stressReductionStress = this.availableStressReductions
|
||||
? stressReductions.reduce((acc, red) => acc + red.cost, 0)
|
||||
|
|
@ -122,12 +132,15 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
|
|||
getDamageInfo = () => {
|
||||
const selectedArmorMarks = Object.values(this.marks.armor).filter(x => x.selected);
|
||||
const selectedStressMarks = Object.values(this.marks.stress).filter(x => x.selected);
|
||||
const stressReductions = Object.values(this.availableStressReductions ?? {}).filter(red => red.selected);
|
||||
const stressReductions = this.availableStressReductions
|
||||
? Object.values(this.availableStressReductions).filter(red => red.selected)
|
||||
: [];
|
||||
const currentMarks =
|
||||
this.actor.system.armor.system.marks.value + selectedArmorMarks.length + selectedStressMarks.length;
|
||||
|
||||
const currentDamage =
|
||||
this.damage - selectedArmorMarks.length - selectedStressMarks.length - stressReductions.length;
|
||||
const armorMarkReduction =
|
||||
selectedArmorMarks.length * this.actor.system.rules.damageReduction.increasePerArmorMark;
|
||||
const currentDamage = this.damage - armorMarkReduction - selectedStressMarks.length - stressReductions.length;
|
||||
|
||||
return { selectedArmorMarks, selectedStressMarks, stressReductions, currentMarks, currentDamage };
|
||||
};
|
||||
|
|
@ -216,11 +229,11 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
|
|||
await super.close({});
|
||||
}
|
||||
|
||||
static async armorStackQuery({actorId, damage}) {
|
||||
static async armorStackQuery({ actorId, damage, type }) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const actor = await fromUuid(actorId);
|
||||
if(!actor || !actor?.isOwner) reject();
|
||||
new DamageReductionDialog(resolve, reject, actor, damage).render({ force: true });
|
||||
})
|
||||
if (!actor || !actor?.isOwner) reject();
|
||||
new DamageReductionDialog(resolve, reject, actor, damage, type).render({ force: true });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1
module/applications/hud/_module.mjs
Normal file
1
module/applications/hud/_module.mjs
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { default as DHTokenHUD } from './tokenHud.mjs';
|
||||
84
module/applications/hud/tokenHUD.mjs
Normal file
84
module/applications/hud/tokenHUD.mjs
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
export default class DHTokenHUD extends TokenHUD {
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ['daggerheart']
|
||||
};
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
hud: {
|
||||
root: true,
|
||||
template: 'systems/daggerheart/templates/hud/tokenHUD.hbs'
|
||||
}
|
||||
};
|
||||
|
||||
async _prepareContext(options) {
|
||||
const context = await super._prepareContext(options);
|
||||
context.systemStatusEffects = Object.keys(context.statusEffects).reduce((acc, key) => {
|
||||
const effect = context.statusEffects[key];
|
||||
if (effect.systemEffect) acc[key] = effect;
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const useGeneric = game.settings.get(
|
||||
CONFIG.DH.id,
|
||||
CONFIG.DH.SETTINGS.gameSettings.appearance
|
||||
).showGenericStatusEffects;
|
||||
context.genericStatusEffects = useGeneric
|
||||
? Object.keys(context.statusEffects).reduce((acc, key) => {
|
||||
const effect = context.statusEffects[key];
|
||||
if (!effect.systemEffect) acc[key] = effect;
|
||||
|
||||
return acc;
|
||||
}, {})
|
||||
: null;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
_getStatusEffectChoices() {
|
||||
// Include all HUD-enabled status effects
|
||||
const choices = {};
|
||||
for (const status of CONFIG.statusEffects) {
|
||||
if (
|
||||
status.hud === false ||
|
||||
(foundry.utils.getType(status.hud) === 'Object' &&
|
||||
status.hud.actorTypes?.includes(this.document.actor.type) === false)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
choices[status.id] = {
|
||||
_id: status._id,
|
||||
id: status.id,
|
||||
systemEffect: status.systemEffect,
|
||||
title: game.i18n.localize(status.name ?? /** @deprecated since v12 */ status.label),
|
||||
src: status.img ?? /** @deprecated since v12 */ status.icon,
|
||||
isActive: false,
|
||||
isOverlay: false
|
||||
};
|
||||
}
|
||||
|
||||
// Update the status of effects which are active for the token actor
|
||||
const activeEffects = this.actor?.effects || [];
|
||||
for (const effect of activeEffects) {
|
||||
for (const statusId of effect.statuses) {
|
||||
const status = choices[statusId];
|
||||
if (!status) continue;
|
||||
if (status._id) {
|
||||
if (status._id !== effect.id) continue;
|
||||
} else {
|
||||
if (effect.statuses.size !== 1) continue;
|
||||
}
|
||||
status.isActive = true;
|
||||
if (effect.getFlag('core', 'overlay')) status.isOverlay = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Flag status CSS class
|
||||
for (const status of Object.values(choices)) {
|
||||
status.cssClass = [status.isActive ? 'active' : null, status.isOverlay ? 'overlay' : null].filterJoin(' ');
|
||||
}
|
||||
return choices;
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ export default class AdversarySheet extends DHBaseActorSheet {
|
|||
actions: {
|
||||
reactionRoll: AdversarySheet.#reactionRoll,
|
||||
useItem: this.useItem,
|
||||
useAction: this.useItem,
|
||||
toChat: this.toChat
|
||||
},
|
||||
window: {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
levelManagement: CharacterSheet.#levelManagement,
|
||||
toggleEquipItem: CharacterSheet.#toggleEquipItem,
|
||||
useItem: this.useItem, //TODO Fix this
|
||||
useAction: this.useAction,
|
||||
toChat: this.toChat
|
||||
},
|
||||
window: {
|
||||
|
|
@ -625,6 +626,20 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use an action
|
||||
* @type {ApplicationClickAction}
|
||||
*/
|
||||
static async useAction(event, button) {
|
||||
const item = this.getItem(button);
|
||||
if (!item) return;
|
||||
|
||||
const action = item.system.actions.find(x => x.id === button.dataset.actionId);
|
||||
if (!action) return;
|
||||
|
||||
action.use(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send item to Chat
|
||||
* @type {ApplicationClickAction}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ export default class DhpEnvironment extends DHBaseActorSheet {
|
|||
},
|
||||
actions: {
|
||||
useItem: this.useItem,
|
||||
useAction: this.useItem,
|
||||
toChat: this.toChat
|
||||
},
|
||||
dragDrop: [{ dragSelector: '.action-section .inventory-item', dropSelector: null }]
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export default class ArmorSheet extends ItemAttachmentSheet(DHBaseItemSheet) {
|
|||
|
||||
switch (partId) {
|
||||
case 'settings':
|
||||
context.features = this.document.system.features.map(x => x.value);
|
||||
context.features = this.document.system.armorFeatures.map(x => x.value);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -48,6 +48,6 @@ export default class ArmorSheet extends ItemAttachmentSheet(DHBaseItemSheet) {
|
|||
* @param {Array<Object>} selectedOptions - The currently selected tag objects.
|
||||
*/
|
||||
static async #onFeatureSelect(selectedOptions) {
|
||||
await this.document.update({ 'system.features': selectedOptions.map(x => ({ value: x.value })) });
|
||||
await this.document.update({ 'system.armorFeatures': selectedOptions.map(x => ({ value: x.value })) });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ export default class WeaponSheet extends ItemAttachmentSheet(DHBaseItemSheet) {
|
|||
await super._preparePartContext(partId, context);
|
||||
switch (partId) {
|
||||
case 'settings':
|
||||
context.features = this.document.system.features.map(x => x.value);
|
||||
context.features = this.document.system.weaponFeatures.map(x => x.value);
|
||||
context.systemFields.attack.fields = this.document.system.attack.schema.fields;
|
||||
break;
|
||||
}
|
||||
|
|
@ -47,6 +47,6 @@ export default class WeaponSheet extends ItemAttachmentSheet(DHBaseItemSheet) {
|
|||
* @param {Array<Object>} selectedOptions - The currently selected tag objects.
|
||||
*/
|
||||
static async #onFeatureSelect(selectedOptions) {
|
||||
await this.document.update({ 'system.features': selectedOptions.map(x => ({ value: x.value })) });
|
||||
await this.document.update({ 'system.weaponFeatures': selectedOptions.map(x => ({ value: x.value })) });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,8 +80,8 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
actor.system.attack?._id === actionId
|
||||
? actor.system.attack
|
||||
: item.system.attack?._id === actionId
|
||||
? item.system.attack
|
||||
: item?.system?.actions?.find(a => a._id === actionId);
|
||||
? item.system.attack
|
||||
: item?.system?.actions?.find(a => a._id === actionId);
|
||||
return action;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ export const adversaryTypes = {
|
|||
},
|
||||
social: {
|
||||
id: 'social',
|
||||
label: 'DAGGERHEART.CONFIG.AdversaryTypee.social.label',
|
||||
label: 'DAGGERHEART.CONFIG.AdversaryType.social.label',
|
||||
description: 'DAGGERHEART.ACTORS.Adversary.social.description'
|
||||
},
|
||||
solo: {
|
||||
|
|
|
|||
|
|
@ -59,13 +59,13 @@ export const damageTypes = {
|
|||
id: 'physical',
|
||||
label: 'DAGGERHEART.CONFIG.DamageType.physical.name',
|
||||
abbreviation: 'DAGGERHEART.CONFIG.DamageType.physical.abbreviation',
|
||||
icon: ["fa-hand-fist"]
|
||||
icon: ['fa-hand-fist']
|
||||
},
|
||||
magical: {
|
||||
id: 'magical',
|
||||
label: 'DAGGERHEART.CONFIG.DamageType.magical.name',
|
||||
abbreviation: 'DAGGERHEART.CONFIG.DamageType.magical.abbreviation',
|
||||
icon: ["fa-wand-sparkles"]
|
||||
icon: ['fa-wand-sparkles']
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -14,12 +14,12 @@ export default class DHAttackAction extends DHDamageAction {
|
|||
|
||||
prepareData() {
|
||||
super.prepareData();
|
||||
if(!!this.item?.system?.attack) {
|
||||
if (!!this.item?.system?.attack) {
|
||||
if (this.damage.includeBase) {
|
||||
const baseDamage = this.getParentDamage();
|
||||
this.damage.parts.unshift(new DHDamageData(baseDamage));
|
||||
}
|
||||
if(this.roll.useDefault) {
|
||||
if (this.roll.useDefault) {
|
||||
this.roll.trait = this.item.system.attack.roll.trait;
|
||||
this.roll.type = 'weapon';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
|||
updateSource['range'] = parent?.system?.attack?.range;
|
||||
updateSource['roll'] = {
|
||||
useDefault: true
|
||||
}
|
||||
};
|
||||
} else {
|
||||
if (parent?.system?.trait) {
|
||||
updateSource['roll'] = {
|
||||
|
|
@ -180,8 +180,8 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
|||
const actorData = this.actor.getRollData(false);
|
||||
|
||||
// Remove when included directly in Actor getRollData
|
||||
actorData.prof = actorData.proficiency?.value ?? 1;
|
||||
actorData.cast = actorData.spellcast?.value ?? 1;
|
||||
actorData.prof = actorData.proficiency?.total ?? 1;
|
||||
actorData.cast = actorData.spellcast?.total ?? 1;
|
||||
actorData.result = data.roll?.total ?? 1;
|
||||
/* actorData.scale = data.costs?.length
|
||||
? data.costs.reduce((a, c) => {
|
||||
|
|
@ -295,7 +295,7 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
|||
}
|
||||
|
||||
prepareTarget() {
|
||||
if(!this.target?.type) return [];
|
||||
if (!this.target?.type) return [];
|
||||
let targets;
|
||||
if (this.target?.type === CONFIG.DH.ACTIONS.targetTypes.self.id)
|
||||
targets = this.constructor.formatTarget(this.actor.token ?? this.actor.prototypeToken);
|
||||
|
|
@ -337,7 +337,8 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
|||
const resources = config.costs
|
||||
.filter(c => c.enabled !== false)
|
||||
.map(c => {
|
||||
return { type: c.type, value: (c.total ?? c.value) * -1 };
|
||||
const resource = this.actor.system.resources[c.type];
|
||||
return { type: c.type, value: (c.total ?? c.value) * (resource.hasOwnProperty('maxTotal') ? 1 : -1) };
|
||||
});
|
||||
|
||||
await this.actor.modifyResource(resources);
|
||||
|
|
@ -382,15 +383,21 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
|||
const realCosts = this.getRealCosts(costs),
|
||||
hasFearCost = realCosts.findIndex(c => c.type === 'fear');
|
||||
if (hasFearCost > -1) {
|
||||
const fearCost = realCosts.splice(hasFearCost, 1);
|
||||
const fearCost = realCosts.splice(hasFearCost, 1)[0];
|
||||
if (
|
||||
!game.user.isGM ||
|
||||
fearCost[0].total > game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear)
|
||||
fearCost.total > game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear)
|
||||
)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* maxTotal is a sign that the resource is inverted, IE it counts upwards instead of down */
|
||||
const resources = this.actor.system.resources;
|
||||
return realCosts.reduce(
|
||||
(a, c) => a && this.actor.system.resources[c.type]?.value >= (c.total ?? c.value),
|
||||
(a, c) =>
|
||||
a && resources[c.type].hasOwnProperty('maxTotal')
|
||||
? resources[c.type].value + (c.total ?? c.value) <= resources[c.type].maxTotal
|
||||
: resources[c.type]?.value >= (c.total ?? c.value),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -100,12 +100,19 @@ export default class DhCharacter extends BaseDataActor {
|
|||
levelData: new fields.EmbeddedDataField(DhLevelData),
|
||||
bonuses: new fields.SchemaField({
|
||||
armorScore: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
damageReduction: new fields.SchemaField({
|
||||
physical: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
magical: new fields.NumberField({ integer: true, initial: 0 })
|
||||
}),
|
||||
damageThresholds: new fields.SchemaField({
|
||||
severe: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
major: new fields.NumberField({ integer: true, initial: 0 })
|
||||
}),
|
||||
roll: new fields.SchemaField({
|
||||
attack: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
primaryWeapon: new fields.SchemaField({
|
||||
attack: new fields.NumberField({ integer: true, initial: 0 })
|
||||
}),
|
||||
spellcast: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
action: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
hopeOrFear: new fields.NumberField({ integer: true, initial: 0 })
|
||||
|
|
@ -113,20 +120,29 @@ export default class DhCharacter extends BaseDataActor {
|
|||
damage: new fields.SchemaField({
|
||||
all: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
physical: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
magic: new fields.NumberField({ integer: true, initial: 0 })
|
||||
magic: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
primaryWeapon: new fields.SchemaField({
|
||||
bonus: new fields.NumberField({ integer: true }),
|
||||
extraDice: new fields.NumberField({ integer: true })
|
||||
})
|
||||
})
|
||||
}),
|
||||
companion: new ForeignDocumentUUIDField({ type: 'Actor', nullable: true, initial: null }),
|
||||
rules: new fields.SchemaField({
|
||||
maxArmorMarked: new fields.SchemaField({
|
||||
value: new fields.NumberField({ required: true, integer: true, initial: 1 }),
|
||||
bonus: new fields.NumberField({ required: true, integer: true, initial: 0 }),
|
||||
stressExtra: new fields.NumberField({ required: true, integer: true, initial: 0 })
|
||||
}),
|
||||
stressDamageReduction: new fields.SchemaField({
|
||||
severe: stressDamageReductionRule(),
|
||||
major: stressDamageReductionRule(),
|
||||
minor: stressDamageReductionRule()
|
||||
damageReduction: new fields.SchemaField({
|
||||
maxArmorMarked: new fields.SchemaField({
|
||||
value: new fields.NumberField({ required: true, integer: true, initial: 1 }),
|
||||
bonus: new fields.NumberField({ required: true, integer: true, initial: 0 }),
|
||||
stressExtra: new fields.NumberField({ required: true, integer: true, initial: 0 })
|
||||
}),
|
||||
stressDamageReduction: new fields.SchemaField({
|
||||
severe: stressDamageReductionRule(),
|
||||
major: stressDamageReductionRule(),
|
||||
minor: stressDamageReductionRule()
|
||||
}),
|
||||
increasePerArmorMark: new fields.NumberField({ integer: true, initial: 1 }),
|
||||
magical: new fields.BooleanField({ initial: false }),
|
||||
physical: new fields.BooleanField({ initial: false })
|
||||
}),
|
||||
strangePatterns: new fields.NumberField({
|
||||
integer: true,
|
||||
|
|
@ -135,6 +151,18 @@ export default class DhCharacter extends BaseDataActor {
|
|||
nullable: true,
|
||||
initial: null
|
||||
}),
|
||||
weapon: new fields.SchemaField({
|
||||
/* Unimplemented
|
||||
-> Should remove the lowest damage dice from weapon damage
|
||||
-> Reflect this in the chat message somehow so players get feedback that their choice is helping them.
|
||||
*/
|
||||
dropLowestDamageDice: new fields.BooleanField({ initial: false }),
|
||||
/* Unimplemented
|
||||
-> Should flip any lowest possible dice rolls for weapon damage to highest
|
||||
-> Reflect this in the chat message somehow so players get feedback that their choice is helping them.
|
||||
*/
|
||||
flipMinDiceValue: new fields.BooleanField({ intial: false })
|
||||
}),
|
||||
runeWard: new fields.BooleanField({ initial: false })
|
||||
})
|
||||
};
|
||||
|
|
@ -282,6 +310,13 @@ export default class DhCharacter extends BaseDataActor {
|
|||
);
|
||||
}
|
||||
|
||||
get armorApplicableDamageTypes() {
|
||||
return {
|
||||
physical: !this.rules.damageReduction.magical,
|
||||
magical: !this.rules.damageReduction.physical
|
||||
};
|
||||
}
|
||||
|
||||
static async unequipBeforeEquip(itemToEquip) {
|
||||
const primary = this.primaryWeapon,
|
||||
secondary = this.secondaryWeapon;
|
||||
|
|
@ -348,6 +383,7 @@ export default class DhCharacter extends BaseDataActor {
|
|||
}
|
||||
|
||||
const armor = this.armor;
|
||||
this.armorScore = this.armor ? this.armor.system.baseScore + (this.bonuses.armorScore ?? 0) : 0; // Bonuses to armorScore won't have been applied yet. Need to solve in documentPreparation somehow
|
||||
this.damageThresholds = {
|
||||
major: armor
|
||||
? armor.system.baseThresholds.major + this.levelData.level.current
|
||||
|
|
@ -372,9 +408,9 @@ export default class DhCharacter extends BaseDataActor {
|
|||
experience.total = experience.value + experience.bonus;
|
||||
}
|
||||
|
||||
this.rules.maxArmorMarked.total = this.rules.maxArmorMarked.value + this.rules.maxArmorMarked.bonus;
|
||||
this.rules.damageReduction.maxArmorMarked.total =
|
||||
this.rules.damageReduction.maxArmorMarked.value + this.rules.damageReduction.maxArmorMarked.bonus;
|
||||
|
||||
this.armorScore = this.armor ? this.armor.system.baseScore + (this.bonuses.armorScore ?? 0) : 0;
|
||||
this.resources.hitPoints.maxTotal = (this.class.value?.system?.hitPoints ?? 0) + this.resources.hitPoints.bonus;
|
||||
this.resources.stress.maxTotal = this.resources.stress.max + this.resources.stress.bonus;
|
||||
this.evasion.total = (this.class?.evasion ?? 0) + this.evasion.bonus;
|
||||
|
|
|
|||
|
|
@ -66,10 +66,9 @@ export default class DhCompanion extends BaseDataActor {
|
|||
damage: {
|
||||
parts: [
|
||||
{
|
||||
multiplier: 'flat',
|
||||
value: {
|
||||
dice: 'd6',
|
||||
multiplier: 'flat'
|
||||
multiplier: 'prof'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -87,6 +86,12 @@ export default class DhCompanion extends BaseDataActor {
|
|||
};
|
||||
}
|
||||
|
||||
get proficiency() {
|
||||
return {
|
||||
total: this.partner?.system?.proficiency?.total ?? 1
|
||||
};
|
||||
}
|
||||
|
||||
prepareBaseData() {
|
||||
const partnerSpellcastingModifier = this.partner?.system?.spellcastingModifiers?.main;
|
||||
const spellcastingModifier = this.partner?.system?.traits?.[partnerSpellcastingModifier]?.total;
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ export default class DHArmor extends AttachableItem {
|
|||
tier: new fields.NumberField({ required: true, integer: true, initial: 1, min: 1 }),
|
||||
equipped: new fields.BooleanField({ initial: false }),
|
||||
baseScore: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
features: new fields.ArrayField(
|
||||
armorFeatures: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
value: new fields.StringField({
|
||||
required: true,
|
||||
|
|
@ -45,25 +45,28 @@ export default class DHArmor extends AttachableItem {
|
|||
};
|
||||
}
|
||||
|
||||
get featureInfo() {
|
||||
return this.feature ? CONFIG.DH.ITEM.armorFeatures[this.feature] : null;
|
||||
get customActions() {
|
||||
return this.actions.filter(
|
||||
action => !this.armorFeatures.some(feature => feature.actionIds.includes(action.id))
|
||||
);
|
||||
}
|
||||
|
||||
async _preUpdate(changes, options, user) {
|
||||
const allowed = await super._preUpdate(changes, options, user);
|
||||
if (allowed === false) return false;
|
||||
|
||||
if (changes.system.features) {
|
||||
const removed = this.features.filter(x => !changes.system.features.includes(x));
|
||||
const added = changes.system.features.filter(x => !this.features.includes(x));
|
||||
if (changes.system.armorFeatures) {
|
||||
const removed = this.armorFeatures.filter(x => !changes.system.armorFeatures.includes(x));
|
||||
const added = changes.system.armorFeatures.filter(x => !this.armorFeatures.includes(x));
|
||||
|
||||
const effectIds = [];
|
||||
const actionIds = [];
|
||||
for (var feature of removed) {
|
||||
for (var effectId of feature.effectIds) {
|
||||
await this.parent.effects.get(effectId).delete();
|
||||
}
|
||||
|
||||
changes.system.actions = this.actions.filter(x => !feature.actionIds.includes(x._id));
|
||||
effectIds.push(...feature.effectIds);
|
||||
actionIds.push(...feature.actionIds);
|
||||
}
|
||||
await this.parent.deleteEmbeddedDocuments('ActiveEffect', effectIds);
|
||||
changes.system.actions = this.actions.filter(x => !actionIds.includes(x._id));
|
||||
|
||||
for (var feature of added) {
|
||||
const featureData = armorFeatures[feature.value];
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export default class DHWeapon extends AttachableItem {
|
|||
type: 'weapon',
|
||||
hasDescription: true,
|
||||
isQuantifiable: true,
|
||||
isInventoryItem: true,
|
||||
isInventoryItem: true
|
||||
// hasInitialAction: true
|
||||
});
|
||||
}
|
||||
|
|
@ -26,8 +26,7 @@ export default class DHWeapon extends AttachableItem {
|
|||
//SETTINGS
|
||||
secondary: new fields.BooleanField({ initial: false }),
|
||||
burden: new fields.StringField({ required: true, choices: CONFIG.DH.GENERAL.burden, initial: 'oneHanded' }),
|
||||
|
||||
features: new fields.ArrayField(
|
||||
weaponFeatures: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
value: new fields.StringField({
|
||||
required: true,
|
||||
|
|
@ -59,7 +58,7 @@ export default class DHWeapon extends AttachableItem {
|
|||
{
|
||||
value: {
|
||||
multiplier: 'prof',
|
||||
dice: "d8"
|
||||
dice: 'd8'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -74,22 +73,30 @@ export default class DHWeapon extends AttachableItem {
|
|||
return [this.attack, ...this.actions];
|
||||
}
|
||||
|
||||
get customActions() {
|
||||
return this.actions.filter(
|
||||
action => !this.weaponFeatures.some(feature => feature.actionIds.includes(action.id))
|
||||
);
|
||||
}
|
||||
|
||||
async _preUpdate(changes, options, user) {
|
||||
const allowed = await super._preUpdate(changes, options, user);
|
||||
if (allowed === false) return false;
|
||||
|
||||
if (changes.system?.features) {
|
||||
const removed = this.features.filter(x => !changes.system.features.includes(x));
|
||||
const added = changes.system.features.filter(x => !this.features.includes(x));
|
||||
if (changes.system?.weaponFeatures) {
|
||||
const removed = this.weaponFeatures.filter(x => !changes.system.weaponFeatures.includes(x));
|
||||
const added = changes.system.weaponFeatures.filter(x => !this.weaponFeatures.includes(x));
|
||||
|
||||
const removedEffectsUpdate = [];
|
||||
const removedActionsUpdate = [];
|
||||
for (let weaponFeature of removed) {
|
||||
for (var effectId of weaponFeature.effectIds) {
|
||||
await this.parent.effects.get(effectId).delete();
|
||||
}
|
||||
|
||||
changes.system.actions = this.actions.filter(x => !weaponFeature.actionIds.includes(x._id));
|
||||
removedEffectsUpdate.push(...weaponFeature.effectIds);
|
||||
removedActionsUpdate.push(...weaponFeature.actionIds);
|
||||
}
|
||||
|
||||
await this.parent.deleteEmbeddedDocuments('ActiveEffect', removedEffectsUpdate);
|
||||
changes.system.actions = this.actions.filter(x => !removedActionsUpdate.includes(x._id));
|
||||
|
||||
for (let weaponFeature of added) {
|
||||
const featureData = CONFIG.DH.ITEM.weaponFeatures[weaponFeature.value];
|
||||
if (featureData.effects?.length > 0) {
|
||||
|
|
@ -102,17 +109,37 @@ export default class DHWeapon extends AttachableItem {
|
|||
]);
|
||||
weaponFeature.effectIds = embeddedItems.map(x => x.id);
|
||||
}
|
||||
|
||||
const newActions = [];
|
||||
if (featureData.actions?.length > 0) {
|
||||
const newActions = featureData.actions.map(action => {
|
||||
const cls = actionsTypes[action.type];
|
||||
return new cls(
|
||||
{ ...action, _id: foundry.utils.randomID(), name: game.i18n.localize(action.name) },
|
||||
{ parent: this }
|
||||
for (let action of featureData.actions) {
|
||||
const embeddedEffects = await this.parent.createEmbeddedDocuments(
|
||||
'ActiveEffect',
|
||||
(action.effects ?? []).map(effect => ({
|
||||
...effect,
|
||||
transfer: false,
|
||||
name: game.i18n.localize(effect.name),
|
||||
description: game.i18n.localize(effect.description)
|
||||
}))
|
||||
);
|
||||
});
|
||||
changes.system.actions = [...this.actions, ...newActions];
|
||||
weaponFeature.actionIds = newActions.map(x => x._id);
|
||||
const cls = actionsTypes[action.type];
|
||||
newActions.push(
|
||||
new cls(
|
||||
{
|
||||
...action,
|
||||
_id: foundry.utils.randomID(),
|
||||
name: game.i18n.localize(action.name),
|
||||
description: game.i18n.localize(action.description),
|
||||
effects: embeddedEffects.map(x => ({ _id: x.id }))
|
||||
},
|
||||
{ parent: this }
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
changes.system.actions = [...this.actions, ...newActions];
|
||||
weaponFeature.actionIds = newActions.map(x => x._id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,10 @@ export default class DhAppearance extends foundry.abstract.DataModel {
|
|||
outline: new fields.ColorField({ required: true, initial: '#ffffff' }),
|
||||
edge: new fields.ColorField({ required: true, initial: '#000000' })
|
||||
})
|
||||
}),
|
||||
showGenericStatusEffects: new fields.BooleanField({
|
||||
initial: true,
|
||||
label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.showGenericStatusEffects.label'
|
||||
})
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ export default class DhRangeMeasurement extends foundry.abstract.DataModel {
|
|||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
enabled: new fields.BooleanField({ required: true, initial: false, label: 'DAGGERHEART.GENERAL.enabled' }),
|
||||
enabled: new fields.BooleanField({ required: true, initial: true, label: 'DAGGERHEART.GENERAL.enabled' }),
|
||||
melee: new fields.NumberField({ required: true, initial: 5, label: 'DAGGERHEART.CONFIG.Range.melee.name' }),
|
||||
veryClose: new fields.NumberField({
|
||||
required: true,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs';
|
||||
import { setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs';
|
||||
import DHRoll from './dhRoll.mjs';
|
||||
|
||||
export default class D20Roll extends DHRoll {
|
||||
|
|
@ -137,12 +136,7 @@ export default class D20Roll extends DHRoll {
|
|||
|
||||
static async buildEvaluate(roll, config = {}, message = {}) {
|
||||
if (config.evaluate !== false) await roll.evaluate();
|
||||
const advantageState =
|
||||
config.roll.advantage == this.ADV_MODE.ADVANTAGE
|
||||
? true
|
||||
: config.roll.advantage == this.ADV_MODE.DISADVANTAGE
|
||||
? false
|
||||
: null;
|
||||
|
||||
this.postEvaluate(roll, config);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ export const registerRollDiceHooks = () => {
|
|||
|
||||
if (updates.length) actor.modifyResource(updates);
|
||||
|
||||
if (!config.roll.hasOwnProperty('success') && !config.targets.length) return;
|
||||
if (!config.roll.hasOwnProperty('success') && !config.targets?.length) return;
|
||||
|
||||
const rollResult = config.roll.success || config.targets.some(t => t.hit),
|
||||
looseSpotlight = !rollResult || config.roll.result.duality === -1;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs';
|
||||
import D20Roll from './d20Roll.mjs';
|
||||
import { setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs';
|
||||
|
||||
export default class DualityRoll extends D20Roll {
|
||||
_advantageFaces = 6;
|
||||
|
|
@ -80,7 +81,6 @@ export default class DualityRoll extends D20Roll {
|
|||
}
|
||||
|
||||
static getHooks(hooks) {
|
||||
|
||||
return [...(hooks ?? []), 'Duality'];
|
||||
}
|
||||
|
||||
|
|
@ -142,5 +142,7 @@ export default class DualityRoll extends D20Roll {
|
|||
total: roll.dHope.total + roll.dFear.total,
|
||||
label: roll.totalLabel
|
||||
};
|
||||
|
||||
setDiceSoNiceForDualityRoll(roll, config.roll.advantage.type);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,11 @@ export default class DhActiveEffect extends ActiveEffect {
|
|||
}
|
||||
|
||||
static applyField(model, change, field) {
|
||||
change.value = Roll.safeEval(Roll.replaceFormulaData(change.value, change.effect.parent));
|
||||
const isItemTarget = change.value.toLowerCase().startsWith('item.');
|
||||
change.value = isItemTarget ? change.value.slice(5) : change.value;
|
||||
change.value = Roll.safeEval(
|
||||
Roll.replaceFormulaData(change.value, isItemTarget ? change.effect.parent : model)
|
||||
);
|
||||
super.applyField(model, change, field);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { emitAsGM, emitAsOwner, GMUpdateEvent, socketEvent } from '../systemRegi
|
|||
import DamageReductionDialog from '../applications/dialogs/damageReductionDialog.mjs';
|
||||
import { LevelOptionType } from '../data/levelTier.mjs';
|
||||
import DHFeature from '../data/item/feature.mjs';
|
||||
import { damageKeyToNumber, getDamageKey } from '../helpers/utils.mjs';
|
||||
|
||||
export default class DhpActor extends Actor {
|
||||
/**
|
||||
|
|
@ -455,14 +456,32 @@ export default class DhpActor extends Actor {
|
|||
cls.create(msg.toObject());
|
||||
}
|
||||
|
||||
async takeDamage(damage, type) {
|
||||
if (Hooks.call(`${CONFIG.DH.id}.preTakeDamage`, this, damage, type) === false) return null;
|
||||
#canReduceDamage(hpDamage, type) {
|
||||
const availableStress = this.system.resources.stress.maxTotal - this.system.resources.stress.value;
|
||||
|
||||
const canUseArmor =
|
||||
this.system.armor &&
|
||||
this.system.armor.system.marks.value < this.system.armorScore &&
|
||||
this.system.armorApplicableDamageTypes[type];
|
||||
const canUseStress = Object.keys(this.system.rules.damageReduction.stressDamageReduction).reduce((acc, x) => {
|
||||
const rule = this.system.rules.damageReduction.stressDamageReduction[x];
|
||||
if (damageKeyToNumber(x) <= hpDamage) return acc || (rule.enabled && availableStress >= rule.cost);
|
||||
return acc;
|
||||
}, false);
|
||||
|
||||
return canUseArmor || canUseStress;
|
||||
}
|
||||
|
||||
async takeDamage(baseDamage, type) {
|
||||
if (Hooks.call(`${CONFIG.DH.id}.preTakeDamage`, this, baseDamage, type) === false) return null;
|
||||
|
||||
if (this.type === 'companion') {
|
||||
await this.modifyResource([{ value: 1, type: 'stress' }]);
|
||||
return;
|
||||
}
|
||||
|
||||
const flatReduction = this.system.bonuses.damageReduction[type];
|
||||
const damage = Math.max(baseDamage - (flatReduction ?? 0), 0);
|
||||
const hpDamage = this.convertDamageToThreshold(damage);
|
||||
|
||||
if (Hooks.call(`${CONFIG.DH.id}.postDamageTreshold`, this, hpDamage, damage, type) === false) return null;
|
||||
|
|
@ -471,12 +490,12 @@ export default class DhpActor extends Actor {
|
|||
|
||||
const updates = [{ value: hpDamage, type: 'hitPoints' }];
|
||||
|
||||
if (
|
||||
this.type === 'character' &&
|
||||
this.system.armor &&
|
||||
this.system.armor.system.marks.value < this.system.armorScore
|
||||
) {
|
||||
const armorStackResult = await this.owner.query('armorStack', { actorId: this.uuid, damage: hpDamage });
|
||||
if (this.type === 'character' && this.system.armor && this.#canReduceDamage(hpDamage, type)) {
|
||||
const armorStackResult = await this.owner.query('armorStack', {
|
||||
actorId: this.uuid,
|
||||
damage: hpDamage,
|
||||
type: type
|
||||
});
|
||||
if (armorStackResult) {
|
||||
const { modifiedDamage, armorSpent, stressSpent } = armorStackResult;
|
||||
updates.find(u => u.type === 'hitPoints').value = modifiedDamage;
|
||||
|
|
|
|||
|
|
@ -90,10 +90,12 @@ export default class DHItem extends foundry.documents.Item {
|
|||
ok: {
|
||||
label: title,
|
||||
callback: (event, button, dialog) => {
|
||||
Object.defineProperty(prevEvent, "shiftKey", {
|
||||
get() { return event.shiftKey; },
|
||||
Object.defineProperty(prevEvent, 'shiftKey', {
|
||||
get() {
|
||||
return event.shiftKey;
|
||||
}
|
||||
});
|
||||
return this.system.actionsList.find(a => a._id === button.form.elements.actionId.value)
|
||||
return this.system.actionsList.find(a => a._id === button.form.elements.actionId.value);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,15 +2,33 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
|
|||
async activate(element, options = {}) {
|
||||
let html = options.html;
|
||||
if (element.dataset.tooltip?.startsWith('#item#')) {
|
||||
const item = await foundry.utils.fromUuid(element.dataset.tooltip.slice(6));
|
||||
const splitValues = element.dataset.tooltip.slice(6).split('#action#');
|
||||
const itemUuid = splitValues[0];
|
||||
const actionId = splitValues.length > 1 ? splitValues[1] : null;
|
||||
|
||||
const baseItem = await foundry.utils.fromUuid(itemUuid);
|
||||
const item = actionId ? baseItem.system.actions.find(x => x.id === actionId) : baseItem;
|
||||
if (item) {
|
||||
const type = actionId ? 'action' : item.type;
|
||||
html = await foundry.applications.handlebars.renderTemplate(
|
||||
`systems/daggerheart/templates/ui/tooltip/${item.type}.hbs`,
|
||||
item
|
||||
`systems/daggerheart/templates/ui/tooltip/${type}.hbs`,
|
||||
{
|
||||
item: item,
|
||||
config: CONFIG.DH
|
||||
}
|
||||
);
|
||||
|
||||
this.tooltip.innerHTML = html;
|
||||
options.direction = this._determineItemTooltipDirection(element);
|
||||
}
|
||||
}
|
||||
|
||||
super.activate(element, { ...options, html: html });
|
||||
}
|
||||
|
||||
_determineItemTooltipDirection(element) {
|
||||
const pos = element.getBoundingClientRect();
|
||||
const dirs = this.constructor.TOOLTIP_DIRECTIONS;
|
||||
return dirs[pos.x - this.tooltip.offsetWidth < 0 ? 'DOWN' : 'LEFT'];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,47 +1,49 @@
|
|||
import { getWidthOfText } from './utils.mjs';
|
||||
|
||||
export default class RegisterHandlebarsHelpers {
|
||||
static registerHelpers() {
|
||||
Handlebars.registerHelper({
|
||||
times: this.times,
|
||||
join: this.join,
|
||||
add: this.add,
|
||||
subtract: this.subtract,
|
||||
includes: this.includes,
|
||||
case: this.case
|
||||
times: this.times,
|
||||
damageFormula: this.damageFormula,
|
||||
damageSymbols: this.damageSymbols,
|
||||
tertiary: this.tertiary,
|
||||
signedNumber: this.signedNumber
|
||||
});
|
||||
}
|
||||
|
||||
static times(nr, block) {
|
||||
var accum = '';
|
||||
for (var i = 0; i < nr; ++i) accum += block.fn(i);
|
||||
return accum;
|
||||
}
|
||||
|
||||
static join(...options) {
|
||||
return options.slice(0, options.length - 1);
|
||||
}
|
||||
|
||||
static add(a, b) {
|
||||
const aNum = Number.parseInt(a);
|
||||
const bNum = Number.parseInt(b);
|
||||
return (Number.isNaN(aNum) ? 0 : aNum) + (Number.isNaN(bNum) ? 0 : bNum);
|
||||
}
|
||||
|
||||
static subtract(a, b) {
|
||||
const aNum = Number.parseInt(a);
|
||||
const bNum = Number.parseInt(b);
|
||||
return (Number.isNaN(aNum) ? 0 : aNum) - (Number.isNaN(bNum) ? 0 : bNum);
|
||||
}
|
||||
|
||||
static includes(list, item) {
|
||||
return list.includes(item);
|
||||
}
|
||||
|
||||
static case(value, options) {
|
||||
if (value == this.switch_value) {
|
||||
this.switch_break = true;
|
||||
return options.fn(this);
|
||||
}
|
||||
static times(nr, block) {
|
||||
var accum = '';
|
||||
for (var i = 0; i < nr; ++i) accum += block.fn(i);
|
||||
return accum;
|
||||
}
|
||||
|
||||
static damageFormula(attack, actor) {
|
||||
const traitTotal = actor.system.traits?.[attack.roll.trait]?.total;
|
||||
const instances = [
|
||||
attack.damage.parts.map(x => Roll.replaceFormulaData(x.value.getFormula(), actor)).join(' + '),
|
||||
traitTotal
|
||||
].filter(x => x);
|
||||
|
||||
return instances.join(traitTotal > 0 ? ' + ' : ' - ');
|
||||
}
|
||||
|
||||
static damageSymbols(damageParts) {
|
||||
const symbols = new Set();
|
||||
damageParts.forEach(part => symbols.add(...CONFIG.DH.GENERAL.damageTypes[part.type].icon));
|
||||
return new Handlebars.SafeString(Array.from(symbols).map(symbol => `<i class="fa-solid ${symbol}"></i>`));
|
||||
}
|
||||
|
||||
static tertiary(a, b) {
|
||||
return a ?? b;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,12 +126,10 @@ export const setDiceSoNiceForDualityRoll = (rollResult, advantageState) => {
|
|||
const diceSoNicePresets = getDiceSoNicePresets();
|
||||
rollResult.dice[0].options = { appearance: diceSoNicePresets.hope };
|
||||
rollResult.dice[1].options = { appearance: diceSoNicePresets.fear }; //diceSoNicePresets.fear;
|
||||
if (rollResult.dice[2]) {
|
||||
if (advantageState === true) {
|
||||
rollResult.dice[2].options = { appearance: diceSoNicePresets.advantage };
|
||||
} else if (advantageState === false) {
|
||||
rollResult.dice[2].options = { appearance: diceSoNicePresets.disadvantage };
|
||||
}
|
||||
if (rollResult.dice[2] && advantageState) {
|
||||
rollResult.dice[2].options = {
|
||||
appearance: advantageState === 1 ? diceSoNicePresets.advantage : diceSoNicePresets.disadvantage
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -159,7 +157,8 @@ export const tagifyElement = (element, options, onChange, tagifyOptions = {}) =>
|
|||
return {
|
||||
value: key,
|
||||
name: game.i18n.localize(option.label),
|
||||
src: option.src
|
||||
src: option.src,
|
||||
description: option.description
|
||||
};
|
||||
}),
|
||||
maxTags: maxTags,
|
||||
|
|
@ -173,11 +172,12 @@ export const tagifyElement = (element, options, onChange, tagifyOptions = {}) =>
|
|||
},
|
||||
templates: {
|
||||
tag(tagData) {
|
||||
return `<tag title="${tagData.title || tagData.value}"
|
||||
return `<tag
|
||||
contenteditable='false'
|
||||
spellcheck='false'
|
||||
tabIndex="${this.settings.a11y.focusableTags ? 0 : -1}"
|
||||
class="${this.settings.classNames.tag} ${tagData.class ? tagData.class : ''}"
|
||||
data-tooltip="${tagData.description || tagData.name}"
|
||||
${this.getAttributes(tagData)}>
|
||||
<x class="${this.settings.classNames.tagX}" role='button' aria-label='remove tag'></x>
|
||||
<div>
|
||||
|
|
@ -190,6 +190,8 @@ export const tagifyElement = (element, options, onChange, tagifyOptions = {}) =>
|
|||
});
|
||||
|
||||
tagifyElement.on('add', event => {
|
||||
if (event.detail.data.__isValid === 'not allowed') return;
|
||||
|
||||
const input = event.detail.tagify.DOM.originalInput;
|
||||
const currentList = input.value ? JSON.parse(input.value) : [];
|
||||
onChange([...currentList, event.detail.data], { option: event.detail.data.value, removed: false }, input);
|
||||
|
|
@ -233,19 +235,23 @@ Roll.replaceFormulaData = function (formula, data = {}, { missing, warn = false
|
|||
return nativeReplaceFormulaData(formula, data, { missing, warn });
|
||||
};
|
||||
|
||||
export const getDamageLabel = damage => {
|
||||
export const getDamageKey = damage => {
|
||||
switch (damage) {
|
||||
case 3:
|
||||
return game.i18n.localize('DAGGERHEART.GENERAL.Damage.severe');
|
||||
return 'severe';
|
||||
case 2:
|
||||
return game.i18n.localize('DAGGERHEART.GENERAL.Damage.major');
|
||||
return 'major';
|
||||
case 1:
|
||||
return game.i18n.localize('DAGGERHEART.GENERAL.Damage.minor');
|
||||
return 'minor';
|
||||
case 0:
|
||||
return game.i18n.localize('DAGGERHEART.GENERAL.Damage.none');
|
||||
return 'none';
|
||||
}
|
||||
};
|
||||
|
||||
export const getDamageLabel = damage => {
|
||||
return game.i18n.localize(`DAGGERHEART.GENERAL.Damage.${getDamageKey(damage)}`);
|
||||
};
|
||||
|
||||
export const damageKeyToNumber = key => {
|
||||
switch (key) {
|
||||
case 'severe':
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ export const preloadHandlebarsTemplates = async function () {
|
|||
'systems/daggerheart/templates/actionTypes/beastform.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/target-chat.hbs'
|
||||
'systems/daggerheart/templates/ui/chat/parts/target-chat.hbs',
|
||||
'systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs'
|
||||
]);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ const registerMenuSettings = () => {
|
|||
});
|
||||
|
||||
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.RangeMeasurement, {
|
||||
scope: 'client',
|
||||
scope: 'world',
|
||||
config: false,
|
||||
type: DhRangeMeasurement
|
||||
});
|
||||
|
|
|
|||
|
|
@ -45,7 +45,13 @@ export const registerSocketHooks = () => {
|
|||
await game.settings.set(
|
||||
CONFIG.DH.id,
|
||||
CONFIG.DH.SETTINGS.gameSettings.Resources.Fear,
|
||||
Math.max(0, Math.min(game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxFear, data.update))
|
||||
Math.max(
|
||||
0,
|
||||
Math.min(
|
||||
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxFear,
|
||||
data.update
|
||||
)
|
||||
)
|
||||
);
|
||||
/* Hooks.callAll(socketEvent.DhpFearUpdate);
|
||||
await game.socket.emit(`system.${CONFIG.DH.id}`, { action: socketEvent.DhpFearUpdate }); */
|
||||
|
|
@ -64,7 +70,7 @@ export const registerSocketHooks = () => {
|
|||
};
|
||||
|
||||
export const emitAsGM = async (eventName, callback, update, uuid = null) => {
|
||||
if(!game.user.isGM) {
|
||||
if (!game.user.isGM) {
|
||||
return await game.socket.emit(`system.${CONFIG.DH.id}`, {
|
||||
action: socketEvent.GMUpdate,
|
||||
data: {
|
||||
|
|
@ -74,11 +80,11 @@ export const emitAsGM = async (eventName, callback, update, uuid = null) => {
|
|||
}
|
||||
});
|
||||
} else return callback(update);
|
||||
}
|
||||
};
|
||||
|
||||
export const emitAsOwner = (eventName, userId, args) => {
|
||||
if(userId === game.user.id) return;
|
||||
if(!eventName || !userId) return false;
|
||||
if (userId === game.user.id) return;
|
||||
if (!eventName || !userId) return false;
|
||||
game.socket.emit(`system.${CONFIG.DH.id}`, {
|
||||
action: eventName,
|
||||
data: {
|
||||
|
|
@ -87,4 +93,4 @@ export const emitAsOwner = (eventName, userId, args) => {
|
|||
}
|
||||
});
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
@import './less/dialog/index.less';
|
||||
|
||||
@import './less//hud/index.less';
|
||||
|
||||
@import './less/utils/colors.less';
|
||||
@import './less/utils/fonts.less';
|
||||
|
||||
|
|
@ -10,4 +12,6 @@
|
|||
|
||||
@import './less/ui/index.less';
|
||||
|
||||
@import './less/ux/index.less';
|
||||
|
||||
@import '../node_modules/@yaireo/tagify/dist/tagify.css';
|
||||
|
|
|
|||
|
|
@ -319,6 +319,17 @@
|
|||
transform: translateY(-20px);
|
||||
transform-origin: top;
|
||||
}
|
||||
|
||||
.item-buttons {
|
||||
grid-column: span 3;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
|
||||
button {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.application.setting.dh-style {
|
||||
|
|
|
|||
1
styles/less/hud/index.less
Normal file
1
styles/less/hud/index.less
Normal file
|
|
@ -0,0 +1 @@
|
|||
@import './token-hud/token-hud.less';
|
||||
10
styles/less/hud/token-hud/token-hud.less
Normal file
10
styles/less/hud/token-hud/token-hud.less
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
.daggerheart.placeable-hud {
|
||||
.col.right {
|
||||
.palette {
|
||||
.palette-category-title {
|
||||
grid-column: span var(--effect-columns);
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
styles/less/ux/index.less
Normal file
1
styles/less/ux/index.less
Normal file
|
|
@ -0,0 +1 @@
|
|||
@import './tooltip/tooltip.less';
|
||||
106
styles/less/ux/tooltip/tooltip.less
Normal file
106
styles/less/ux/tooltip/tooltip.less
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
.daggerheart.dh-style.tooltip {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
|
||||
.tooltip-title {
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tooltip-image {
|
||||
height: 180px;
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
.tooltip-description {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.tooltip-sub-title {
|
||||
margin: 0;
|
||||
color: light-dark(@dark-blue, @beige);
|
||||
}
|
||||
|
||||
.tooltip-information-section {
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 4px;
|
||||
|
||||
&.triple {
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
}
|
||||
|
||||
&.border {
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border-radius: 6px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.tooltip-information {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
|
||||
&.full-width {
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
label,
|
||||
div {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tooltip-tags {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
|
||||
.tooltip-tag {
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: 80px 1fr;
|
||||
align-items: start;
|
||||
gap: 8px;
|
||||
padding: 4px;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border-radius: 6px;
|
||||
|
||||
.tooltip-tag-label-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
|
||||
.tooltip-tag-image {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.tooltip-tag-label {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tooltip-tag-description {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.spaced {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
}
|
||||
78
templates/hud/tokenHUD.hbs
Normal file
78
templates/hud/tokenHUD.hbs
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
<div class="col left">
|
||||
<div class="attribute elevation" data-tooltip="HUD.Elevation">
|
||||
<i class="fas fa-angle-up"></i>
|
||||
<input type="text" name="elevation" value="{{elevation}}" {{disabled (or locked (and isGamePaused (not isGM)))}}>
|
||||
</div>
|
||||
|
||||
<button type="button" class="control-icon" data-action="sort" data-direction="up" data-tooltip="HUD.ToFront">
|
||||
<img src="{{icons.up}}">
|
||||
</button>
|
||||
|
||||
<button type="button" class="control-icon" data-action="sort" data-direction="down" data-tooltip="HUD.ToBack">
|
||||
<img src="{{icons.down}}">
|
||||
</button>
|
||||
|
||||
{{#if canConfigure}}
|
||||
<button type="button" class="control-icon" data-action="config" data-tooltip="HUD.OpenConfig">
|
||||
<i class="fa-solid fa-gear" inert></i>
|
||||
</button>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class="col middle">
|
||||
<div class="attribute bar2">
|
||||
{{#if displayBar2}}
|
||||
<input type="text" name="bar2" value="{{bar2Data.value}}" {{#unless bar2Data.editable}}disabled{{/unless}}>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class="attribute bar1">
|
||||
{{#if displayBar1}}
|
||||
<input type="text" name="bar1" value="{{bar1Data.value}}" {{#unless bar1Data.editable}}disabled{{/unless}}>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col right">
|
||||
{{#if isGM}}
|
||||
<button type="button" class="control-icon {{visibilityClass}}" data-action="visibility" data-tooltip="HUD.ToggleVis">
|
||||
<img src="{{icons.visibility}}">
|
||||
</button>
|
||||
{{/if}}
|
||||
|
||||
<button type="button" class="control-icon" data-action="togglePalette" data-palette="effects" data-tooltip="HUD.AssignStatusEffects">
|
||||
<img src="{{icons.effects}}">
|
||||
</button>
|
||||
<div class="palette status-effects" data-palette="effects">
|
||||
{{#each systemStatusEffects as |status|}}
|
||||
<img class="effect-control {{status.cssClass}}" src="{{status.src}}" data-action="effect" data-status-id="{{status.id}}" {{#if status.title}}data-tooltip-text="{{status.title}}"{{/if}}>
|
||||
{{/each}}
|
||||
{{#if genericStatusEffects}}
|
||||
<label class="palette-category-title">{{localize "DAGGERHEART.APPLICATIONS.HUD.tokenHUD.genericEffects"}}</label>
|
||||
{{#each genericStatusEffects as |status|}}
|
||||
<img class="effect-control {{status.cssClass}}" src="{{status.src}}" data-action="effect" data-status-id="{{status.id}}" {{#if status.title}}data-tooltip-text="{{status.title}}"{{/if}}>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<button type="button" class="control-icon" data-action="togglePalette" data-palette="movementActions" data-tooltip="HUD.SelectMovementAction">
|
||||
<i class="{{movementActionsIcon}}" inert></i>
|
||||
</button>
|
||||
<div class="palette movement-actions" data-palette="movementActions">
|
||||
{{#each movementActions as |action|}}
|
||||
<a class="movement-action-control {{action.cssClass}}" data-action="movementAction" data-movement-action="{{action.id}}">
|
||||
<span>{{#if action.icon}}<i class="{{action.icon}} fa-fw" inert></i>{{/if}} {{action.label}}</span>
|
||||
</a>
|
||||
{{/each}}
|
||||
</div>
|
||||
|
||||
<button type="button" class="control-icon {{targetClass}}" data-action="target" data-tooltip="HUD.ToggleTargetState">
|
||||
<i class="fa-solid fa-bullseye" inert></i>
|
||||
</button>
|
||||
|
||||
{{#if canToggleCombat}}
|
||||
<button type="button" class="control-icon {{combatClass}}" data-action="combat" data-tooltip="HUD.ToggleCombatState">
|
||||
<img src="{{icons.combat}}">
|
||||
</button>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
<div>
|
||||
{{formGroup settingFields.schema.fields.displayFear value=settingFields._source.displayFear localize=true}}
|
||||
{{formGroup settingFields.schema.fields.showGenericStatusEffects value=settingFields._source.showGenericStatusEffects localize=true}}
|
||||
|
||||
<fieldset>
|
||||
<legend>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.duality"}}</legend>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
{{/each}}
|
||||
{{else}}
|
||||
{{#each values}}
|
||||
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-item.hbs' item=this type=../type hideControls=../hideControls }}
|
||||
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-item.hbs' item=this type=../type hideControls=../hideControls featureType=true }}
|
||||
{{/each}}
|
||||
|
||||
{{#each adversaries as |adversary|}}
|
||||
|
|
|
|||
|
|
@ -171,4 +171,12 @@
|
|||
<span></span>
|
||||
{{/unless}}
|
||||
<div class="item-description">{{#unless isSidebar}}{{{item.system.description}}}{{/unless}}</div>
|
||||
|
||||
{{#if featureType}}
|
||||
<div class="item-buttons">
|
||||
{{#each item.system.actions as | action |}}
|
||||
<button type="button" data-action="useAction" data-action-id="{{action.id}}">{{action.name}}</button>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</li>
|
||||
|
|
@ -10,6 +10,7 @@
|
|||
<div class="action-item"
|
||||
data-action="editAction"
|
||||
data-index="{{index}}"
|
||||
data-tooltip="{{concat "#item#" @root.document.uuid "#action#" action.id}}"
|
||||
>
|
||||
<img class="image" src="{{action.img}}" />
|
||||
<span>{{action.name}}</span>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
data-action="editFeature"
|
||||
id="{{feature.id}}"
|
||||
>
|
||||
<img class="image" src="{{feature.img}}" />
|
||||
<img class="image" src="{{feature.img}}" data-tooltip="{{concat "#item#" feature.uuid}}" />
|
||||
<span>{{feature.name}}</span>
|
||||
<div class="controls">
|
||||
<a data-action="removeFeature" id="{{feature.id}}"><i class="fa-solid fa-trash"></i></a>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<line-div></line-div>
|
||||
<h1 class='item-name'><input type='text' name='name' value='{{source.name}}' /></h1>
|
||||
<div class='item-description'>
|
||||
<h3>{{localize (concat 'TYPES.Item.feature' source.system.type)}}</h3>
|
||||
<h3>{{localize 'TYPES.Item.feature'}}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
83
templates/ui/tooltip/action.hbs
Normal file
83
templates/ui/tooltip/action.hbs
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
<div class="daggerheart dh-style tooltip">
|
||||
<h2 class="tooltip-title">{{item.name}}</h2>
|
||||
<img class="tooltip-image" src="{{item.img}}" />
|
||||
<div class="tooltip-description">{{{item.description}}}</div>
|
||||
|
||||
{{#if item.uses.max}}
|
||||
<h4 class="tooltip-sub-title">{{localize "DAGGERHEART.GENERAL.uses"}}</h4>
|
||||
<div class="tooltip-information-section triple spaced">
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.used"}}</label>
|
||||
<div>{{item.uses.value}}</div>
|
||||
</div>
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.max"}}</label>
|
||||
<div>{{item.uses.max}}</div>
|
||||
</div>
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.recovery"}}</label>
|
||||
{{#with (lookup config.GENERAL.refreshTypes item.uses.recovery) as | type |}}
|
||||
<div>{{localize type.label}}</div>
|
||||
{{/with}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if (gt item.cost.length 0)}}
|
||||
<h4 class="tooltip-sub-title">{{localize "DAGGERHEART.GENERAL.Cost.plural"}}</h4>
|
||||
{{#each item.cost as | cost |}}
|
||||
<div class="tooltip-information-section border spaced">
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "Type"}}</label>
|
||||
{{#with (lookup @root.config.GENERAL.abilityCosts cost.type) as | type |}}
|
||||
<div>{{localize type.label}}</div>
|
||||
{{/with}}
|
||||
</div>
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.value"}}</label>
|
||||
<div>{{cost.value}}</div>
|
||||
</div>
|
||||
{{#if cost.scalable}}
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.scalable"}}</label>
|
||||
<div>{{localize "DAGGERHEART.GENERAL.true"}}</div>
|
||||
</div>
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.value"}}</label>
|
||||
<div>{{cost.step}}</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
|
||||
{{#if (or item.range item.target)}}
|
||||
<h4 class="tooltip-sub-title">{{localize "DAGGERHEART.UI.Tooltip.rangeAndTarget"}}</h4>
|
||||
<div class="tooltip-information-section border spaced">
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.range"}}</label>
|
||||
<div>
|
||||
{{#if item.range}}
|
||||
{{#with (lookup @root.config.GENERAL.range item.range) as | range |}}
|
||||
<div>{{localize range.label}}</div>
|
||||
{{/with}}
|
||||
{{else}}
|
||||
<div>{{localize "DAGGERHEART.GENERAL.none"}}</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.target"}}</label>
|
||||
<div>
|
||||
{{#if item.target.type}}
|
||||
{{#with (lookup @root.config.ACTIONS.targetTypes item.target.type) as | target |}}
|
||||
<div>{{@root.item.target.amount}} {{localize target.label}}</div>
|
||||
{{/with}}
|
||||
{{else}}
|
||||
<div>{{localize "DAGGERHEART.GENERAL.none"}}</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
64
templates/ui/tooltip/adversary.hbs
Normal file
64
templates/ui/tooltip/adversary.hbs
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
<div class="daggerheart dh-style tooltip">
|
||||
<h2 class="tooltip-title">{{item.name}}</h2>
|
||||
<img class="tooltip-image" src="{{item.img}}" />
|
||||
<div class="tooltip-description">{{{item.system.description}}}</div>
|
||||
|
||||
<div class="tooltip-information-section triple spaced">
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.Tiers.singular"}}</label>
|
||||
{{#with (lookup config.GENERAL.tiers item.system.tier) as | tier |}}
|
||||
<div>{{localize tier.label}}</div>
|
||||
{{/with}}
|
||||
</div>
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "Type"}}</label>
|
||||
{{#with (lookup config.ACTOR.adversaryTypes item.system.type) as | type |}}
|
||||
<div>{{localize type.label}}</div>
|
||||
{{/with}}
|
||||
</div>
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.ACTORS.Adversary.FIELDS.difficulty.label"}}</label>
|
||||
<div>{{item.system.difficulty}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tooltip-information-section spaced">
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.hitPoints"}}</label>
|
||||
<div>{{item.system.resources.hitPoints.max}}</div>
|
||||
</div>
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.stress"}}</label>
|
||||
<div>{{item.system.resources.stress.max}}</div>
|
||||
</div>
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.ITEMS.Armor.baseThresholds.major"}}</label>
|
||||
<div>{{item.system.damageThresholds.major}}</div>
|
||||
</div>
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.ITEMS.Armor.baseThresholds.severe"}}</label>
|
||||
<div>{{item.system.damageThresholds.severe}}</div>
|
||||
</div>
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.attack"}}</label>
|
||||
<div>{{numberFormat item.system.attack.roll.bonus sign=true}}</div>
|
||||
</div>
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.damage"}}</label>
|
||||
<div>{{damageFormula item.system.attack item}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<strong>{{localize "DAGGERHEART.GENERAL.Experience.plural"}}</strong>
|
||||
{{#each item.system.experiences as | experience |}}
|
||||
<div>{{experience.name}} {{numberFormat experience.total sign=true}}</div>
|
||||
{{/each}}
|
||||
|
||||
<div class="tooltip-information-section">
|
||||
<div class="tooltip-information full-width">
|
||||
<label>{{localize "DAGGERHEART.ACTORS.Adversary.FIELDS.motivesAndTactics.label"}}</label>
|
||||
<div>{{item.system.motivesAndTactics}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{> "systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs" features=item.system.features }}
|
||||
</div>
|
||||
|
|
@ -1,5 +1,37 @@
|
|||
<div>
|
||||
<div>{{name}}</div>
|
||||
<img src="{{img}}" />
|
||||
<div>{{{system.description}}}</div>
|
||||
<div class="daggerheart dh-style tooltip">
|
||||
<h2 class="tooltip-title">{{item.name}}</h2>
|
||||
<img class="tooltip-image" src="{{item.img}}" />
|
||||
<div class="tooltip-description">{{{item.system.description}}}</div>
|
||||
|
||||
<div class="tooltip-information-section">
|
||||
<div class="tooltip-information full-width">
|
||||
<label>{{localize "DAGGERHEART.ITEMS.Armor.baseScore"}}</label>
|
||||
<div>{{item.system.baseScore}}</div>
|
||||
</div>
|
||||
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.ITEMS.Armor.baseThresholds.major"}}</label>
|
||||
<div>{{item.system.baseThresholds.major}}</div>
|
||||
</div>
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.ITEMS.Armor.baseThresholds.severe"}}</label>
|
||||
<div>{{item.system.baseThresholds.severe}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if (gt item.system.armorFeatures.length 0)}}<h4 class="tooltip-sub-title">{{localize "DAGGERHEART.GENERAL.features"}}</h4>{{/if}}
|
||||
<div class="tooltip-tags">
|
||||
{{#each item.system.armorFeatures}}
|
||||
{{#with (lookup ../config.ITEM.armorFeatures this.value) as | feature | }}
|
||||
<div class="tooltip-tag">
|
||||
<div class="tooltip-tag-label-container">
|
||||
<div class="tooltip-tag-label">{{localize feature.label}}</div>
|
||||
</div>
|
||||
<div class="tooltip-tag-description">{{{localize feature.description}}}</div>
|
||||
</div>
|
||||
{{/with}}
|
||||
{{/each}}
|
||||
</div>
|
||||
|
||||
{{> "systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs" features=item.system.customActions label=(localize "DAGGERHEART.GENERAL.Action.plural")}}
|
||||
</div>
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
<div>
|
||||
<div>{{name}}</div>
|
||||
<img src="{{system.tokenImg}}" />
|
||||
<div>{{{system.examples}}}</div>
|
||||
<div>{{system.advantageOn}}</div>
|
||||
</div>
|
||||
14
templates/ui/tooltip/consumable.hbs
Normal file
14
templates/ui/tooltip/consumable.hbs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<div class="daggerheart dh-style tooltip">
|
||||
<h2 class="tooltip-title">{{item.name}}</h2>
|
||||
<img class="tooltip-image" src="{{item.img}}" />
|
||||
<div class="tooltip-description">{{{item.system.description}}}</div>
|
||||
|
||||
<div class="tooltip-information-section">
|
||||
<div class="tooltip-information full-width">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.quantity"}}</label>
|
||||
<div>{{item.system.quantity}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{> "systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs" features=item.system.actions label=(localize "DAGGERHEART.GENERAL.Action.plural") }}
|
||||
</div>
|
||||
|
|
@ -1,5 +1,31 @@
|
|||
<div>
|
||||
<div>{{name}}</div>
|
||||
<img src="{{img}}" />
|
||||
<div>{{{system.description}}}</div>
|
||||
<div class="daggerheart dh-style tooltip">
|
||||
<h2 class="tooltip-title">{{item.name}}</h2>
|
||||
<img class="tooltip-image" src="{{item.img}}" />
|
||||
<div class="tooltip-description">{{{item.system.description}}}</div>
|
||||
|
||||
<div class="tooltip-information-section">
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.Domain.single"}}</label>
|
||||
{{#with (lookup config.DOMAIN.domains item.system.domain) as | domain |}}
|
||||
<div>{{localize domain.label}}</div>
|
||||
{{/with}}
|
||||
</div>
|
||||
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "Type"}}</label>
|
||||
{{#with (lookup config.DOMAIN.cardTypes item.system.type) as | type |}}
|
||||
<div>{{localize type.label}}</div>
|
||||
{{/with}}
|
||||
</div>
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.level"}}</label>
|
||||
<div>{{item.system.level}}</div>
|
||||
</div>
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.ITEMS.DomainCard.recallCost"}}</label>
|
||||
<div>{{item.system.recallCost}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{> "systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs" features=item.system.actions label=(localize "DAGGERHEART.GENERAL.Action.plural") }}
|
||||
</div>
|
||||
8
templates/ui/tooltip/feature.hbs
Normal file
8
templates/ui/tooltip/feature.hbs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<div class="daggerheart dh-style tooltip">
|
||||
<h2 class="tooltip-title">{{item.name}}</h2>
|
||||
<img class="tooltip-image" src="{{item.img}}" />
|
||||
<div class="tooltip-description">{{{item.system.description}}}</div>
|
||||
|
||||
{{> "systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs" features=item.system.actions label=(localize "DAGGERHEART.GENERAL.Action.plural") }}
|
||||
{{> "systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs" features=item.effects label=(localize "DAGGERHEART.GENERAL.Effect.plural") }}
|
||||
</div>
|
||||
7
templates/ui/tooltip/miscellaneous.hbs
Normal file
7
templates/ui/tooltip/miscellaneous.hbs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<div class="daggerheart dh-style tooltip">
|
||||
<h2 class="tooltip-title">{{item.name}}</h2>
|
||||
<img class="tooltip-image" src="{{item.img}}" />
|
||||
<div class="tooltip-description">{{{item.system.description}}}</div>
|
||||
|
||||
{{> "systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs" features=item.system.actions label=(localize "DAGGERHEART.GENERAL.Action.plural") }}
|
||||
</div>
|
||||
12
templates/ui/tooltip/parts/tooltipTags.hbs
Normal file
12
templates/ui/tooltip/parts/tooltipTags.hbs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{{#if (gt features.length 0)}}<h4 class="tooltip-sub-title">{{label}}</h4>{{/if}}
|
||||
<div class="tooltip-tags">
|
||||
{{#each features as | feature |}}
|
||||
<div class="tooltip-tag">
|
||||
<div class="tooltip-tag-label-container">
|
||||
<div class="tooltip-tag-label">{{localize feature.name}}</div>
|
||||
{{#if feature.img}}<img class="tooltip-tag-image" src="{{feature.img}}" />{{/if}}
|
||||
</div>
|
||||
<div class="tooltip-tag-description">{{{localize (tertiary feature.description feature.system.description)}}}</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
|
|
@ -1,5 +1,56 @@
|
|||
<div>
|
||||
<div>{{name}}</div>
|
||||
<img src="{{img}}" />
|
||||
<div>{{{system.description}}}</div>
|
||||
<div class="daggerheart dh-style tooltip">
|
||||
<h2 class="tooltip-title">{{item.name}}</h2>
|
||||
<img class="tooltip-image" src="{{item.img}}" />
|
||||
<div class="tooltip-description">{{{item.system.description}}}</div>
|
||||
|
||||
<div class="tooltip-information-section">
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.ITEMS.Weapon.weaponType"}}</label>
|
||||
<div>{{#if item.system.secondaryWeapon}}{{localize "DAGGERHEART.ITEMS.Weapon.secondaryWeapon"}}{{else}}{{localize "DAGGERHEART.ITEMS.Weapon.primaryWeapon"}}{{/if}}</div>
|
||||
</div>
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.burden"}}</label>
|
||||
{{#with (lookup config.GENERAL.burden item.system.burden) as | burden |}}
|
||||
<div>{{localize burden.label}}</div>
|
||||
{{/with}}
|
||||
</div>
|
||||
{{#if item.system.attack.roll.trait}}
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.Trait.single"}}</label>
|
||||
{{#with (lookup config.ACTOR.abilities item.system.attack.roll.trait) as | trait |}}
|
||||
<div>{{localize trait.label}}</div>
|
||||
{{/with}}
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.range"}}</label>
|
||||
{{#with (lookup config.GENERAL.range item.system.attack.range) as | range |}}
|
||||
<div>{{localize range.label}}</div>
|
||||
{{/with}}
|
||||
</div>
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.damage"}}</label>
|
||||
<div>{{{damageFormula item.system.attack item.parent}}}</div>
|
||||
</div>
|
||||
<div class="tooltip-information">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.damageType"}}</label>
|
||||
<div>{{{damageSymbols item.system.attack.damage.parts}}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if (gt item.system.weaponFeatures.length 0)}}<h4 class="tooltip-sub-title">{{localize "DAGGERHEART.GENERAL.features"}}</h4>{{/if}}
|
||||
<div class="tooltip-tags">
|
||||
{{#each item.system.weaponFeatures}}
|
||||
{{#with (lookup ../config.ITEM.weaponFeatures this.value) as | feature | }}
|
||||
<div class="tooltip-tag">
|
||||
<div class="tooltip-tag-label-container">
|
||||
<div class="tooltip-tag-label">{{localize feature.label}}</div>
|
||||
</div>
|
||||
<div class="tooltip-tag-description">{{{localize feature.description}}}</div>
|
||||
</div>
|
||||
{{/with}}
|
||||
{{/each}}
|
||||
</div>
|
||||
|
||||
{{> "systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs" features=item.system.customActions label=(localize "DAGGERHEART.GENERAL.Action.plural") }}
|
||||
</div>
|
||||
Loading…
Add table
Add a link
Reference in a new issue