Action Roll DiceSet type

This commit is contained in:
Dapoolp 2025-06-29 20:50:38 +02:00
parent b25e1cec78
commit 4ffcd115e7
17 changed files with 256 additions and 134 deletions

View file

@ -1566,6 +1566,9 @@
}, },
"spellcast": { "spellcast": {
"name": "SpellCast" "name": "SpellCast"
},
"diceSet": {
"name": "Dice Set"
} }
} }
} }

View file

@ -11,6 +11,10 @@ export class DHRoll extends Roll {
super(formula, data, options); super(formula, data, options);
} }
static messageType = 'adversaryRoll';
static DefaultDialog = D20RollDialog;
static async build(config = {}, message = {}) { static async build(config = {}, message = {}) {
const roll = await this.buildConfigure(config, message); const roll = await this.buildConfigure(config, message);
if (!roll) return; if (!roll) return;
@ -28,19 +32,19 @@ export class DHRoll extends Roll {
this.applyKeybindings(config); this.applyKeybindings(config);
let roll = new this(config.roll.formula, config.data, config);
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;
config = await DialogClass.configure(config, message); config = await DialogClass.configure(roll, config, message);
if (!config) return; if (!config) return;
roll.terms = Roll.parse(roll._formula);
} }
let roll = new this(config.formula, config.data, config);
for (const hook of config.hooks) { for (const hook of config.hooks) {
if (Hooks.call(`${SYSTEM.id}.post${hook.capitalize()}RollConfiguration`, roll, config, message) === false) if (Hooks.call(`${SYSTEM.id}.post${hook.capitalize()}RollConfiguration`, roll, config, message) === false)
return []; return [];
} }
return roll; return roll;
} }
@ -62,7 +66,20 @@ export class DHRoll extends Roll {
} }
} }
static async postEvaluate(roll, config = {}) {} static async postEvaluate(roll, config = {}) {
if(!config.roll) config.roll = {};
config.roll.total = roll.total;
config.roll.formula = roll.formula;
config.roll.dice = [];
roll.dice.forEach(d => {
config.roll.dice.push({
dice: d.denomination,
total: d.total,
formula: d.formula,
results: d.results
});
});
}
static async toMessage(roll, config) { static async toMessage(roll, config) {
const cls = getDocumentClass('ChatMessage'), const cls = getDocumentClass('ChatMessage'),
@ -79,12 +96,11 @@ export class DHRoll extends Roll {
static applyKeybindings(config) { static applyKeybindings(config) {
config.dialog.configure ??= true; config.dialog.configure ??= true;
} }
}
// DHopeDie constructFormula(config) {
// DFearDie return (this._formula = Roll.replaceFormulaData(this.options.roll.formula, config.data));
// DualityDie }
// D20Die }
export class DualityDie extends foundry.dice.terms.Die { export class DualityDie extends foundry.dice.terms.Die {
constructor({ number = 1, faces = 12, ...args } = {}) { constructor({ number = 1, faces = 12, ...args } = {}) {
@ -95,10 +111,11 @@ 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.createBaseDice();
this.configureModifiers(); // this.configureModifiers();
this._formula = this.resetFormula(); // this._formula = this.resetFormula();
this.constructFormula();
} }
static ADV_MODE = { static ADV_MODE = {
@ -165,7 +182,7 @@ export class D20Roll extends DHRoll {
applyAdvantage() { applyAdvantage() {
this.d20.modifiers.findSplice(m => ['kh', 'kl'].includes(m)); this.d20.modifiers.findSplice(m => ['kh', 'kl'].includes(m));
if (!this.hasAdvantage && !this.hasAdvantage) this.number = 1; if (!this.hasAdvantage && !this.hasDisadvantage) this.number = 1;
else { else {
this.d20.number = 2; this.d20.number = 2;
this.d20.modifiers.push(this.hasAdvantage ? 'kh' : 'kl'); this.d20.modifiers.push(this.hasAdvantage ? 'kh' : 'kl');
@ -175,62 +192,62 @@ export class D20Roll extends DHRoll {
// Trait bonus != Adversary // Trait bonus != Adversary
configureModifiers() { configureModifiers() {
this.applyAdvantage(); this.applyAdvantage();
// this.options.roll.modifiers = [];
this.applyBaseBonus(); this.applyBaseBonus();
this.options.experiences?.forEach(m => { this.options.experiences?.forEach(m => {
if (this.options.data.experiences?.[m]) if (this.options.data.experiences?.[m])
this.options.roll.modifiers.push({ this.options.roll.modifiers.push({
label: this.options.data.experiences[m].description, label: this.options.data.experiences[m].name,
value: this.options.data.experiences[m].total value: this.options.data.experiences[m].total ?? this.options.data.experiences[m].value
}); });
}); });
this.options.roll.modifiers?.forEach(m => { this.options.roll.modifiers?.forEach(m => {
this.terms.push(...this.formatModifier(m.value)); this.terms.push(...this.formatModifier(m.value));
}); });
if (this.options.extraFormula) if (this.options.extraFormula) {
this.terms.push( this.terms.push(
new foundry.dice.terms.OperatorTerm({ operator: '+' }), new foundry.dice.terms.OperatorTerm({ operator: '+' }),
...this.constructor.parse(this.options.extraFormula, this.getRollData()) ...this.constructor.parse(this.options.extraFormula, this.getRollData())
); );
}
// this.resetFormula(); // this.resetFormula();
} }
constructFormula(config) {
this.terms = [];
this.createBaseDice();
this.configureModifiers();
this.resetFormula();
return this._formula;
}
applyBaseBonus() { applyBaseBonus() {
if (this.options.type === 'attack') this.options.roll.modifiers = [{
this.terms.push(...this.formatModifier(this.options.data.attack.roll.bonus)); label : 'Bonus to Hit',
value: Roll.replaceFormulaData('@attackBonus', this.data)
}];
} }
static async postEvaluate(roll, config = {}) { static async postEvaluate(roll, config = {}) {
super.postEvaluate(roll, config);
if (config.targets?.length) { if (config.targets?.length) {
config.targets.forEach(target => { config.targets.forEach(target => {
const difficulty = config.roll.difficulty ?? target.difficulty ?? target.evasion; const difficulty = config.roll.difficulty ?? target.difficulty ?? target.evasion;
target.hit = this.isCritical || roll.total >= difficulty; target.hit = this.isCritical || roll.total >= difficulty;
}); });
} else if (config.roll.difficulty) config.roll.success = roll.isCritical || roll.total >= config.roll.difficulty; } else if (config.roll.difficulty) config.roll.success = roll.isCritical || roll.total >= config.roll.difficulty;
config.roll.total = roll.total;
config.roll.formula = roll.formula;
config.roll.advantage = { config.roll.advantage = {
type: config.advantage, type: config.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 + c.value, 0); config.roll.modifierTotal = config.roll.modifiers.reduce((a, c) => a + Number(c.value), 0);
config.roll.dice = [];
roll.dice.forEach(d => {
config.roll.dice.push({
dice: d.denomination,
total: d.total,
formula: d.formula,
results: d.results
});
});
} }
getRollData() { getRollData() {
return this.options.data(); return this.options.data;
} }
formatModifier(modifier) { formatModifier(modifier) {
@ -332,7 +349,7 @@ export class DualityRoll extends D20Roll {
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)
this.terms.push(new foundry.dice.terms.OperatorTerm({ operator: '+' })); this.terms.push(new foundry.dice.terms.OperatorTerm({ operator: (this.hasDisadvantage ? '-' : '+') }));
if (bardRallyFaces) { if (bardRallyFaces) {
const rallyDie = new foundry.dice.terms.Die({ faces: bardRallyFaces }); const rallyDie = new foundry.dice.terms.Die({ faces: bardRallyFaces });
if (this.hasAdvantage) { if (this.hasAdvantage) {
@ -349,12 +366,10 @@ export class DualityRoll extends D20Roll {
} }
applyBaseBonus() { applyBaseBonus() {
if (!this.options.roll.modifiers) this.options.roll.modifiers = []; this.options.roll.modifiers = [{
if (this.options.roll?.trait) 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: this.options.data.traits[this.options.roll.trait].total
});
} }
static async postEvaluate(roll, config = {}) { static async postEvaluate(roll, config = {}) {
@ -385,19 +400,7 @@ export class DamageRoll extends DHRoll {
static DefaultDialog = DamageDialog; static DefaultDialog = DamageDialog;
static async postEvaluate(roll, config = {}) { static async postEvaluate(roll, config = {}) {
config.roll = { super.postEvaluate(roll, config);
total: roll.total, config.roll.type = config.type;
formula: roll.formula,
type: config.type
};
config.roll.dice = [];
roll.dice.forEach(d => {
config.roll.dice.push({
dice: d.denomination,
total: d.total,
formula: d.formula,
results: d.results
});
});
} }
} }

View file

@ -73,23 +73,15 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
'systems/daggerheart/templates/views/actionType.hbs', 'systems/daggerheart/templates/views/actionType.hbs',
{ types: SYSTEM.ACTIONS.actionTypes } { types: SYSTEM.ACTIONS.actionTypes }
), ),
title = 'Select Action Type', //useless var title = 'Select Action Type'
type = 'form',
data = {}; //useless var return foundry.applications.api.DialogV2.prompt({
//TODO: use DialogV2 window: { title },
return Dialog.prompt({
title,
label: title,
content, content,
type, //this prop is useless ok: {
callback: html => { label: title,
const form = html[0].querySelector('form'), callback: (event, button, dialog) => button.form.elements.type.value
fd = new foundry.applications.ux.FormDataExtended(form); }
foundry.utils.mergeObject(data, fd.object, { inplace: true });
// if (!data.name?.trim()) data.name = game.i18n.localize(SYSTEM.ACTIONS.actionTypes[data.type].name);
return data;
},
rejectClose: false
}); });
} }
@ -100,13 +92,14 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
*/ */
static async #addAction(_event, _button) { static async #addAction(_event, _button) {
const actionType = await DHBaseItemSheet.selectActionType(); const actionType = await DHBaseItemSheet.selectActionType();
if(!actionType) return;
try { try {
const cls = actionsTypes[actionType?.type] ?? actionsTypes.attack, const cls = actionsTypes[actionType] ?? actionsTypes.attack,
action = new cls( action = new cls(
{ {
_id: foundry.utils.randomID(), _id: foundry.utils.randomID(),
type: actionType.type, type: actionType,
name: game.i18n.localize(SYSTEM.ACTIONS.actionTypes[actionType.type].name), name: game.i18n.localize(SYSTEM.ACTIONS.actionTypes[actionType].name),
...cls.getSourceConfig(this.document) ...cls.getSourceConfig(this.document)
}, },
{ {

View file

@ -77,3 +77,31 @@ export const damageOnSave = {
mod: 1 mod: 1
} }
} }
export const diceCompare = {
below: {
id: 'below',
label: 'Below',
operator: '<'
},
belowEqual: {
id: 'belowEqual',
label: 'Below or Equal',
operator: '<='
},
equal: {
id: 'equal',
label: 'Equal',
operator: '='
},
aboveEqual: {
id: 'aboveEqual',
label: 'Above or Equal',
operator: '>='
},
above: {
id: 'above',
label: 'Above',
operator: '>'
}
}

View file

@ -313,11 +313,20 @@ export const diceTypes = {
}; };
export const multiplierTypes = { export const multiplierTypes = {
proficiency: 'Proficiency', prof: 'Proficiency',
spellcast: 'Spellcast', cast: 'Spellcast',
scale: 'Cost Scaling',
result: 'Roll Result',
flat: 'Flat' flat: 'Flat'
}; };
export const diceSetNumbers = {
prof: 'Proficiency',
cast: 'Spellcast',
scale: 'Cost Scaling',
flat: 'Flat'
}
export const getDiceSoNicePresets = () => { export const getDiceSoNicePresets = () => {
const { diceSoNice } = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance); const { diceSoNice } = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance);
@ -437,6 +446,10 @@ export const rollTypes = {
ability: { ability: {
id: 'ability', id: 'ability',
label: 'DAGGERHEART.RollTypes.ability.name' label: 'DAGGERHEART.RollTypes.ability.name'
},
diceSet: {
id: 'diceSet',
label: 'DAGGERHEART.RollTypes.diceSet.name'
} }
}; };

View file

@ -1,5 +1,5 @@
import CostSelectionDialog from '../../applications/costSelectionDialog.mjs'; import CostSelectionDialog from '../../applications/costSelectionDialog.mjs';
import { DHActionDiceData, 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';
@ -69,12 +69,7 @@ export class DHBaseAction extends foundry.abstract.DataModel {
static defineExtraSchema() { static defineExtraSchema() {
const extraFields = { const extraFields = {
damage: new DHDamageField(), damage: new DHDamageField(),
roll: new fields.SchemaField({ roll: new fields.EmbeddedDataField(DHActionRollData),
type: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.GENERAL.rollTypes }),
trait: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.ACTOR.abilities }),
difficulty: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }),
bonus: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 })
}),
save: new fields.SchemaField({ save: new fields.SchemaField({
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: 10, integer: true, min: 0 }), difficulty: new fields.NumberField({ nullable: true, initial: 10, integer: true, min: 0 }),
@ -158,20 +153,24 @@ export class DHBaseAction extends foundry.abstract.DataModel {
return updateSource; return updateSource;
} }
getRollData() { getRollData(data={}) {
const actorData = this.actor.getRollData(false); const actorData = this.actor.getRollData(false);
// Remove when included directly in Actor getRollData // Remove when included directly in Actor getRollData
actorData.prof = actorData.proficiency?.value ?? 1, actorData.prof = actorData.proficiency?.value ?? 1;
actorData.cast = actorData.spellcast?.value ?? 1, actorData.cast = actorData.spellcast?.value ?? 1;
actorData.scale = this.cost.length actorData.result = data.roll?.total ?? 1;
? this.cost.reduce((a, c) => { /* actorData.scale = data.costs?.length
? data.costs.reduce((a, c) => {
a[c.type] = c.value; a[c.type] = c.value;
return a; return a;
}, {}) }, {})
: 1, : 1; */
actorData.roll = {} actorData.scale = data.costs?.length // Right now only return the first scalable cost.
? (data.costs.find(c => c.scalable)?.total ?? 1)
: 1;
actorData.roll = {};
return actorData; return actorData;
} }
@ -335,8 +334,11 @@ export class DHBaseAction extends foundry.abstract.DataModel {
trait: this.roll?.trait, trait: this.roll?.trait,
label: 'Attack', label: 'Attack',
type: this.actionType, type: this.actionType,
difficulty: this.roll?.difficulty difficulty: this.roll?.difficulty,
formula: this.roll.getFormula()
}; };
if(this.roll?.type === 'diceSet') roll.lite = true;
return roll; return roll;
} }
@ -536,9 +538,11 @@ export class DHDamageAction extends DHBaseAction {
let roll = { formula: formula, total: formula }, let roll = { formula: formula, total: formula },
bonusDamage = []; bonusDamage = [];
if(isNaN(formula)) formula = Roll.replaceFormulaData(formula, this.getRollData(data.system ?? data));
const config = { const config = {
title: game.i18n.format('DAGGERHEART.Chat.DamageRoll.Title', { damage: this.name }), title: game.i18n.format('DAGGERHEART.Chat.DamageRoll.Title', { damage: this.name }),
formula, roll: {formula},
targets: (data.system?.targets.filter(t => t.hit) ?? data.targets), targets: (data.system?.targets.filter(t => t.hit) ?? data.targets),
hasSave: this.hasSave, hasSave: this.hasSave,
source: data.system?.source source: data.system?.source
@ -575,7 +579,7 @@ export class DHAttackAction extends DHDamageAction {
getParentDamage() { getParentDamage() {
return { return {
value: { value: {
multiplier: 'proficiency', multiplier: 'prof',
dice: this.item?.system?.damage.value, dice: this.item?.system?.damage.value,
bonus: this.item?.system?.damage.bonus ?? 0 bonus: this.item?.system?.damage.bonus ?? 0
}, },
@ -610,7 +614,7 @@ export class DHHealingAction extends DHBaseAction {
title: game.i18n.format('DAGGERHEART.Chat.HealingRoll.Title', { title: game.i18n.format('DAGGERHEART.Chat.HealingRoll.Title', {
healing: game.i18n.localize(SYSTEM.GENERAL.healingTypes[this.healing.type].label) healing: game.i18n.localize(SYSTEM.GENERAL.healingTypes[this.healing.type].label)
}), }),
formula, roll: {formula},
targets: (data.system?.targets ?? data.targets).filter(t => t.hit), targets: (data.system?.targets ?? data.targets).filter(t => t.hit),
messageType: 'healing', messageType: 'healing',
type: this.healing.type type: this.healing.type

View file

@ -2,13 +2,60 @@ import FormulaField from '../fields/formulaField.mjs';
const fields = foundry.data.fields; const fields = foundry.data.fields;
/* Roll Field */
export class DHActionRollData extends foundry.abstract.DataModel {
/** @override */
static defineSchema() {
return {
type: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.GENERAL.rollTypes }),
trait: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.ACTOR.abilities }),
difficulty: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }),
bonus: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }),
diceRolling: new fields.SchemaField({
multiplier: new fields.StringField({
choices: SYSTEM.GENERAL.diceSetNumbers,
initial: 'prof',
label: 'Dice Number'
}),
flatMultiplier: new fields.NumberField({ nullable: true, initial: 1, label: 'Flat Multiplier' }),
dice: new fields.StringField({ choices: SYSTEM.GENERAL.diceTypes, initial: 'd6', label: 'Dice Type' }),
compare: new fields.StringField({
choices: SYSTEM.ACTIONS.diceCompare,
initial: 'above',
label: 'Should be'
}),
treshold: new fields.NumberField({ initial: 1, integer: true, min: 1, label: 'Treshold' }),
})
};
}
getFormula() {
if(!this.type) return;
let formula = '';
switch (this.type) {
case 'diceSet':
const multiplier = this.diceRolling.multiplier === 'flat' ? this.diceRolling.flatMultiplier : `@${this.diceRolling.multiplier}`;
formula = `${multiplier}${this.diceRolling.dice}cs${SYSTEM.ACTIONS.diceCompare[this.diceRolling.compare].operator}${this.diceRolling.treshold}`;
break;
default:
// formula = `${(!!this.parent?.actor?.system?.attack ? `@attackBonus` : `@traits.${this.trait}.total`)}`;
formula = '';
break;
}
return formula;
}
}
/* Damage & Healing Field */
export class DHActionDiceData extends foundry.abstract.DataModel { export class DHActionDiceData extends foundry.abstract.DataModel {
/** @override */ /** @override */
static defineSchema() { static defineSchema() {
return { return {
multiplier: new fields.StringField({ multiplier: new fields.StringField({
choices: SYSTEM.GENERAL.multiplierTypes, choices: SYSTEM.GENERAL.multiplierTypes,
initial: 'proficiency', initial: 'prof',
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' }),
@ -22,10 +69,15 @@ export class DHActionDiceData extends foundry.abstract.DataModel {
} }
getFormula(actor) { getFormula(actor) {
const multiplier = this.multiplier === 'flat' ? this.flatMultiplier : actor.system[this.multiplier]?.total; /* const multiplier = this.multiplier === 'flat' ? this.flatMultiplier : actor.system[this.multiplier]?.total;
return this.custom.enabled return this.custom.enabled
? this.custom.formula ? this.custom.formula
: `${multiplier ?? 1}${this.dice}${this.bonus ? (this.bonus < 0 ? ` - ${Math.abs(this.bonus)}` : ` + ${this.bonus}`) : ''}`; : `${multiplier ?? 1}${this.dice}${this.bonus ? (this.bonus < 0 ? ` - ${Math.abs(this.bonus)}` : ` + ${this.bonus}`) : ''}`; */
const multiplier = this.multiplier === 'flat' ? this.flatMultiplier : `@${this.multiplier}`,
bonus = this.bonus ? (this.bonus < 0 ? ` - ${Math.abs(this.bonus)}` : ` + ${this.bonus}`) : '';
return this.custom.enabled
? this.custom.formula
: `${multiplier ?? 1}${this.dice}${bonus}`;
} }
} }

View file

@ -77,4 +77,8 @@ export default class DhpAdversary extends BaseDataActor {
}) })
}; };
} }
get attackBonus() {
return this.attack.roll.bonus;
}
} }

View file

@ -62,7 +62,7 @@ export default class DhCharacter extends BaseDataActor {
}), }),
experiences: new fields.TypedObjectField( experiences: new fields.TypedObjectField(
new fields.SchemaField({ new fields.SchemaField({
description: new fields.StringField({}), name: new fields.StringField(),
value: new fields.NumberField({ integer: true, initial: 0 }), value: new fields.NumberField({ integer: true, initial: 0 }),
bonus: new fields.NumberField({ integer: true, initial: 0 }) bonus: new fields.NumberField({ integer: true, initial: 0 })
}) })

View file

@ -26,6 +26,7 @@ export default class DHDualityRoll extends foundry.abstract.TypeDataModel {
}) })
}) })
), ),
costs: new fields.ArrayField(new fields.ObjectField()),
hasDamage: new fields.BooleanField({ initial: false }), hasDamage: new fields.BooleanField({ initial: false }),
hasHealing: new fields.BooleanField({ initial: false }), hasHealing: new fields.BooleanField({ initial: false }),
hasEffect: new fields.BooleanField({ initial: false }), hasEffect: new fields.BooleanField({ initial: false }),

