Merged with main

This commit is contained in:
WBHarry 2025-07-02 23:50:29 +02:00
commit 978d45b931
44 changed files with 653 additions and 373 deletions

View file

@ -125,6 +125,7 @@ Hooks.once('init', () => {
CONFIG.ui.resources = Resources; CONFIG.ui.resources = Resources;
CONFIG.ux.ContextMenu = applications.DhContextMenu; CONFIG.ux.ContextMenu = applications.DhContextMenu;
CONFIG.ux.TooltipManager = applications.DhTooltipManager;
game.socket.on(`system.${SYSTEM.id}`, handleSocketEvent); game.socket.on(`system.${SYSTEM.id}`, handleSocketEvent);

View file

@ -21,6 +21,11 @@
} }
}, },
"DAGGERHEART": { "DAGGERHEART": {
"UI": {
"notifications": {
"adversaryMissing": "The linked adversary doesn't exist in the world."
}
},
"Settings": { "Settings": {
"Menu": { "Menu": {
"Automation": { "Automation": {
@ -193,6 +198,10 @@
"Full": "Disadvantage", "Full": "Disadvantage",
"Short": "Dis" "Short": "Dis"
}, },
"Neutral": {
"Full": "None",
"Short": "no"
},
"OK": "OK", "OK": "OK",
"Cancel": "Cancel", "Cancel": "Cancel",
"Or": "Or", "Or": "Or",
@ -1130,6 +1139,7 @@
"RemoveCountdownText": "Are you sure you want to remove the countdown: {name}?", "RemoveCountdownText": "Are you sure you want to remove the countdown: {name}?",
"OpenOwnership": "Edit Player Ownership", "OpenOwnership": "Edit Player Ownership",
"Title": "{type} Countdowns", "Title": "{type} Countdowns",
"ToggleSimple": "Toggle Simple View",
"Types": { "Types": {
"narrative": "Narrative", "narrative": "Narrative",
"encounter": "Encounter" "encounter": "Encounter"

View file

@ -16,5 +16,6 @@ export { default as DhpEnvironment } from './sheets/actors/environment.mjs';
export { default as DhActiveEffectConfig } from './sheets/activeEffectConfig.mjs'; export { default as DhActiveEffectConfig } from './sheets/activeEffectConfig.mjs';
export { default as DhContextMenu } from './contextMenu.mjs'; export { default as DhContextMenu } from './contextMenu.mjs';
export { default as DhBeastform } from './sheets/items/beastform.mjs'; export { default as DhBeastform } from './sheets/items/beastform.mjs';
export { default as DhTooltipManager } from './tooltipManager.mjs';
export * as api from './sheets/api/_modules.mjs'; export * as api from './sheets/api/_modules.mjs';

View file

@ -1,5 +1,6 @@
import { countdownTypes } from '../config/generalConfig.mjs'; import { countdownTypes } from '../config/generalConfig.mjs';
import { GMUpdateEvent, RefreshType, socketEvent } from '../helpers/socket.mjs'; import { GMUpdateEvent, RefreshType, socketEvent } from '../helpers/socket.mjs';
import constructHTMLButton from '../helpers/utils.mjs';
import OwnershipSelection from './ownershipSelection.mjs'; import OwnershipSelection from './ownershipSelection.mjs';
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
@ -25,14 +26,15 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
frame: true, frame: true,
title: 'Countdowns', title: 'Countdowns',
resizable: true, resizable: true,
minimizable: true minimizable: false
}, },
actions: { actions: {
addCountdown: this.addCountdown, addCountdown: this.addCountdown,
removeCountdown: this.removeCountdown, removeCountdown: this.removeCountdown,
editImage: this.onEditImage, editImage: this.onEditImage,
openOwnership: this.openOwnership, openOwnership: this.openOwnership,
openCountdownOwnership: this.openCountdownOwnership openCountdownOwnership: this.openCountdownOwnership,
toggleSimpleView: this.toggleSimpleView
}, },
form: { handler: this.updateData, submitOnChange: true } form: { handler: this.updateData, submitOnChange: true }
}; };
@ -53,11 +55,47 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
}); });
} }
async _onFirstRender(context, options) { async _preFirstRender(context, options) {
super._onFirstRender(context, options); options.position =
game.user.getFlag(SYSTEM.id, SYSTEM.FLAGS[`${this.basePath}Countdown`].position) ??
Countdowns.DEFAULT_OPTIONS.position;
this.element.querySelector('.expanded-view').classList.toggle('hidden'); const viewSetting =
this.element.querySelector('.minimized-view').classList.toggle('hidden'); game.user.getFlag(SYSTEM.id, SYSTEM.FLAGS[`${this.basePath}Countdown`].simple) ?? !game.user.isGM;
this.simpleView =
game.user.isGM || !this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER) ? viewSetting : true;
context.simple = this.simpleView;
}
_onPosition(position) {
game.user.setFlag(SYSTEM.id, SYSTEM.FLAGS[`${this.basePath}Countdown`].position, position);
}
async _renderFrame(options) {
const frame = await super._renderFrame(options);
if (this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER)) {
const button = constructHTMLButton({
label: '',
classes: ['header-control', 'icon', 'fa-solid', 'fa-wrench'],
dataset: { action: 'toggleSimpleView', tooltip: 'DAGGERHEART.Countdown.ToggleSimple' }
});
this.window.controls.after(button);
}
return frame;
}
testUserPermission(level, exact, altSettings) {
if (game.user.isGM) return true;
const settings =
altSettings ?? game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath];
const defaultAllowed = exact ? settings.ownership.default === level : settings.ownership.default >= level;
const userAllowed = exact
? settings.playerOwnership[game.user.id]?.value === level
: settings.playerOwnership[game.user.id]?.value >= level;
return defaultAllowed || userAllowed;
} }
async _prepareContext(_options) { async _prepareContext(_options) {
@ -67,15 +105,17 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
context.isGM = game.user.isGM; context.isGM = game.user.isGM;
context.base = this.basePath; context.base = this.basePath;
context.canCreate = countdownData.playerOwnership[game.user.id].value === CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER; context.canCreate = this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER, true);
context.source = { context.source = {
...countdownData, ...countdownData,
countdowns: Object.keys(countdownData.countdowns).reduce((acc, key) => { countdowns: Object.keys(countdownData.countdowns).reduce((acc, key) => {
const countdown = countdownData.countdowns[key]; const countdown = countdownData.countdowns[key];
const ownershipValue = countdown.playerOwnership[game.user.id].value; if (this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.LIMITED, false, countdown)) {
if (ownershipValue > CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE) { acc[key] = {
acc[key] = { ...countdown, canEdit: ownershipValue === CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER }; ...countdown,
canEdit: this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER, true, countdown)
};
} }
return acc; return acc;
@ -83,7 +123,7 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
}; };
context.systemFields = countdownData.schema.fields; context.systemFields = countdownData.schema.fields;
context.countdownFields = context.systemFields.countdowns.element.fields; context.countdownFields = context.systemFields.countdowns.element.fields;
context.minimized = this.minimized || _options.isFirstRender; context.simple = this.simpleView;
return context; return context;
} }
@ -110,28 +150,6 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
} }
} }
async minimize() {
await super.minimize();
this.element.querySelector('.expanded-view').classList.toggle('hidden');
this.element.querySelector('.minimized-view').classList.toggle('hidden');
}
async maximize() {
if (this.minimized) {
const settings = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath];
if (settings.playerOwnership[game.user.id].value <= CONST.DOCUMENT_OWNERSHIP_LEVELS.LIMITED) {
ui.notifications.info(game.i18n.localize('DAGGERHEART.Countdown.Notifications.LimitedOwnership'));
return;
}
this.element.querySelector('.expanded-view').classList.toggle('hidden');
this.element.querySelector('.minimized-view').classList.toggle('hidden');
}
await super.maximize();
}
async updateSetting(update) { async updateSetting(update) {
if (game.user.isGM) { if (game.user.isGM) {
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, update); await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, update);
@ -213,11 +231,17 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) {
}); });
} }
static async toggleSimpleView() {
this.simpleView = !this.simpleView;
await game.user.setFlag(SYSTEM.id, SYSTEM.FLAGS[`${this.basePath}Countdown`].simple, this.simpleView);
this.render();
}
async updateCountdownValue(event, increase) { async updateCountdownValue(event, increase) {
const countdownSetting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns); const countdownSetting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns);
const countdown = countdownSetting[this.basePath].countdowns[event.currentTarget.dataset.countdown]; const countdown = countdownSetting[this.basePath].countdowns[event.currentTarget.dataset.countdown];
if (countdown.playerOwnership[game.user.id] < CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER) { if (!this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER)) {
return; return;
} }

View file