View file

@ -1,9 +1,10 @@
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api; const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
export default class D20RollDialog extends HandlebarsApplicationMixin(ApplicationV2) { export default class D20RollDialog extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(config = {}, options = {}) { constructor(roll, config = {}, options = {}) {
super(options); super(options);
this.roll = roll;
this.config = config; this.config = config;
this.config.experiences = []; this.config.experiences = [];
@ -59,22 +60,26 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
context.advantage = this.config.advantage; context.advantage = this.config.advantage;
/* context.diceOptions = this.diceOptions; */ /* context.diceOptions = this.diceOptions; */
context.canRoll = true; context.canRoll = true;
context.isLite = this.config.roll?.lite;
if (this.config.costs?.length) { if (this.config.costs?.length) {
const updatedCosts = this.action.calcCosts(this.config.costs); const updatedCosts = this.action.calcCosts(this.config.costs);
context.costs = updatedCosts; context.costs = updatedCosts;
context.canRoll = this.action.hasCost(updatedCosts); context.canRoll = this.action.hasCost(updatedCosts);
this.config.data.scale = this.config.costs[0].total;
} }
if (this.config.uses?.max) { if (this.config.uses?.max) {
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);
} }
console.log(context, _options) context.formula = this.roll.constructFormula(this.config);
return context; return context;
} }
static updateRollConfiguration(event, _, formData) { static updateRollConfiguration(event, _, formData) {
const { ...rest } = foundry.utils.expandObject(formData.object); const { ...rest } = foundry.utils.expandObject(formData.object);
if (this.config.costs) this.config.costs = foundry.utils.mergeObject(this.config.costs, rest.costs); if (this.config.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);
this.render(); this.render();
} }
@ -86,11 +91,12 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
} }
static selectExperience(_, button) { static selectExperience(_, button) {
if (this.config.experiences.find(x => x === button.dataset.key)) { /* if (this.config.experiences.find(x => x === button.dataset.key)) {
this.config.experiences = this.config.experiences.filter(x => x !== button.dataset.key); this.config.experiences = this.config.experiences.filter(x => x !== button.dataset.key);
} else { } else {
this.config.experiences = [...this.config.experiences, button.dataset.key]; this.config.experiences = [...this.config.experiences, button.dataset.key];
} } */
this.config.experiences = this.config.experiences.indexOf(button.dataset.key) > -1 ? this.config.experiences.filter(x => x !== button.dataset.key) : [...this.config.experiences, button.dataset.key];
this.render(); this.render();
} }
@ -103,9 +109,9 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
if (!options.submitted) this.config = false; if (!options.submitted) this.config = false;
} }
static async configure(config = {}, options={}) { static async configure(roll, config = {}, options={}) {
return new Promise(resolve => { return new Promise(resolve => {
const app = new this(config, options); const app = new this(roll, config, options);
app.addEventListener('close', () => resolve(app.config), { once: true }); app.addEventListener('close', () => resolve(app.config), { once: true });
app.render({ force: true }); app.render({ force: true });
}); });

View file

@ -1,9 +1,10 @@
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api; const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
export default class DamageDialog extends HandlebarsApplicationMixin(ApplicationV2) { export default class DamageDialog extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(config={}, options={}) { constructor(roll, config={}, options={}) {
super(options); super(options);
this.roll = roll;
this.config = config; this.config = config;
} }
@ -36,7 +37,7 @@ 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.formula; context.formula = this.config.roll.formula;
return context; return context;
} }
@ -49,9 +50,9 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
if ( !options.submitted ) this.config = false; if ( !options.submitted ) this.config = false;
} }
static async configure(config={}) { static async configure(roll, config={}) {
return new Promise(resolve => { return new Promise(resolve => {
const app = new this(config); const app = new this(roll, config);
app.addEventListener("close", () => resolve(app.config), { once: true }); app.addEventListener("close", () => resolve(app.config), { once: true });
app.render({ force: true }); app.render({ force: true });
}); });

View file

@ -276,7 +276,8 @@ export default class DhpActor extends Actor {
async diceRoll(config) { async diceRoll(config) {
config.source = {...(config.source ?? {}), actor: this.uuid}; config.source = {...(config.source ?? {}), actor: this.uuid};
config.data = this.getRollData(); config.data = this.getRollData();
return await this.rollClass.build(config); const rollClass = config.roll.lite ? CONFIG.Dice.daggerheart['DHRoll'] : this.rollClass;
return await rollClass.build(config);
} }
get rollClass() { get rollClass() {

View file

@ -104,20 +104,15 @@ export default class DhpItem extends Item {
'systems/daggerheart/templates/views/actionSelect.hbs', 'systems/daggerheart/templates/views/actionSelect.hbs',
{ actions: this.system.actions } { actions: this.system.actions }
), ),
title = 'Select Action', title = 'Select Action';
type = 'div',
data = {}; return foundry.applications.api.DialogV2.prompt({
return Dialog.prompt({ window: { title },
title,
// label: title,
content, content,
type, ok: {
callback: html => { label: title,
const form = html[0].querySelector('form'), callback: (event, button, dialog) => this.system.actions.find(a => a._id === button.form.elements.actionId.value)
fd = new foundry.applications.ux.FormDataExtended(form); }
return this.system.actions.find(a => a._id === fd.object.actionId);
},
rejectClose: false
}); });
} }

View file

@ -225,11 +225,10 @@ export const getDeleteKeys = (property, innerProperty, innerPropertyDefaultValue
// Fix on Foundry native formula replacement for DH // Fix on Foundry native formula replacement for DH
const nativeReplaceFormulaData = Roll.replaceFormulaData; const nativeReplaceFormulaData = Roll.replaceFormulaData;
Roll.replaceFormulaData = function (formula, data, { missing, warn = false } = {}) { Roll.replaceFormulaData = function (formula, data={}, { missing, warn = false } = {}) {
const terms = [ const terms = Object.keys(SYSTEM.GENERAL.multiplierTypes).map(type => {
{ term: 'prof', default: 1 }, return { term: type, default: 1}
{ term: 'cast', default: 1 } })
];
formula = terms.reduce((a, c) => a.replaceAll(`@${c.term}`, data[c.term] ?? c.default), formula); formula = terms.reduce((a, c) => a.replaceAll(`@${c.term}`, data[c.term] ?? c.default), formula);
return nativeReplaceFormulaData(formula, data, { missing, warn }); return nativeReplaceFormulaData(formula, data, { missing, warn });
}; };

View file

@ -7,8 +7,22 @@
{{formField fields.bonus label="Bonus" name="roll.bonus" value=source.bonus}} {{formField fields.bonus label="Bonus" name="roll.bonus" value=source.bonus}}
{{else}} {{else}}
{{formField fields.type label="Type" name="roll.type" value=source.type localize=true}} {{formField fields.type label="Type" name="roll.type" value=source.type localize=true}}
{{formField fields.trait label="Trait" name="roll.trait" value=source.trait localize=true disabled=(not source.type)}} {{#if (eq source.type "diceSet")}}
{{formField fields.difficulty label="Difficulty" name="roll.difficulty" value=source.difficulty disabled=(not source.type)}} <div class="multi-display">
{{formField fields.diceRolling.fields.multiplier name="roll.diceRolling.multiplier" value=source.diceRolling.multiplier localize=true}}
{{#if (eq source.diceRolling.multiplier 'flat')}}{{formField fields.diceRolling.fields.flatMultiplier value=source.diceRolling.flatMultiplier name="roll.diceRolling.flatMultiplier" }}{{/if}}
{{formField fields.diceRolling.fields.dice name="roll.diceRolling.dice" value=source.diceRolling.dice}}
</div>
<div class="multi-display">
{{formField fields.diceRolling.fields.compare name="roll.diceRolling.compare" value=source.diceRolling.compare localize=true}}
{{formField fields.diceRolling.fields.treshold name="roll.diceRolling.treshold" value=source.diceRolling.treshold localize=true}}
</div>
{{else}}
<div class="multi-display">
{{formField fields.trait label="Trait" name="roll.trait" value=source.trait localize=true disabled=(not source.type)}}
{{formField fields.difficulty label="Difficulty" name="roll.difficulty" value=source.difficulty disabled=(not source.type)}}
</div>
{{/if}}
{{/if}} {{/if}}
</div> </div>
</fieldset> </fieldset>

View file

@ -2,11 +2,12 @@
{{#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"> <div class="roll-dialog-experience-container">
{{#each experiences}} {{#each experiences}}
{{#if description}} {{#if name}}
<div class="roll-dialog-chip {{#if (includes ../selectedExperiences id)}}selected{{/if}}" data-action="selectExperience" data-key="{{id}}"> <div class="roll-dialog-chip {{#if (includes ../selectedExperiences id)}}selected{{/if}}" data-action="selectExperience" data-key="{{id}}">
<span>{{description}}</span> <span>{{name}}</span>
<span>+{{value}}</span> <span>+{{value}}</span>
</div> </div>
{{/if}} {{/if}}
@ -16,6 +17,10 @@
<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.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> <button class="disadvantage flex1 {{#if (eq advantage -1)}}selected{{/if}}" data-action="updateIsAdvantage" data-advantage="-1">{{localize "DAGGERHEART.General.Disadvantage.Full"}}</button>
</div> </div>
{{/unless}}
<div>
<input type="text" value="{{@root.formula}}" disabled>
</div>
{{!-- {{#if (not isNpc)}} --}} {{!-- {{#if (not isNpc)}} --}}
{{!-- <div class="form-group"> {{!-- <div class="form-group">
<label>Hope</label> <label>Hope</label>