@ -1,3 +1,5 @@
/** NOT USED ANYMORE - TO BE DELETED **/
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
export default class NpcRollSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) { export default class NpcRollSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) {

View file

@ -8,6 +8,7 @@ import DamageDialog from '../dialogs/damageDialog.mjs';
*/ */
export class DHRoll extends Roll { export class DHRoll extends Roll {
baseTerms = [];
constructor(formula, data, options) { constructor(formula, data, options) {
super(formula, data, options); super(formula, data, options);
} }
@ -37,6 +38,7 @@ export class DHRoll extends Roll {
if (config.dialog.configure !== false) { if (config.dialog.configure !== false) {
// Open Roll Dialog // Open Roll Dialog
const DialogClass = config.dialog?.class ?? this.DefaultDialog; const DialogClass = config.dialog?.class ?? this.DefaultDialog;
console.log(roll, config);
const configDialog = await DialogClass.configure(roll, config, message); const configDialog = await DialogClass.configure(roll, config, message);
if (!configDialog) return; if (!configDialog) return;
} }
@ -54,7 +56,6 @@ export class DHRoll extends Roll {
} }
static async buildPost(roll, config, message) { static async buildPost(roll, config, message) {
console.log(config)
for (const hook of config.hooks) { for (const hook of config.hooks) {
if (Hooks.call(`${SYSTEM.id}.postRoll${hook.capitalize()}`, config, message) === false) return null; if (Hooks.call(`${SYSTEM.id}.postRoll${hook.capitalize()}`, config, message) === false) return null;
} }
@ -98,9 +99,27 @@ export class DHRoll extends Roll {
config.dialog.configure ??= !(config.event.shiftKey || config.event.altKey || config.event.ctrlKey); config.dialog.configure ??= !(config.event.shiftKey || config.event.altKey || config.event.ctrlKey);
} }
formatModifier(modifier) {
const numTerm = modifier < 0 ? '-' : '+';
return [
new foundry.dice.terms.OperatorTerm({ operator: numTerm }),
new foundry.dice.terms.NumericTerm({ number: Math.abs(modifier) })
];
}
getFaces(faces) {
return Number(faces.startsWith('d') ? faces.replace('d', '') : faces);
}
constructFormula(config) { constructFormula(config) {
// const formula = Roll.replaceFormulaData(this.options.roll.formula, config.data);
this.terms = Roll.parse(this.options.roll.formula, config.data); this.terms = Roll.parse(this.options.roll.formula, config.data);
if (this.options.extraFormula) {
this.terms.push(
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
...this.constructor.parse(this.options.extraFormula, this.options.data)
);
}
return (this._formula = this.constructor.getFormula(this.terms)); return (this._formula = this.constructor.getFormula(this.terms));
} }
} }
@ -114,10 +133,6 @@ export class DualityDie extends foundry.dice.terms.Die {
export class D20Roll extends DHRoll { export class D20Roll extends DHRoll {
constructor(formula, data = {}, options = {}) { constructor(formula, data = {}, options = {}) {
super(formula, data, options); super(formula, data, options);
// this.createBaseDice();
// this.configureModifiers();
// this._formula = this.resetFormula();
this.constructFormula(); this.constructFormula();
} }
@ -140,7 +155,7 @@ export class D20Roll extends DHRoll {
set d20(faces) { set d20(faces) {
if (!(this.terms[0] instanceof foundry.dice.terms.Die)) this.createBaseDice(); if (!(this.terms[0] instanceof foundry.dice.terms.Die)) this.createBaseDice();
this.terms[0].faces = faces; this.terms[0].faces = this.getFaces(faces);
} }
get dAdvantage() { get dAdvantage() {
@ -153,11 +168,11 @@ export class D20Roll extends DHRoll {
} }
get hasAdvantage() { get hasAdvantage() {
return this.options.advantage === this.constructor.ADV_MODE.ADVANTAGE; return this.options.roll.advantage === this.constructor.ADV_MODE.ADVANTAGE;
} }
get hasDisadvantage() { get hasDisadvantage() {
return this.options.advantage === this.constructor.ADV_MODE.DISADVANTAGE; return this.options.roll.advantage === this.constructor.ADV_MODE.DISADVANTAGE;
} }
static applyKeybindings(config) { static applyKeybindings(config) {
@ -171,18 +186,55 @@ export class D20Roll extends DHRoll {
config.dialog.configure ??= !Object.values(keys).some(k => k); config.dialog.configure ??= !Object.values(keys).some(k => k);
// Determine advantage mode // Determine advantage mode
const advantage = config.advantage || keys.advantage; const advantage = config.roll.advantage === this.ADV_MODE.ADVANTAGE || keys.advantage;
const disadvantage = config.disadvantage || keys.disadvantage; const disadvantage = config.roll.advantage === this.ADV_MODE.DISADVANTAGE || keys.disadvantage;
if (advantage && !disadvantage) config.advantage = this.ADV_MODE.ADVANTAGE; if (advantage && !disadvantage) config.roll.advantage = this.ADV_MODE.ADVANTAGE;
else if (!advantage && disadvantage) config.advantage = this.ADV_MODE.DISADVANTAGE; else if (!advantage && disadvantage) config.roll.advantage = this.ADV_MODE.DISADVANTAGE;
else config.advantage = this.ADV_MODE.NORMAL; else config.roll.advantage = this.ADV_MODE.NORMAL;
}
constructFormula(config) {
// this.terms = [];
this.createBaseDice();
this.configureModifiers();
this.resetFormula();
return this._formula;
} }
createBaseDice() { createBaseDice() {
if (this.terms[0] instanceof foundry.dice.terms.Die) return; if (this.terms[0] instanceof foundry.dice.terms.Die) {
this.terms = [this.terms[0]];
return;
}
this.terms[0] = new foundry.dice.terms.Die({ faces: 20 }); this.terms[0] = new foundry.dice.terms.Die({ faces: 20 });
} }
configureModifiers() {
this.applyAdvantage();
this.applyBaseBonus();
this.options.experiences?.forEach(m => {
if (this.options.data.experiences?.[m])
this.options.roll.modifiers.push({
label: this.options.data.experiences[m].name,
value: this.options.data.experiences[m].total ?? this.options.data.experiences[m].value
});
});
this.options.roll.modifiers?.forEach(m => {
this.terms.push(...this.formatModifier(m.value));
});
this.baseTerms = foundry.utils.deepClone(this.terms);
if (this.options.extraFormula) {
this.terms.push(
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
...this.constructor.parse(this.options.extraFormula, this.options.data)
);
}
}
applyAdvantage() { applyAdvantage() {
this.d20.modifiers.findSplice(m => ['kh', 'kl'].includes(m)); this.d20.modifiers.findSplice(m => ['kh', 'kl'].includes(m));
if (!this.hasAdvantage && !this.hasDisadvantage) this.number = 1; if (!this.hasAdvantage && !this.hasDisadvantage) this.number = 1;
@ -192,47 +244,14 @@ export class D20Roll extends DHRoll {
} }
} }
// Trait bonus != Adversary
configureModifiers() {
this.applyAdvantage();
// this.options.roll.modifiers = [];
this.applyBaseBonus();
this.options.experiences?.forEach(m => {
if (this.options.data.experiences?.[m])
this.options.roll.modifiers.push({
label: this.options.data.experiences[m].name,
value: this.options.data.experiences[m].total ?? this.options.data.experiences[m].value
});
});
this.options.roll.modifiers?.forEach(m => {
this.terms.push(...this.formatModifier(m.value));
});
if (this.options.extraFormula) {
this.terms.push(
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
...this.constructor.parse(this.options.extraFormula, this.getRollData())
);
}
// this.resetFormula();
}
constructFormula(config) {
this.terms = [];
this.createBaseDice();
this.configureModifiers();
this.resetFormula();
return this._formula;
}
applyBaseBonus() { applyBaseBonus() {
this.options.roll.modifiers = [ this.options.roll.modifiers = [];
{ if (!this.options.roll.bonus) return;
label: 'Bonus to Hit', this.options.roll.modifiers.push({
value: Roll.replaceFormulaData('@attackBonus', this.data) label: 'Bonus to Hit',
} value: this.options.roll.bonus
]; // value: Roll.replaceFormulaData('@attackBonus', this.data)
});
} }
static postEvaluate(roll, config = {}) { static postEvaluate(roll, config = {}) {
@ -245,23 +264,27 @@ export class D20Roll extends DHRoll {
} else if (config.roll.difficulty) } else if (config.roll.difficulty)
config.roll.success = roll.isCritical || roll.total >= config.roll.difficulty; config.roll.success = roll.isCritical || roll.total >= config.roll.difficulty;
config.roll.advantage = { config.roll.advantage = {
type: config.advantage, type: config.roll.advantage,
dice: roll.dAdvantage?.denomination, dice: roll.dAdvantage?.denomination,
value: roll.dAdvantage?.total value: roll.dAdvantage?.total
}; };
config.roll.modifierTotal = config.roll.modifiers.reduce((a, c) => a + Number(c.value), 0); config.roll.extra = roll.dice
} .filter(d => !roll.baseTerms.includes(d))
.map(d => {
getRollData() { return {
return this.options.data; dice: d.denomination,
} value: d.total
};
formatModifier(modifier) { });
const numTerm = modifier < 0 ? '-' : '+'; config.roll.modifierTotal = 0;
return [ for (let i = 0; i < roll.terms.length; i++) {
new foundry.dice.terms.OperatorTerm({ operator: numTerm }), if (
new foundry.dice.terms.NumericTerm({ number: Math.abs(modifier) }) roll.terms[i] instanceof foundry.dice.terms.NumericTerm &&
]; !!roll.terms[i - 1] &&
roll.terms[i - 1] instanceof foundry.dice.terms.OperatorTerm
)
config.roll.modifierTotal += Number(`${roll.terms[i - 1].operator}${roll.terms[i].total}`);
}
} }
resetFormula() { resetFormula() {
@ -270,6 +293,8 @@ export class D20Roll extends DHRoll {
} }
export class DualityRoll extends D20Roll { export class DualityRoll extends D20Roll {
_advantageFaces = 6;
constructor(formula, data = {}, options = {}) { constructor(formula, data = {}, options = {}) {
super(formula, data, options); super(formula, data, options);
} }
@ -287,7 +312,7 @@ export class DualityRoll extends D20Roll {
set dHope(faces) { set dHope(faces) {
if (!(this.dice[0] instanceof CONFIG.Dice.daggerheart.DualityDie)) this.createBaseDice(); if (!(this.dice[0] instanceof CONFIG.Dice.daggerheart.DualityDie)) this.createBaseDice();
this.terms[0].faces = faces; this.terms[0].faces = this.getFaces(faces);
// this.#hopeDice = `d${face}`; // this.#hopeDice = `d${face}`;
} }
@ -300,7 +325,7 @@ export class DualityRoll extends D20Roll {
set dFear(faces) { set dFear(faces) {
if (!(this.dice[1] instanceof CONFIG.Dice.daggerheart.DualityDie)) this.createBaseDice(); if (!(this.dice[1] instanceof CONFIG.Dice.daggerheart.DualityDie)) this.createBaseDice();
this.dice[1].faces = faces; this.dice[1].faces = this.getFaces(faces);
// this.#fearDice = `d${face}`; // this.#fearDice = `d${face}`;
} }
@ -308,6 +333,14 @@ export class DualityRoll extends D20Roll {
return this.dice[2]; return this.dice[2];
} }
get advantageFaces() {
return this._advantageFaces;
}
set advantageFaces(faces) {
this._advantageFaces = this.getFaces(faces);
}
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;
@ -337,21 +370,23 @@ export class DualityRoll extends D20Roll {
return game.i18n.localize(label); return game.i18n.localize(label);
} }
updateFormula() {}
createBaseDice() { createBaseDice() {
if ( if (
this.dice[0] instanceof CONFIG.Dice.daggerheart.DualityDie && this.dice[0] instanceof CONFIG.Dice.daggerheart.DualityDie &&
this.dice[1] instanceof CONFIG.Dice.daggerheart.DualityDie this.dice[1] instanceof CONFIG.Dice.daggerheart.DualityDie
) ) {
this.terms = [this.terms[0], this.terms[1], this.terms[2]];
return; return;
if (!(this.dice[0] instanceof CONFIG.Dice.daggerheart.DualityDie)) }
this.terms[0] = new CONFIG.Dice.daggerheart.DualityDie(); this.terms[0] = new CONFIG.Dice.daggerheart.DualityDie();
this.terms[1] = new foundry.dice.terms.OperatorTerm({ operator: '+' }); this.terms[1] = new foundry.dice.terms.OperatorTerm({ operator: '+' });
if (!(this.dice[2] instanceof CONFIG.Dice.daggerheart.DualityDie)) this.terms[2] = new CONFIG.Dice.daggerheart.DualityDie();
this.terms[2] = new CONFIG.Dice.daggerheart.DualityDie();
} }
applyAdvantage() { applyAdvantage() {
const dieFaces = 6, const dieFaces = this.advantageFaces,
bardRallyFaces = this.hasBarRally, bardRallyFaces = this.hasBarRally,
advDie = new foundry.dice.terms.Die({ faces: dieFaces }); advDie = new foundry.dice.terms.Die({ faces: dieFaces });
if (this.hasAdvantage || this.hasDisadvantage || bardRallyFaces) if (this.hasAdvantage || this.hasDisadvantage || bardRallyFaces)
@ -372,12 +407,12 @@ export class DualityRoll extends D20Roll {
} }
applyBaseBonus() { applyBaseBonus() {
this.options.roll.modifiers = [ this.options.roll.modifiers = [];
{ if (!this.options.roll.trait) return;
label: `DAGGERHEART.Abilities.${this.options.roll.trait}.name`, this.options.roll.modifiers.push({
value: Roll.replaceFormulaData(`@traits.${this.options.roll.trait}.total`, this.data) label: `DAGGERHEART.Abilities.${this.options.roll.trait}.name`,
} value: Roll.replaceFormulaData(`@traits.${this.options.roll.trait}.total`, this.data)
]; });
} }
static postEvaluate(roll, config = {}) { static postEvaluate(roll, config = {}) {
@ -410,9 +445,9 @@ export class DamageRoll extends DHRoll {
static async postEvaluate(roll, config = {}) { static async postEvaluate(roll, config = {}) {
super.postEvaluate(roll, config); super.postEvaluate(roll, config);
config.roll.type = config.type; config.roll.type = config.type;
if(config.source?.message) { if (config.source?.message) {
const chatMessage = ui.chat.collection.get(config.source.message); const chatMessage = ui.chat.collection.get(config.source.message);
chatMessage.update({'system.damage': config}); chatMessage.update({ 'system.damage': config });
} }
} }
} }

View file

@ -1,9 +1,11 @@
/** NOT USED ANYMORE - TO BE DELETED **/
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api; const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
export default class RollSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) { export default class RollSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(experiences, costs, action, resolve) { constructor(experiences, costs, action, resolve) {
super({}, {}); super({}, {});
this.experiences = experiences; this.experiences = experiences;
this.costs = costs; this.costs = costs;
this.action = action; this.action = action;
@ -65,9 +67,9 @@ export default class RollSelectionDialog extends HandlebarsApplicationMixin(Appl
context.fear = this.data.fear; context.fear = this.data.fear;
context.advantage = this.data.advantage; context.advantage = this.data.advantage;
context.experiences = Object.keys(this.experiences).map(id => ({ id, ...this.experiences[id] })); context.experiences = Object.keys(this.experiences).map(id => ({ id, ...this.experiences[id] }));
if(this.costs?.length) { if (this.costs?.length) {
const updatedCosts = this.action.calcCosts(this.costs); const updatedCosts = this.action.calcCosts(this.costs);
context.costs = updatedCosts context.costs = updatedCosts;
context.canRoll = this.action.getRealCosts(updatedCosts)?.hasCost; context.canRoll = this.action.getRealCosts(updatedCosts)?.hasCost;
} else context.canRoll = true; } else context.canRoll = true;

View file

@ -87,7 +87,7 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
event: event, event: event,
title: `${this.actor.name} - Reaction Roll`, title: `${this.actor.name} - Reaction Roll`,
roll: { roll: {
modifier: null, // modifier: null,
type: 'reaction' type: 'reaction'
}, },
chatMessage: { chatMessage: {

View file

@ -12,7 +12,6 @@ export default class DhpEnvironment extends DaggerheartSheet(ActorSheetV2) {
actions: { actions: {
addAdversary: this.addAdversary, addAdversary: this.addAdversary,
deleteProperty: this.deleteProperty, deleteProperty: this.deleteProperty,
viewAdversary: this.viewAdversary,
openSettings: this.openSettings, openSettings: this.openSettings,
useItem: this.useItem, useItem: this.useItem,
toChat: this.toChat toChat: this.toChat
@ -103,14 +102,24 @@ export default class DhpEnvironment extends DaggerheartSheet(ActorSheetV2) {
this.render(); this.render();
} }
static async viewAdversary(_, button) { async viewAdversary(_, button) {
const adversary = await foundry.utils.fromUuid(button.dataset.adversary); const target = button.closest('[data-item-uuid]');
const adversary = await foundry.utils.fromUuid(target.dataset.itemUuid);
if (!adversary) {
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.notifications.adversaryMissing'));
return;
}
adversary.sheet.render(true); adversary.sheet.render(true);
} }
static async useItem(event) { static async useItem(event, button) {
const action = this.getAction(event); const action = this.getAction(event);
action.use(event); if (!action) {
await this.viewAdversary(event, button);
} else {
action.use(event);
}
} }
static async toChat(event) { static async toChat(event) {

View file

@ -103,6 +103,7 @@ export default class DHAdversarySettings extends HandlebarsApplicationMixin(Appl
context.systemFields = this.actor.system.schema.fields; context.systemFields = this.actor.system.schema.fields;
context.systemFields.attack.fields = this.actor.system.attack.schema.fields; context.systemFields.attack.fields = this.actor.system.attack.schema.fields;
context.isNPC = true; context.isNPC = true;
console.log(context);
return context; return context;
} }

View file

@ -181,13 +181,20 @@ export default class DHEnvironmentSettings extends HandlebarsApplicationMixin(Ap
static async #viewAdversary(_, button) { static async #viewAdversary(_, button) {
const adversary = await foundry.utils.fromUuid(button.dataset.adversary); const adversary = await foundry.utils.fromUuid(button.dataset.adversary);
if (!adversary) {
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.notifications.adversaryMissing'));
return;
}
adversary.sheet.render(true); adversary.sheet.render(true);
} }
static async #deleteAdversary(event, target) { static async #deleteAdversary(event, target) {
const adversaryKey = target.dataset.adversary; const adversaryKey = target.dataset.adversary;
const path = `system.potentialAdversaries.${target.dataset.potentialAdversary}.adversaries`; const path = `system.potentialAdversaries.${target.dataset.potentialAdversary}.adversaries`;
const newAdversaries = foundry.utils.getProperty(this.actor, path).filter(x => x.uuid !== adversaryKey); const newAdversaries = foundry.utils
.getProperty(this.actor, path)
.filter(x => x && (x?.uuid ?? x) !== adversaryKey);
await this.actor.update({ [path]: newAdversaries }); await this.actor.update({ [path]: newAdversaries });
this.render(); this.render();
} }

View file

@ -0,0 +1,16 @@
export default class DhTooltipManager extends TooltipManager {
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));
if (item) {
html = await foundry.applications.handlebars.renderTemplate(
`systems/daggerheart/templates/tooltip/${item.type}.hbs`,
item
);
}
}
super.activate(element, { ...options, html: html });
}
}

View file

@ -110,3 +110,18 @@ export const diceCompare = {
operator: '>' operator: '>'
} }
}; };
export const advandtageState = {
disadvantage: {
label: 'DAGGERHEART.General.Disadvantage.Full',
value: -1
},
neutral: {
label: 'DAGGERHEART.General.Neutral.Full',
value: 0
},
advantage: {
label: 'DAGGERHEART.General.Advantage.Full',
value: 1
}
};

View file

@ -1 +1,9 @@
export const displayDomainCardsAsList = 'displayDomainCardsAsList'; export const displayDomainCardsAsList = 'displayDomainCardsAsList';
export const narrativeCountdown = {
simple: 'countdown-narrative-simple',
position: 'countdown-narrative-position'
};
export const encounterCountdown = {
simple: 'countdown-encounter-simple',
position: 'countdown-encounter-position'
};

View file

@ -1,4 +1,3 @@
import CostSelectionDialog from '../../applications/costSelectionDialog.mjs';
import { DHActionDiceData, DHActionRollData, DHDamageData, DHDamageField } from './actionDice.mjs'; import { DHActionDiceData, DHActionRollData, DHDamageData, DHDamageField } from './actionDice.mjs';
import DhpActor from '../../documents/actor.mjs'; import DhpActor from '../../documents/actor.mjs';
import D20RollDialog from '../../dialogs/d20RollDialog.mjs'; import D20RollDialog from '../../dialogs/d20RollDialog.mjs';
@ -349,7 +348,9 @@ export class DHBaseAction extends foundry.abstract.DataModel {
label: 'Attack', label: 'Attack',
type: this.actionType, type: this.actionType,
difficulty: this.roll?.difficulty, difficulty: this.roll?.difficulty,
formula: this.roll.getFormula() formula: this.roll.getFormula(),
bonus: this.roll.bonus,
advantage: SYSTEM.ACTIONS.advandtageState[this.roll.advState].value
}; };
if (this.roll?.type === 'diceSet') roll.lite = true; if (this.roll?.type === 'diceSet') roll.lite = true;
@ -378,7 +379,7 @@ export class DHBaseAction extends foundry.abstract.DataModel {
/* ROLL */ /* ROLL */
get hasRoll() { get hasRoll() {
return !!this.roll?.type; return !!this.roll?.type || !!this.roll?.bonus;
} }
/* ROLL */ /* ROLL */

View file

@ -11,7 +11,8 @@ export class DHActionRollData extends foundry.abstract.DataModel {
type: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.GENERAL.rollTypes }), type: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.GENERAL.rollTypes }),
trait: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.ACTOR.abilities }), trait: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.ACTOR.abilities }),
difficulty: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }), difficulty: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }),
bonus: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }), bonus: new fields.NumberField({ nullable: true, initial: null, integer: true }),
advState: new fields.StringField({ choices: SYSTEM.ACTIONS.advandtageState, initial: 'neutral' }),
diceRolling: new fields.SchemaField({ diceRolling: new fields.SchemaField({
multiplier: new fields.StringField({ multiplier: new fields.StringField({
choices: SYSTEM.GENERAL.diceSetNumbers, choices: SYSTEM.GENERAL.diceSetNumbers,
@ -62,7 +63,7 @@ export class DHActionDiceData extends foundry.abstract.DataModel {
label: 'Multiplier' label: 'Multiplier'
}), }),
flatMultiplier: new fields.NumberField({ nullable: true, initial: 1, label: 'Flat Multiplier' }), flatMultiplier: new fields.NumberField({ nullable: true, initial: 1, label: 'Flat Multiplier' }),
dice: new fields.StringField({ choices: SYSTEM.GENERAL.diceTypes, initial: 'd6', label: 'Formula' }), dice: new fields.StringField({ choices: SYSTEM.GENERAL.diceTypes, initial: 'd6', label: 'Dice' }),
bonus: new fields.NumberField({ nullable: true, initial: null, label: 'Bonus' }), bonus: new fields.NumberField({ nullable: true, initial: null, label: 'Bonus' }),
custom: new fields.SchemaField({ custom: new fields.SchemaField({
enabled: new fields.BooleanField({ label: 'Custom Formula' }), enabled: new fields.BooleanField({ label: 'Custom Formula' }),

View file

@ -1,4 +1,4 @@
import { DHBaseAction } from "../action/action.mjs"; import { DHBaseAction } from '../action/action.mjs';
const fields = foundry.data.fields; const fields = foundry.data.fields;
@ -42,6 +42,9 @@ export default class DHAdversaryRoll extends foundry.abstract.TypeDataModel {
prepareDerivedData() { prepareDerivedData() {
this.hasHitTarget = this.targets.filter(t => t.hit === true).length > 0; this.hasHitTarget = this.targets.filter(t => t.hit === true).length > 0;
this.currentTargets = this.targetSelection !== true ? Array.from(game.user.targets).map(t => DHBaseAction.formatTarget(t)) : this.targets; this.currentTargets =
this.targetSelection !== true
? Array.from(game.user.targets).map(t => DHBaseAction.formatTarget(t))
: this.targets;
} }
} }

View file

@ -3,7 +3,7 @@ export default class DHDamageRoll extends foundry.abstract.TypeDataModel {
const fields = foundry.data.fields; const fields = foundry.data.fields;
return { return {
messageType: new fields.StringField({initial: 'damage'}), messageType: new fields.StringField({ initial: 'damage' }),
title: new fields.StringField(), title: new fields.StringField(),
roll: new fields.DataField({}), roll: new fields.DataField({}),
targets: new fields.ArrayField( targets: new fields.ArrayField(
@ -28,7 +28,7 @@ export default class DHDamageRoll extends foundry.abstract.TypeDataModel {
action: new fields.StringField(), action: new fields.StringField(),
message: new fields.StringField() message: new fields.StringField()
}), }),
directDamage: new fields.BooleanField({initial: true}) directDamage: new fields.BooleanField({ initial: true })
}; };
} }
@ -38,6 +38,9 @@ export default class DHDamageRoll extends foundry.abstract.TypeDataModel {
prepareDerivedData() { prepareDerivedData() {
this.hasHitTarget = this.targets.filter(t => t.hit === true).length > 0; this.hasHitTarget = this.targets.filter(t => t.hit === true).length > 0;
this.currentTargets = this.targetSelection !== true ? Array.from(game.user.targets).map(t => DHBaseAction.formatTarget(t)) : this.targets; this.currentTargets =
this.targetSelection !== true
? Array.from(game.user.targets).map(t => DHBaseAction.formatTarget(t))
: this.targets;
} }
} }

View file

@ -1,4 +1,4 @@
import DHAdversaryRoll from "./adversaryRoll.mjs"; import DHAdversaryRoll from './adversaryRoll.mjs';
export default class DHDualityRoll extends DHAdversaryRoll { export default class DHDualityRoll extends DHAdversaryRoll {
get messageTemplate() { get messageTemplate() {

View file

@ -36,7 +36,8 @@ class DhCountdownData extends foundry.abstract.DataModel {
}) })
}) })
) )
}) }),
window: new fields.SchemaField({})
}; };
} }

View file

@ -1,9 +1,9 @@
import { actionsTypes } from '../action/_module.mjs'; // import { actionsTypes } from '../action/_module.mjs';
// Temporary Solution // Temporary Solution
export default class ActionField extends foundry.data.fields.ObjectField { export default class ActionField extends foundry.data.fields.ObjectField {
getModel(value) { getModel(value) {
return actionsTypes[value.type] ?? actionsTypes.attack; return game.system.api.models.actionsTypes[value.type] ?? game.system.api.models.actionsTypes.attack;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */

View file

@ -1,4 +1,4 @@
import { actionsTypes } from '../action/_module.mjs'; // import { actionsTypes } from '../action/_module.mjs';
/** /**
* Describes metadata about the item data model type * Describes metadata about the item data model type
@ -60,7 +60,8 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
const actionType = { const actionType = {
weapon: 'attack' weapon: 'attack'
}[this.constructor.metadata.type], }[this.constructor.metadata.type],
cls = actionsTypes.attack, cls = game.system.api.models.actionsTypes[actionType],
// cls = actionsTypes.attack,
action = new cls( action = new cls(
{ {
_id: foundry.utils.randomID(), _id: foundry.utils.randomID(),

View file

@ -9,7 +9,8 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
this.config.experiences = []; this.config.experiences = [];
if (config.source?.action) { if (config.source?.action) {
this.item = config.data.parent.items.get(config.source.item); console.log(config);
this.item = config.data.parent.items.get(config.source.item) ?? config.data.parent;
this.action = this.action =
config.data.attack?._id == config.source.action config.data.attack?._id == config.source.action
? config.data.attack ? config.data.attack
@ -50,15 +51,18 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
}; };
async _prepareContext(_options) { async _prepareContext(_options) {
console.log(this.config, this.roll);
const context = await super._prepareContext(_options); const context = await super._prepareContext(_options);
context.hasRoll = !!this.config.roll; context.hasRoll = !!this.config.roll;
context.roll = this.roll;
context.rollType = this.roll?.constructor.name;
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]
})); }));
context.selectedExperiences = this.config.experiences; context.selectedExperiences = this.config.experiences;
context.advantage = this.config.advantage; context.advantage = this.config.roll?.advantage;
/* context.diceOptions = this.diceOptions; */ context.diceOptions = SYSTEM.GENERAL.diceTypes;
context.canRoll = true; context.canRoll = true;
context.isLite = this.config.roll?.lite; context.isLite = this.config.roll?.lite;
if (this.config.costs?.length) { if (this.config.costs?.length) {
@ -71,7 +75,9 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
context.uses = this.action.calcUses(this.config.uses); context.uses = this.action.calcUses(this.config.uses);
context.canRoll = context.canRoll && this.action.hasUses(context.uses); context.canRoll = context.canRoll && this.action.hasUses(context.uses);
} }
context.extraFormula = this.config.extraFormula;
context.formula = this.roll.constructFormula(this.config); context.formula = this.roll.constructFormula(this.config);
return context; return context;
} }
@ -81,12 +87,18 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
this.config.costs = foundry.utils.mergeObject(this.config.costs, rest.costs); this.config.costs = foundry.utils.mergeObject(this.config.costs, rest.costs);
} }
if (this.config.uses) this.config.uses = foundry.utils.mergeObject(this.config.uses, rest.uses); if (this.config.uses) this.config.uses = foundry.utils.mergeObject(this.config.uses, rest.uses);
if (rest.roll?.dice) {
Object.entries(rest.roll.dice).forEach(([key, value]) => {
this.roll[key] = value;
});
}
this.config.extraFormula = rest.extraFormula;
this.render(); this.render();
} }
static updateIsAdvantage(_, button) { static updateIsAdvantage(_, button) {
const advantage = Number(button.dataset.advantage); const advantage = Number(button.dataset.advantage);
this.config.advantage = this.config.advantage === advantage ? 0 : advantage; this.config.roll.advantage = this.config.roll.advantage === advantage ? 0 : advantage;
this.render(); this.render();
} }

View file

@ -37,10 +37,17 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
async _prepareContext(_options) { async _prepareContext(_options) {
const context = await super._prepareContext(_options); const context = await super._prepareContext(_options);
context.title = this.config.title; context.title = this.config.title;
context.formula = this.config.roll.formula; context.extraFormula = this.config.extraFormula;
context.formula = this.roll.constructFormula(this.config);
return context; return context;
} }
static updateRollConfiguration(event, _, formData) {
const { ...rest } = foundry.utils.expandObject(formData.object);
this.config.extraFormula = rest.extraFormula;
this.render();
}
static async submitRoll() { static async submitRoll() {
await this.close({ submitted: true }); await this.close({ submitted: true });
} }

View file

@ -485,7 +485,9 @@ export default class DhpActor extends Actor {
resources.forEach(r => { resources.forEach(r => {
switch (r.type) { switch (r.type) {
case 'fear': case 'fear':
ui.resources.updateFear(game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear) + r.value); ui.resources.updateFear(
game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.Fear) + r.value
);
break; break;
case 'armorStack': case 'armorStack':
updates.armor.resources['system.marks.value'] = Math.max( updates.armor.resources['system.marks.value'] = Math.max(

View file

@ -259,6 +259,28 @@ export const damageKeyToNumber = key => {
} }
}; };
export default function constructHTMLButton({
label,
dataset = {},
classes = [],
icon = '',
type = 'button',
disabled = false
}) {
const button = document.createElement('button');
button.type = type;
for (const [key, value] of Object.entries(dataset)) {
button.dataset[key] = value;
}
button.classList.add(...classes);
if (icon) icon = `<i class="${icon}"></i> `;
if (disabled) button.disabled = true;
button.innerHTML = `${icon}${label}`;
return button;
}
export const adjustDice = (dice, decrease) => { export const adjustDice = (dice, decrease) => {
const diceKeys = Object.keys(diceTypes); const diceKeys = Object.keys(diceTypes);
const index = diceKeys.indexOf(dice); const index = diceKeys.indexOf(dice);

View file

@ -37,7 +37,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
element.addEventListener('click', this.clickTarget); element.addEventListener('click', this.clickTarget);
}); });
html.querySelectorAll('.button-target-selection').forEach(element => { html.querySelectorAll('.button-target-selection').forEach(element => {
element.addEventListener('click', event => this.onTargetSelection(event, data.message)) element.addEventListener('click', event => this.onTargetSelection(event, data.message));
}); });
html.querySelectorAll('.damage-button').forEach(element => html.querySelectorAll('.damage-button').forEach(element =>
element.addEventListener('click', event => this.onDamage(event, data.message)) element.addEventListener('click', event => this.onDamage(event, data.message))
@ -122,11 +122,13 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
onRollAllSave = async (event, message) => { onRollAllSave = async (event, message) => {
event.stopPropagation(); event.stopPropagation();
const targets = event.target.parentElement.querySelectorAll('.target-section > [data-token] .target-save-container'); const targets = event.target.parentElement.querySelectorAll(
targets.forEach((el) => { '.target-section > [data-token] .target-save-container'
el.dispatchEvent(new PointerEvent("click", { shiftKey: true})) );
}) targets.forEach(el => {
} el.dispatchEvent(new PointerEvent('click', { shiftKey: true }));
});
};
onApplyEffect = async (event, message) => { onApplyEffect = async (event, message) => {
event.stopPropagation(); event.stopPropagation();
@ -146,18 +148,26 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
event.stopPropagation(); event.stopPropagation();
const targetSelection = Boolean(event.target.dataset.targetHit), const targetSelection = Boolean(event.target.dataset.targetHit),
msg = ui.chat.collection.get(message._id); msg = ui.chat.collection.get(message._id);
if(msg.system.targetSelection === targetSelection) return; if (msg.system.targetSelection === targetSelection) return;
if(targetSelection !== true && !Array.from(game.user.targets).length) return ui.notifications.info(game.i18n.localize('DAGGERHEART.Notification.Info.NoTargetsSelected')); if (targetSelection !== true && !Array.from(game.user.targets).length)
return ui.notifications.info(game.i18n.localize('DAGGERHEART.Notification.Info.NoTargetsSelected'));
msg.system.targetSelection = targetSelection; msg.system.targetSelection = targetSelection;
msg.system.prepareDerivedData(); msg.system.prepareDerivedData();
ui.chat.updateMessage(msg); ui.chat.updateMessage(msg);
} };
getTargetList = (event, message) => { getTargetList = (event, message) => {
const targetSelection = event.target.closest('.message-content').querySelector('.button-target-selection.target-selected'), const targetSelection = event.target
.closest('.message-content')
.querySelector('.button-target-selection.target-selected'),
isHit = Boolean(targetSelection.dataset.targetHit); isHit = Boolean(targetSelection.dataset.targetHit);
return {isHit, targets: isHit ? message.system.targets.filter(t => t.hit === true).map(target => game.canvas.tokens.get(target.id)) : Array.from(game.user.targets)}; return {
} isHit,
targets: isHit
? message.system.targets.filter(t => t.hit === true).map(target => game.canvas.tokens.get(target.id))
: Array.from(game.user.targets)
};
};
hoverTarget = event => { hoverTarget = event => {
event.stopPropagation(); event.stopPropagation();
@ -185,9 +195,11 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
event.stopPropagation(); event.stopPropagation();
const { isHit, targets } = this.getTargetList(event, message); const { isHit, targets } = this.getTargetList(event, message);
if(message.system.onSave && isHit) { if (message.system.onSave && isHit) {
const pendingingSaves = message.system.targets.filter(target => target.hit && target.saved.success === null); const pendingingSaves = message.system.targets.filter(
if(pendingingSaves.length) { target => target.hit && target.saved.success === null
);
if (pendingingSaves.length) {
const confirm = await foundry.applications.api.DialogV2.confirm({ const confirm = await foundry.applications.api.DialogV2.confirm({
window: { title: 'Pending Reaction Rolls found' }, window: { title: 'Pending Reaction Rolls found' },
content: `<p>Some Tokens still need to roll their Reaction Roll.</p><p>Are you sure you want to continue ?</p><p><i>Undone reaction rolls will be considered as failed</i></p>` content: `<p>Some Tokens still need to roll their Reaction Roll.</p><p>Are you sure you want to continue ?</p><p><i>Undone reaction rolls will be considered as failed</i></p>`

View file

@ -15,13 +15,15 @@ fieldset.daggerheart.chat {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 5px; gap: 5px;
&:before, &:after { &:before,
&:after {
content: '\f0d8'; content: '\f0d8';
font-family: "Font Awesome 6 Pro"; font-family: 'Font Awesome 6 Pro';
} }
} }
&.expanded { &.expanded {
legend:before, legend:after { legend:before,
legend:after {
content: '\f0d7'; content: '\f0d7';
} }
} }
@ -229,20 +231,20 @@ fieldset.daggerheart.chat {
.target-selection { .target-selection {
display: flex; display: flex;
justify-content: space-around; justify-content: space-around;
input[type="radio"] { input[type='radio'] {
display: none; display: none;
&:checked + label { &:checked + label {
text-shadow: 0px 0px 4px #CE5937; text-shadow: 0px 0px 4px #ce5937;
} }
&:not(:checked) + label { &:not(:checked) + label {
opacity: .75; opacity: 0.75;
} }
} }
label { label {
cursor: pointer; cursor: pointer;
opacity: .75; opacity: 0.75;
&.target-selected { &.target-selected {
text-shadow: 0px 0px 4px #CE5937; text-shadow: 0px 0px 4px #ce5937;
opacity: 1; opacity: 1;
} }
} }
@ -273,7 +275,8 @@ fieldset.daggerheart.chat {
background: @miss; background: @miss;
} }
img, .target-save-container { img,
.target-save-container {
width: 22px; width: 22px;
height: 22px; height: 22px;
align-self: center; align-self: center;
@ -401,7 +404,7 @@ fieldset.daggerheart.chat {
display: none; display: none;
} }
&::after { &::after {
content: "??"; content: '??';
} }
} }
} }
@ -414,7 +417,8 @@ fieldset.daggerheart.chat {
border-top-width: 0; border-top-width: 0;
display: contents; display: contents;
legend { legend {
&:before, &:after { &:before,
&:after {
display: none; display: none;
} }
} }
@ -493,16 +497,18 @@ fieldset.daggerheart.chat {
align-items: end; align-items: end;
gap: 0.25rem; gap: 0.25rem;
.dice { .dice {
.dice-rolls.duality { .dice-rolls {
margin-bottom: 0; margin-bottom: 0;
li { &.duality {
display: flex; li {
align-items: center; display: flex;
justify-content: center; align-items: center;
position: relative; justify-content: center;
background: unset; position: relative;
line-height: unset; background: unset;
font-weight: unset; line-height: unset;
font-weight: unset;
}
} }
} }
} }

View file

@ -1,6 +1,6 @@
.theme-light { .theme-light {
.daggerheart.dh-style.countdown { .daggerheart.dh-style.countdown {
&.minimized .minimized-view .mini-countdown-container { .minimized-view .mini-countdown-container {
background-image: url('../assets/parchments/dh-parchment-dark.png'); background-image: url('../assets/parchments/dh-parchment-dark.png');
} }
} }
@ -26,51 +26,38 @@
} }
} }
&.minimized { .minimized-view {
height: auto !important; display: flex;
max-height: unset !important; gap: 8px;
max-width: 740px !important; flex-wrap: wrap;
width: auto !important;
.window-content { .mini-countdown-container {
display: flex; width: fit-content;
padding: 4px 8px;
justify-content: center;
}
.minimized-view {
display: flex; display: flex;
align-items: center;
gap: 8px; gap: 8px;
flex-wrap: wrap; border: 2px solid light-dark(@dark-blue, @golden);
border-radius: 6px;
padding: 0 4px 0 0;
background-image: url('../assets/parchments/dh-parchment-light.png');
color: light-dark(@beige, @dark);
cursor: pointer;
.mini-countdown-container { &.disabled {
width: fit-content; cursor: initial;
display: flex; }
align-items: center;
gap: 8px;
border: 2px solid light-dark(@dark-blue, @golden);
border-radius: 6px;
padding: 0 4px 0 0;
background-image: url('../assets/parchments/dh-parchment-light.png');
color: light-dark(@beige, @dark);
cursor: pointer;
&.disabled { img {
cursor: initial; width: 30px;
} height: 30px;
border-radius: 6px 0 0 6px;
}
img { .mini-countdown-name {
width: 30px; white-space: nowrap;
height: 30px; }
border-radius: 6px 0 0 6px;
}
.mini-countdown-name { .mini-countdown-value {
white-space: nowrap;
}
.mini-countdown-value {
}
} }
} }
} }

View file

@ -1414,7 +1414,7 @@ fieldset.daggerheart.chat legend {
fieldset.daggerheart.chat legend:before, fieldset.daggerheart.chat legend:before,
fieldset.daggerheart.chat legend:after { fieldset.daggerheart.chat legend:after {
content: '\f0d8'; content: '\f0d8';
font-family: "Font Awesome 6 Pro"; font-family: 'Font Awesome 6 Pro';
} }
fieldset.daggerheart.chat.expanded legend:before, fieldset.daggerheart.chat.expanded legend:before,
fieldset.daggerheart.chat.expanded legend:after { fieldset.daggerheart.chat.expanded legend:after {
@ -1559,13 +1559,13 @@ fieldset.daggerheart.chat .daggerheart.chat {
display: flex; display: flex;
justify-content: space-around; justify-content: space-around;
} }
.daggerheart.chat.roll .target-selection input[type="radio"] { .daggerheart.chat.roll .target-selection input[type='radio'] {
display: none; display: none;
} }
.daggerheart.chat.roll .target-selection input[type="radio"]:checked + label { .daggerheart.chat.roll .target-selection input[type='radio']:checked + label {
text-shadow: 0px 0px 4px #CE5937; text-shadow: 0px 0px 4px #ce5937;
} }
.daggerheart.chat.roll .target-selection input[type="radio"]:not(:checked) + label { .daggerheart.chat.roll .target-selection input[type='radio']:not(:checked) + label {
opacity: 0.75; opacity: 0.75;
} }
.daggerheart.chat.roll .target-selection label { .daggerheart.chat.roll .target-selection label {
@ -1573,7 +1573,7 @@ fieldset.daggerheart.chat .daggerheart.chat {
opacity: 0.75; opacity: 0.75;
} }
.daggerheart.chat.roll .target-selection label.target-selected { .daggerheart.chat.roll .target-selection label.target-selected {
text-shadow: 0px 0px 4px #CE5937; text-shadow: 0px 0px 4px #ce5937;
opacity: 1; opacity: 1;
} }
.daggerheart.chat.roll .target-section { .daggerheart.chat.roll .target-section {
@ -1700,7 +1700,7 @@ fieldset.daggerheart.chat .daggerheart.chat {
display: none; display: none;
} }
.daggerheart.chat [data-view-perm='false']::after { .daggerheart.chat [data-view-perm='false']::after {
content: "??"; content: '??';
} }
.theme-colorful .chat-message.duality { .theme-colorful .chat-message.duality {
border-color: black; border-color: black;
@ -1786,7 +1786,7 @@ fieldset.daggerheart.chat .daggerheart.chat {
align-items: end; align-items: end;
gap: 0.25rem; gap: 0.25rem;
} }
.theme-colorful .chat-message.duality .message-content .dice-result .dice-tooltip .wrapper .tooltip-part .dice .dice-rolls.duality { .theme-colorful .chat-message.duality .message-content .dice-result .dice-tooltip .wrapper .tooltip-part .dice .dice-rolls {
margin-bottom: 0; margin-bottom: 0;
} }
.theme-colorful .chat-message.duality .message-content .dice-result .dice-tooltip .wrapper .tooltip-part .dice .dice-rolls.duality li { .theme-colorful .chat-message.duality .message-content .dice-result .dice-tooltip .wrapper .tooltip-part .dice .dice-rolls.duality li {
@ -3474,7 +3474,7 @@ div.daggerheart.views.multiclass {
#resources:has(.fear-bar) { #resources:has(.fear-bar) {
min-width: 200px; min-width: 200px;
} }
.theme-light .daggerheart.dh-style.countdown.minimized .minimized-view .mini-countdown-container { .theme-light .daggerheart.dh-style.countdown .minimized-view .mini-countdown-container {
background-image: url('../assets/parchments/dh-parchment-dark.png'); background-image: url('../assets/parchments/dh-parchment-dark.png');
} }
.daggerheart.dh-style.countdown { .daggerheart.dh-style.countdown {
@ -3494,23 +3494,12 @@ div.daggerheart.views.multiclass {
.daggerheart.dh-style.countdown fieldset legend a { .daggerheart.dh-style.countdown fieldset legend a {
text-shadow: none; text-shadow: none;
} }
.daggerheart.dh-style.countdown.minimized { .daggerheart.dh-style.countdown .minimized-view {
height: auto !important;
max-height: unset !important;
max-width: 740px !important;
width: auto !important;
}
.daggerheart.dh-style.countdown.minimized .window-content {
display: flex;
padding: 4px 8px;
justify-content: center;
}
.daggerheart.dh-style.countdown.minimized .minimized-view {
display: flex; display: flex;
gap: 8px; gap: 8px;
flex-wrap: wrap; flex-wrap: wrap;
} }
.daggerheart.dh-style.countdown.minimized .minimized-view .mini-countdown-container { .daggerheart.dh-style.countdown .minimized-view .mini-countdown-container {
width: fit-content; width: fit-content;
display: flex; display: flex;
align-items: center; align-items: center;
@ -3522,15 +3511,15 @@ div.daggerheart.views.multiclass {
color: light-dark(#efe6d8, #222); color: light-dark(#efe6d8, #222);
cursor: pointer; cursor: pointer;
} }
.daggerheart.dh-style.countdown.minimized .minimized-view .mini-countdown-container.disabled { .daggerheart.dh-style.countdown .minimized-view .mini-countdown-container.disabled {
cursor: initial; cursor: initial;
} }
.daggerheart.dh-style.countdown.minimized .minimized-view .mini-countdown-container img { .daggerheart.dh-style.countdown .minimized-view .mini-countdown-container img {
width: 30px; width: 30px;
height: 30px; height: 30px;
border-radius: 6px 0 0 6px; border-radius: 6px 0 0 6px;
} }
.daggerheart.dh-style.countdown.minimized .minimized-view .mini-countdown-container .mini-countdown-name { .daggerheart.dh-style.countdown .minimized-view .mini-countdown-container .mini-countdown-name {
white-space: nowrap; white-space: nowrap;
} }
.daggerheart.dh-style.countdown .hidden { .daggerheart.dh-style.countdown .hidden {
@ -5423,6 +5412,18 @@ div.daggerheart.views.multiclass {
display: flex; display: flex;
gap: 20px; gap: 20px;
} }
.application.dh-style fieldset.flex.wrap {
flex-wrap: wrap;
gap: 10px 20px;
}
.application.dh-style fieldset.flex .inline-child {
flex: 1;
}
.application.dh-style fieldset.flex .checkbox {
display: flex;
align-items: center;
gap: 20px;
}
.application.dh-style fieldset.one-column { .application.dh-style fieldset.one-column {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View file

@ -119,6 +119,18 @@
&.flex { &.flex {
display: flex; display: flex;
gap: 20px; gap: 20px;
&.wrap {
flex-wrap: wrap;
gap: 10px 20px;
}
.inline-child {
flex: 1;
}
.checkbox {
display: flex;
align-items: center;
gap: 20px;
}
} }
&.one-column { &.one-column {

View file

@ -6,7 +6,7 @@
<div class="wrapper"> <div class="wrapper">
<section class="tooltip-part"> <section class="tooltip-part">
<div class="dice"> <div class="dice">
{{#each roll.dice}} {{#each roll.dice as | dice index |}}
<header class="part-header flexrow"> <header class="part-header flexrow">
<span class="part-formula">{{formula}}</span> <span class="part-formula">{{formula}}</span>
<span class="part-total">{{total}}</span> <span class="part-total">{{total}}</span>
@ -17,9 +17,11 @@
<li class="roll die {{../dice}}{{#if discarded}} discarded{{/if}} min">{{result}}</li> <li class="roll die {{../dice}}{{#if discarded}} discarded{{/if}} min">{{result}}</li>
{{/each}} {{/each}}
</ol> </ol>
{{#if (eq index 0)}}
<div class="attack-roll-advantage-container"> <div class="attack-roll-advantage-container">
{{#if (eq ../roll.advantage.type 1)}}{{localize "DAGGERHEART.General.Advantage.Full"}}{{/if}}{{#if (eq ../roll.advantage.type -1)}}{{localize "DAGGERHEART.General.Disadvantage.Full"}}{{/if}} {{#if (eq ../roll.advantage.type 1)}}{{localize "DAGGERHEART.General.Advantage.Full"}}{{/if}}{{#if (eq ../roll.advantage.type -1)}}{{localize "DAGGERHEART.General.Disadvantage.Full"}}{{/if}}
</div> </div>
{{/if}}
</div> </div>
{{/each}} {{/each}}
</div> </div>

View file

@ -82,6 +82,30 @@
</div> </div>
</div> </div>
{{/if}} {{/if}}
{{#each roll.extra as | extra | }}
<div class="dice">
<header class="part-header flexrow">
<span class="part-formula">
<span>1{{extra.dice}}</span>
</span>
<span class="part-total">{{extra.value}}</span>
</header>
<div class="flexrow">
<ol class="dice-rolls">
<li class="roll die {{extra.dice}}">
<div class="dice-container">
<div class="dice-inner-container">
<div class="dice-wrapper">
<img class="dice" src="../icons/svg/{{extra.dice}}-grey.svg"/>
</div>
<div class="dice-value">{{extra.value}}</div>
</div>
</div>
</li>
</ol>
</div>
</div>
{{/each}}
{{#if roll.modifierTotal}}<div class="duality-modifier">{{#if (gt roll.modifierTotal 0)}}+{{/if}}{{roll.modifierTotal}}</div>{{/if}} {{#if roll.modifierTotal}}<div class="duality-modifier">{{#if (gt roll.modifierTotal 0)}}+{{/if}}{{roll.modifierTotal}}</div>{{/if}}
</section> </section>
</div> </div>

View file

@ -1,22 +1,22 @@
<section <section
class='tab {{tabs.attack.cssClass}} {{tabs.attack.id}}' class="tab {{tabs.attack.cssClass}} {{tabs.attack.id}}"
data-tab='{{tabs.attack.id}}' data-tab="{{tabs.attack.id}}"
data-group='{{tabs.attack.group}}' data-group="{{tabs.attack.group}}"
> >
<fieldset class="one-column"> <fieldset class="one-column">
<legend>{{localize 'DAGGERHEART.General.basics'}}</legend> <legend>{{localize "DAGGERHEART.General.basics"}}</legend>
{{formGroup systemFields.attack.fields.img value=document.img label="Image Path" name="system.attack.img"}} {{formGroup systemFields.attack.fields.img value=document.system.attack.img label="Image Path" name="system.attack.img"}}
{{formGroup systemFields.attack.fields.name value=document.system.attack.name label="Attack Name" name="system.attack.name"}} {{formGroup systemFields.attack.fields.name value=document.system.attack.name label="Attack Name" name="system.attack.name"}}
</fieldset> </fieldset>
<fieldset class="flex"> <fieldset class="flex">
<legend>{{localize "DAGGERHEART.Sheets.Adversary.Attack"}}</legend> <legend>{{localize "DAGGERHEART.Sheets.Adversary.Attack"}}</legend>
{{formField systemFields.attack.fields.roll.fields.bonus value=document.system.attack.roll.bonus label="Attack Bonus" name="system.attack.roll.bonus"}} {{formField systemFields.attack.fields.roll.fields.bonus value=document.system.attack.roll.bonus label="Attack Bonus" name="system.attack.roll.bonus"}}
{{formField systemFields.attack.fields.range value=document.system.attack.range label="Range" name=(concat path "range") localize=true}} {{formField systemFields.attack.fields.range value=document.system.attack.range label="Range" name="system.attack.range" localize=true}}
{{#if systemFields.attack.fields.target.fields}} {{#if systemFields.attack.fields.target.fields}}
{{#if (and document.system.target.type (not (eq document.system.target.type 'self')))}} {{ formField systemFields.attack.fields.target.fields.type value=document.system.attack.target.type label="Target" name="system.attack.target.type" localize=true }}
{{ formField systemFields.attack.fields.target.fields.amount value=document.system.target.amount label="Amount" name=(concat path "target.amount") }} {{#if (and document.system.attack.target.type (not (eq document.system.attack.target.type 'self')))}}
{{ formField systemFields.attack.fields.target.fields.amount value=document.system.attack.target.amount label="Amount" name="system.attack.target.amount" }}
{{/if}} {{/if}}
{{ formField systemFields.attack.fields.target.fields.type value=document.system.target.type label="Target" name=(concat path "target.type") localize=true }}
{{/if}} {{/if}}
</fieldset> </fieldset>
{{> 'systems/daggerheart/templates/views/actionTypes/damage.hbs' fields=systemFields.attack.fields.damage.fields.parts.element.fields source=document.system.attack.damage path="system.attack."}} {{> 'systems/daggerheart/templates/views/actionTypes/damage.hbs' fields=systemFields.attack.fields.damage.fields.parts.element.fields source=document.system.attack.damage path="system.attack."}}

View file

@ -1,4 +1,4 @@
<li class="inventory-item" data-item-id="{{item.id}}" data-companion="{{companion}}"> <li class="inventory-item" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}" data-companion="{{companion}}" data-tooltip="{{concat "#item#" item.uuid}}">
<img src="{{item.img}}" class="item-img {{#if isActor}}actor-img{{/if}}" data-action="useItem"/> <img src="{{item.img}}" class="item-img {{#if isActor}}actor-img{{/if}}" data-action="useItem"/>
<div class="item-label"> <div class="item-label">
<div class="item-name">{{item.name}}</div> <div class="item-name">{{item.name}}</div>

View file

@ -0,0 +1,5 @@
<div>
<div>{{name}}</div>
<img src="{{img}}" />
<div>{{{system.description}}}</div>
</div>

View file

@ -0,0 +1,5 @@
<div>
<div>{{name}}</div>
<img src="{{img}}" />
<div>{{{system.description}}}</div>
</div>

View file

@ -0,0 +1,5 @@
<div>
<div>{{name}}</div>
<img src="{{img}}" />
<div>{{{system.description}}}</div>
</div>

View file

@ -33,10 +33,8 @@
</fieldset> </fieldset>
</div> </div>
<div class="tab {{this.tabs.config.cssClass}}" data-group="primary" data-tab="config"> <div class="tab {{this.tabs.config.cssClass}}" data-group="primary" data-tab="config">
{{#unless isNPC}}
{{> 'systems/daggerheart/templates/views/actionTypes/uses.hbs' fields=fields.uses.fields source=source.uses}} {{> 'systems/daggerheart/templates/views/actionTypes/uses.hbs' fields=fields.uses.fields source=source.uses}}
{{> 'systems/daggerheart/templates/views/actionTypes/cost.hbs' fields=fields.cost.element.fields source=source.cost}} {{> 'systems/daggerheart/templates/views/actionTypes/cost.hbs' fields=fields.cost.element.fields source=source.cost}}
{{/unless}}
{{#if fields.target}}{{> 'systems/daggerheart/templates/views/actionTypes/range-target.hbs' fields=(object range=fields.range target=fields.target.fields) source=(object target=source.target range=source.range)}}{{/if}} {{#if fields.target}}{{> 'systems/daggerheart/templates/views/actionTypes/range-target.hbs' fields=(object range=fields.range target=fields.target.fields) source=(object target=source.target range=source.range)}}{{/if}}
</div> </div>
<div class="tab {{this.tabs.effect.cssClass}}" data-group="primary" data-tab="effect"> <div class="tab {{this.tabs.effect.cssClass}}" data-group="primary" data-tab="effect">

View file

@ -1,61 +1,57 @@
<fieldset class="action-category"> <fieldset class="flex wrap">
<legend class="action-category-label" data-action="toggleSection" data-section="effects"> <legend>Damage</legend>
<div>Damage</div> {{#unless (eq path 'system.attack.')}}
</legend> <div class="fas fa-plus icon-button" data-action="addDamage"></div>
<div class="action-category-data open"> {{/unless}}
{{#unless @root.isNPC}} {{#unless @root.isNPC}}
<div class="fas fa-plus icon-button" data-action="addDamage"></div> {{#if @root.hasBaseDamage}}
{{#if @root.hasBaseDamage}} <div>
<div> {{formField @root.fields.damage.fields.includeBase value=@root.source.damage.includeBase label="Include Item Damage" name="damage.includeBase" }}
{{formField @root.fields.damage.fields.includeBase value=@root.source.damage.includeBase label="Include Item Damage" name="damage.includeBase" }} </div>
</div> {{/if}}
{{/if}} {{/unless}}
{{/unless}} {{#each source.parts as |dmg index|}}
{{#each source.parts as |dmg index|}} {{#if @root.isNPC}}
{{#if @root.isNPC}} {{formField ../fields.value.fields.custom.fields.enabled value=dmg.value.custom.enabled name=(concat ../path "damage.parts." index ".value.custom.enabled") classes="checkbox"}}
{{formField ../fields.value.fields.custom.fields.enabled value=dmg.value.custom.enabled name=(concat ../path "damage.parts." index ".value.custom.enabled")}} <input type="hidden" name="{{../path}}damage.parts.{{index}}.value.multiplier" value="{{dmg.value.multiplier}}">
<input type="hidden" name="{{../path}}damage.parts.{{index}}.value.multiplier" value="{{dmg.value.multiplier}}"> {{#if dmg.value.custom.enabled}}
{{#if dmg.value.custom.enabled}} {{formField ../fields.value.fields.custom.fields.formula value=dmg.value.custom.formula name=(concat ../path "damage.parts." index ".value.custom.formula") localize=true}}
{{formField ../fields.value.fields.custom.fields.formula value=dmg.value.custom.formula name=(concat ../path "damage.parts." index ".value.custom.formula") localize=true}}
{{else}}
<div class="multi-display">
{{formField ../fields.value.fields.flatMultiplier value=dmg.value.flatMultiplier name=(concat ../path "damage.parts." index ".value.flatMultiplier") label="Multiplier" }}
{{formField ../fields.value.fields.dice value=dmg.value.dice name=(concat ../path "damage.parts." index ".value.dice")}}
{{formField ../fields.value.fields.bonus value=dmg.value.bonus name=(concat ../path "damage.parts." index ".value.bonus") localize=true}}
</div>
{{/if}}
{{formField ../fields.type value=dmg.type name=(concat ../path "damage.parts." index ".type") localize=true}}
{{else}} {{else}}
{{#with (@root.getRealIndex index) as | realIndex |}} {{formField ../fields.value.fields.flatMultiplier value=dmg.value.flatMultiplier name=(concat ../path "damage.parts." index ".value.flatMultiplier") label="Multiplier" classes="inline-child" }}
<fieldset{{#if dmg.base}} disabled{{/if}}> {{formField ../fields.value.fields.dice value=dmg.value.dice name=(concat ../path "damage.parts." index ".value.dice") classes="inline-child"}}
{{#if (and (not @root.isNPC) @root.hasRoll (not dmg.base))}} {{formField ../fields.value.fields.bonus value=dmg.value.bonus name=(concat ../path "damage.parts." index ".value.bonus") localize=true classes="inline-child"}}
{{formField ../../fields.resultBased value=dmg.resultBased name=(concat "damage.parts." realIndex ".resultBased") localize=true}}
{{/if}}
{{#if (and (not @root.isNPC) @root.hasRoll (not dmg.base) dmg.resultBased)}}
<fieldset>
<legend>
<div>With Hope</div>
</legend>
{{> formula fields=../../fields.value.fields type=../../fields.type dmg=dmg source=dmg.value target="value" realIndex=realIndex}}
</fieldset>
<fieldset>
<legend>
<div>With Fear</div>
</legend>
{{> formula fields=../../fields.valueAlt.fields type=../../fields.type dmg=dmg source=dmg.valueAlt target="valueAlt" realIndex=realIndex}}
</fieldset>
{{else}}
{{> formula fields=../../fields.value.fields type=../fields.type dmg=dmg source=dmg.value target="value" realIndex=realIndex}}
{{/if}}
{{formField ../../fields.type value=dmg.type name=(concat "damage.parts." realIndex ".type") localize=true}}
<input type="hidden" name="damage.parts.{{realIndex}}.base" value="{{dmg.base}}">
{{#unless dmg.base}}<div class="fas fa-trash" data-action="removeDamage" data-index="{{realIndex}}"></div>{{/unless}}
</fieldset>
{{/with}}
{{/if}} {{/if}}
{{/each}} {{formField ../fields.type value=dmg.type name=(concat ../path "damage.parts." index ".type") localize=true}}
</div> {{else}}
{{#with (@root.getRealIndex index) as | realIndex |}}
<fieldset{{#if dmg.base}} disabled{{/if}}>
{{#if (and (not @root.isNPC) @root.hasRoll (not dmg.base))}}
{{formField ../../fields.resultBased value=dmg.resultBased name=(concat "damage.parts." realIndex ".resultBased") localize=true}}
{{/if}}
{{#if (and (not @root.isNPC) @root.hasRoll (not dmg.base) dmg.resultBased)}}
<fieldset>
<legend>
<div>With Hope</div>
</legend>
{{> formula fields=../../fields.value.fields type=../../fields.type dmg=dmg source=dmg.value target="value" realIndex=realIndex}}
</fieldset>
<fieldset>
<legend>
<div>With Fear</div>
</legend>
{{> formula fields=../../fields.valueAlt.fields type=../../fields.type dmg=dmg source=dmg.valueAlt target="valueAlt" realIndex=realIndex}}
</fieldset>
{{else}}
{{> formula fields=../../fields.value.fields type=../fields.type dmg=dmg source=dmg.value target="value" realIndex=realIndex}}
{{/if}}
{{formField ../../fields.type value=dmg.type name=(concat "damage.parts." realIndex ".type") localize=true}}
<input type="hidden" name="damage.parts.{{realIndex}}.base" value="{{dmg.base}}">
{{#unless dmg.base}}<div class="fas fa-trash" data-action="removeDamage" data-index="{{realIndex}}"></div>{{/unless}}
</fieldset>
{{/with}}
{{/if}}
{{/each}}
</fieldset> </fieldset>
{{#*inline "formula"}} {{#*inline "formula"}}

View file

@ -24,5 +24,8 @@
</div> </div>
{{/if}} {{/if}}
{{/if}} {{/if}}
{{#unless (eq source.type "diceSet")}}
{{formField fields.advState label= "Advantage State" name="roll.advState" value=source.advState localize=true}}
{{/unless}}
</div> </div>
</fieldset> </fieldset>

View file

@ -1,42 +1,45 @@
<div> <div>
<div class="expanded-view {{#if minimized}}hidden{{/if}}"> {{#if simple}}
<div class="countdowns-menu"> <div class="minimized-view">
{{#if canCreate}}<button class="flex" data-action="addCountdown">{{localize "DAGGERHEART.Countdown.AddCountdown"}}</button>{{/if}}
{{#if isGM}}<button data-action="openOwnership" data-tooltip="{{localize "DAGGERHEART.Countdown.OpenOwnership"}}"><i class="fa-solid fa-users"></i></button>{{/if}}
</div>
<div class="countdowns-container">
{{#each source.countdowns}} {{#each source.countdowns}}
<fieldset class="countdown-fieldset"> <a class="mini-countdown-container {{#if (not this.canEdit)}}disabled{{/if}}" data-countdown="{{@key}}">
<legend> <img src="{{this.img}}" />
{{this.name}} <div class="mini-countdown-name">{{this.name}}</div>
{{#if this.canEdit}}<a><i class="fa-solid fa-trash icon-button" data-action="removeCountdown" data-countdown="{{@key}}"></i></a>{{/if}} <div class="mini-countdown-value">{{this.progress.current}}/{{this.progress.max}}</div>
{{#if @root.isGM}}<a><i class="fa-solid fa-users icon-button" data-action="openCountdownOwnership" data-countdown="{{@key}}" data-tooltip="{{localize "DAGGERHEART.Countdown.OpenOwnership"}}"></i></a>{{/if}} </a>
</legend>
<div class="countdown-container">
<img src="{{this.img}}" {{#if this.canEdit}}data-action='editImage'{{else}}class="disabled"{{/if}} data-countdown="{{@key}}" />
<div class="countdown-inner-container">
{{formGroup @root.countdownFields.name name=(concat @root.base ".countdowns." @key ".name") value=this.name localize=true disabled=(not this.canEdit)}}
<div class="countdown-value-container">
{{formGroup @root.countdownFields.progress.fields.current name=(concat @root.base ".countdowns." @key ".progress.current") value=this.progress.current localize=true disabled=(not this.canEdit)}}
{{formGroup @root.countdownFields.progress.fields.max name=(concat @root.base ".countdowns." @key ".progress.max") value=this.progress.max localize=true disabled=(not this.canEdit)}}
</div>
{{formGroup @root.countdownFields.progress.fields.type.fields.value name=(concat @root.base ".countdowns." @key ".progress.type.value") value=this.progress.type.value localize=true localize=true disabled=(not this.canEdit)}}
</div>
</div>
</fieldset>
{{/each}} {{/each}}
</div> </div>
</div> {{else}}
<div class="minimized-view {{#if (not minimized)}}hidden{{/if}}"> <div class="expanded-view">
{{#each source.countdowns}} <div class="countdowns-menu">
<a class="mini-countdown-container {{#if (not this.canEdit)}}disabled{{/if}}" data-countdown="{{@key}}"> {{#if canCreate}}<button class="flex" data-action="addCountdown">{{localize "DAGGERHEART.Countdown.AddCountdown"}}</button>{{/if}}
<img src="{{this.img}}" /> {{#if isGM}}<button data-action="openOwnership" data-tooltip="{{localize "DAGGERHEART.Countdown.OpenOwnership"}}"><i class="fa-solid fa-users"></i></button>{{/if}}
<div class="mini-countdown-name">{{this.name}}</div> </div>
<div class="mini-countdown-value">{{this.progress.current}}/{{this.progress.max}}</div>
</a> <div class="countdowns-container">
{{/each}} {{#each source.countdowns}}
</div> <fieldset class="countdown-fieldset">
<legend>
{{this.name}}
{{#if this.canEdit}}<a><i class="fa-solid fa-trash icon-button" data-action="removeCountdown" data-countdown="{{@key}}"></i></a>{{/if}}
{{#if @root.isGM}}<a><i class="fa-solid fa-users icon-button" data-action="openCountdownOwnership" data-countdown="{{@key}}" data-tooltip="{{localize "DAGGERHEART.Countdown.OpenOwnership"}}"></i></a>{{/if}}
</legend>
<div class="countdown-container">
<img src="{{this.img}}" {{#if this.canEdit}}data-action='editImage'{{else}}class="disabled"{{/if}} data-countdown="{{@key}}" />
<div class="countdown-inner-container">
{{formGroup @root.countdownFields.name name=(concat @root.base ".countdowns." @key ".name") value=this.name localize=true disabled=(not this.canEdit)}}
<div class="countdown-value-container">
{{formGroup @root.countdownFields.progress.fields.current name=(concat @root.base ".countdowns." @key ".progress.current") value=this.progress.current localize=true disabled=(not this.canEdit)}}
{{formGroup @root.countdownFields.progress.fields.max name=(concat @root.base ".countdowns." @key ".progress.max") value=this.progress.max localize=true disabled=(not this.canEdit)}}
</div>
{{formGroup @root.countdownFields.progress.fields.type.fields.value name=(concat @root.base ".countdowns." @key ".progress.type.value") value=this.progress.type.value localize=true localize=true disabled=(not this.canEdit)}}
</div>
</div>
</fieldset>
{{/each}}
</div>
</div>
{{/if}}
</div> </div>

View file

@ -2,7 +2,11 @@
<div class="form-group"> <div class="form-group">
<label><strong>{{title}}</strong></label> <label><strong>{{title}}</strong></label>
<div class="form-fields"> <div class="form-fields">
<input type="text" value="{{formula}}" disabled /> {{!-- <input type="text" value="{{formula}}" disabled /> --}}
<div>{{@root.formula}}</div>
</div>
<div>
<input type="text" value="{{extraFormula}}" name="extraFormula" placeholder="Situational Bonus">
</div> </div>
</div> </div>
{{!-- {{#each bonusDamage as |damage index|}} {{!-- {{#each bonusDamage as |damage index|}}

View file

@ -2,25 +2,58 @@
{{#if @root.hasRoll}} {{#if @root.hasRoll}}
<div class="roll-dialog-container"> <div class="roll-dialog-container">
<div class="flexcol"> <div class="flexcol">
{{#unless @root.isLite}}
<div class="roll-dialog-experience-container">
{{#each experiences}}
{{#if name}}
<div class="roll-dialog-chip {{#if (includes ../selectedExperiences id)}}selected{{/if}}" data-action="selectExperience" data-key="{{id}}">
<span>{{name}}</span>
<span>+{{value}}</span>
</div>
{{/if}}
{{/each}}
</div>
<div class="flexrow">
<button class="disadvantage flex1 {{#if (eq advantage 1)}}selected{{/if}}" data-action="updateIsAdvantage" data-advantage="1">{{localize "DAGGERHEART.General.Advantage.Full"}}</button>
<button class="disadvantage flex1 {{#if (eq advantage -1)}}selected{{/if}}" data-action="updateIsAdvantage" data-advantage="-1">{{localize "DAGGERHEART.General.Disadvantage.Full"}}</button>
</div>
{{/unless}}
<div> <div>
<input type="text" value="{{@root.formula}}" disabled> {{!-- <input type="text" value="{{@root.formula}}" disabled> --}}
<div>{{@root.formula}}</div>
</div> </div>
{{#unless @root.isLite}}
<div class="roll-dialog-experience-container">
{{#each experiences}}
{{#if name}}
<div class="roll-dialog-chip {{#if (includes ../selectedExperiences id)}}selected{{/if}}" data-action="selectExperience" data-key="{{id}}">
<span>{{name}}</span>
<span>+{{value}}</span>
</div>
{{/if}}
{{/each}}
</div>
<div class="flexrow">
<button class="disadvantage flex1 {{#if (eq advantage 1)}}selected{{/if}}" data-action="updateIsAdvantage" data-advantage="1">{{localize "DAGGERHEART.General.Advantage.Full"}}</button>
<button class="disadvantage flex1 {{#if (eq advantage -1)}}selected{{/if}}" data-action="updateIsAdvantage" data-advantage="-1">{{localize "DAGGERHEART.General.Disadvantage.Full"}}</button>
</div>
{{#if (eq @root.rollType 'D20Roll')}}
<div class="flexrow">
<select name="roll.dice.d20">
{{selectOptions diceOptions selected=@root.roll.d20.denomination}}
</select>
</div>
{{/if}}
{{#if (eq @root.rollType 'DualityRoll')}}
<div class="flexrow">
<div>Hope Dice</div>
<select name="roll.dice.dHope">
{{selectOptions diceOptions selected=@root.roll.dHope.denomination}}
</select>
</div>
<div class="flexrow">
<div>Fear Dice</div>
<select name="roll.dice.dFear">
{{selectOptions diceOptions selected=@root.roll.dFear.denomination}}
</select>
</div>
{{#if roll.advantage}}
<div class="flexrow">
<div>Adv/Disadv Dice</div>
<select name="roll.dice.advantageFaces">
{{selectOptions diceOptions selected=@root.roll.dAdvantage.denomination}}
</select>
</div>
{{/if}}
{{/if}}
<div>
<input type="text" value="{{extraFormula}}" name="extraFormula" placeholder="Situational Bonus">
</div>
{{/unless}}
{{!-- {{#if (not isNpc)}} --}} {{!-- {{#if (not isNpc)}} --}}
{{!-- <div class="form-group"> {{!-- <div class="form-group">
<label>Hope</label> <label>Hope</label>