[Feature] 340-341 RollMode & ChatSpeaker (#347)

* Added RollMode to standalone DamageDialog and to RollDialog. ChatMessage now add ChatSpeaker

* Just a little fix for Damage Action

---------

Co-authored-by: Dapoolp <elcatnet@gmail.com>
This commit is contained in:
WBHarry 2025-07-15 17:01:17 +02:00 committed by GitHub
parent da6d9418b7
commit 045754d107
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 112 additions and 42 deletions

View file

@ -1231,6 +1231,7 @@
"quantity": "Quantity", "quantity": "Quantity",
"range": "Range", "range": "Range",
"recovery": "Recovery", "recovery": "Recovery",
"roll": "Roll",
"scalable": "Scalable", "scalable": "Scalable",
"stress": "Stress", "stress": "Stress",
"take": "Take", "take": "Take",

View file

@ -64,6 +64,13 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
context.rollConfig = this.config; context.rollConfig = this.config;
context.hasRoll = !!this.config.roll; context.hasRoll = !!this.config.roll;
context.canRoll = true; context.canRoll = true;
context.selectedRollMode = this.config.selectedRollMode;
context.rollModes = Object.entries(CONFIG.Dice.rollModes).map(([action, { label, icon }]) => ({
action,
label,
icon
}));
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.map(x => ({ context.costs = updatedCosts.map(x => ({
@ -99,6 +106,8 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
static updateRollConfiguration(event, _, formData) { static updateRollConfiguration(event, _, formData) {
const { ...rest } = foundry.utils.expandObject(formData.object); const { ...rest } = foundry.utils.expandObject(formData.object);
this.config.selectedRollMode = rest.selectedRollMode;
if (this.config.costs) { if (this.config.costs) {
this.config.costs = foundry.utils.mergeObject(this.config.costs, rest.costs); this.config.costs = foundry.utils.mergeObject(this.config.costs, rest.costs);
} }
@ -122,11 +131,6 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
} }
static selectExperience(_, button) { static selectExperience(_, button) {
/* if (this.config.experiences.find(x => x === button.dataset.key)) {
this.config.experiences = this.config.experiences.filter(x => x !== button.dataset.key);
} else {
this.config.experiences = [...this.config.experiences, button.dataset.key];
} */
this.config.experiences = this.config.experiences =
this.config.experiences.indexOf(button.dataset.key) > -1 this.config.experiences.indexOf(button.dataset.key) > -1
? this.config.experiences.filter(x => x !== button.dataset.key) ? this.config.experiences.filter(x => x !== button.dataset.key)

View file

@ -48,12 +48,22 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
: game.i18n.localize('DAGGERHEART.EFFECTS.ApplyLocations.damageRoll.name'); : game.i18n.localize('DAGGERHEART.EFFECTS.ApplyLocations.damageRoll.name');
context.extraFormula = this.config.extraFormula; context.extraFormula = this.config.extraFormula;
context.formula = this.roll.constructFormula(this.config); context.formula = this.roll.constructFormula(this.config);
context.directDamage = this.config.directDamage;
context.selectedRollMode = this.config.selectedRollMode;
context.rollModes = Object.entries(CONFIG.Dice.rollModes).map(([action, { label, icon }]) => ({
action,
label,
icon
}));
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);
this.config.extraFormula = rest.extraFormula; this.config.extraFormula = rest.extraFormula;
this.config.selectedRollMode = rest.selectedRollMode;
this.render(); this.render();
} }

View file

@ -88,7 +88,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
onRollDamage = async (event, message) => { onRollDamage = async (event, message) => {
event.stopPropagation(); event.stopPropagation();
const actor = await this.getActor(message.system.source.actor); const actor = await this.getActor(message.system.source.actor);
if (!actor || !game.user.isGM) return true; if (game.user.character?.id !== actor.id && !game.user.isGM) return true;
if (message.system.source.item && message.system.source.action) { if (message.system.source.item && message.system.source.action) {
const action = this.getAction(actor, message.system.source.item, message.system.source.action); const action = this.getAction(actor, message.system.source.item, message.system.source.action);
if (!action || !action?.rollDamage) return; if (!action || !action?.rollDamage) return;

View file

@ -268,7 +268,8 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
hasDamage: !!this.damage?.parts?.length, hasDamage: !!this.damage?.parts?.length,
hasHealing: !!this.healing, hasHealing: !!this.healing,
hasEffect: !!this.effects?.length, hasEffect: !!this.effects?.length,
hasSave: this.hasSave hasSave: this.hasSave,
selectedRollMode: game.settings.get('core', 'rollMode')
}; };
} }

View file

@ -10,6 +10,7 @@ export default class DHDamageAction extends DHBaseAction {
} }
async rollDamage(event, data) { async rollDamage(event, data) {
const systemData = data.system ?? data;
let formula = this.damage.parts.map(p => this.getFormulaValue(p, data).getFormula(this.actor)).join(' + '), let formula = this.damage.parts.map(p => this.getFormulaValue(p, data).getFormula(this.actor)).join(' + '),
damageTypes = [...new Set(this.damage.parts.reduce((a, c) => a.concat([...c.type]), []))]; damageTypes = [...new Set(this.damage.parts.reduce((a, c) => a.concat([...c.type]), []))];
@ -19,15 +20,15 @@ export default 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)); if (isNaN(formula)) formula = Roll.replaceFormulaData(formula, this.getRollData(systemData));
const config = { const config = {
title: game.i18n.format('DAGGERHEART.UI.Chat.damageRoll.title', { damage: this.name }), title: game.i18n.format('DAGGERHEART.UI.Chat.damageRoll.title', { damage: this.name }),
roll: { formula }, roll: { formula },
targets: data.system?.targets.filter(t => t.hit) ?? data.targets, targets: systemData.targets.filter(t => t.hit) ?? data.targets,
hasSave: this.hasSave, hasSave: this.hasSave,
isCritical: data.system?.roll?.isCritical ?? false, isCritical: systemData.roll?.isCritical ?? false,
source: data.system?.source, source: systemData.source,
data: this.getRollData(), data: this.getRollData(),
damageTypes, damageTypes,
event event
@ -36,6 +37,8 @@ export default class DHDamageAction extends DHBaseAction {
if (data.system) { if (data.system) {
config.source.message = data._id; config.source.message = data._id;
config.directDamage = false; config.directDamage = false;
} else {
config.directDamage = true;
} }
roll = CONFIG.Dice.daggerheart.DamageRoll.build(config); roll = CONFIG.Dice.daggerheart.DamageRoll.build(config);

View file

@ -87,7 +87,7 @@ export default class DHRoll extends Roll {
system: config, system: config,
rolls: [roll] rolls: [roll]
}; };
return await cls.create(msg); return await cls.create(msg, { rollMode: config.selectedRollMode });
} }
static applyKeybindings(config) { static applyKeybindings(config) {

View file

@ -37,4 +37,15 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
e.setAttribute('data-use-perm', document.testUserPermission(game.user, 'OWNER')); e.setAttribute('data-use-perm', document.testUserPermission(game.user, 'OWNER'));
}); });
} }
async _preCreate(data, options, user) {
options.speaker = ChatMessage.getSpeaker();
const rollActorOwner = data.rolls?.[0]?.data?.parent?.owner;
if (rollActorOwner) {
data.author = rollActorOwner ? rollActorOwner.id : data.author;
await this.updateSource({ author: rollActorOwner ?? user });
}
return super._preCreate(data, options, rollActorOwner ?? user);
}
} }

View file

@ -1,20 +1,34 @@
@import '../../utils/colors.less'; @import '../../utils/colors.less';
.daggerheart.dialog.dh-style.views.damage-selection { .daggerheart.dialog.dh-style.views.damage-selection {
.damage-section-container { .damage-section-container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 12px; gap: 12px;
input[type='text'], input[type='text'],
input[type='number'] { input[type='number'] {
color: light-dark(@dark, @beige); color: light-dark(@dark, @beige);
outline: 2px solid transparent; outline: 2px solid transparent;
transition: all 0.3s ease; transition: all 0.3s ease;
&:hover { &:hover {
outline: 2px solid light-dark(@dark, @beige); outline: 2px solid light-dark(@dark, @beige);
} }
} }
}
} .damage-section-controls {
display: flex;
align-items: center;
gap: 16px;
.roll-mode-select {
width: min-content;
}
button {
flex: 1;
}
}
}
}

View file

@ -114,5 +114,19 @@
} }
} }
} }
.roll-dialog-controls {
display: flex;
align-items: center;
gap: 16px;
.roll-mode-select {
width: min-content;
}
button {
flex: 1;
}
}
} }
} }

View file

@ -6,8 +6,15 @@
<div class="form-group"> <div class="form-group">
<input type="text" value="{{extraFormula}}" name="extraFormula" placeholder="Situational Bonus"> <input type="text" value="{{extraFormula}}" name="extraFormula" placeholder="Situational Bonus">
</div> </div>
<button class="submit-btn" data-action="submitRoll"> <div class="damage-section-controls">
<i class="fa-solid fa-dice"></i> {{#if directDamage}}
<span class="label">Roll</span> <select class="roll-mode-select" name="selectedRollMode">
</button> {{selectOptions rollModes selected=selectedRollMode valueAttr="action" labelAttr="label" localize=true}}
</select>
{{/if}}
<button class="submit-btn" data-action="submitRoll">
<i class="fa-solid fa-dice"></i>
<span class="label">{{localize "DAGGERHEART.GENERAL.roll"}}</span>
</button>
</div>
</section> </section>

View file

@ -117,10 +117,15 @@
</fieldset> </fieldset>
{{/unless}} {{/unless}}
<span class="formula-label"><b>Formula:</b> {{@root.formula}}</span> <span class="formula-label"><b>Formula:</b> {{@root.formula}}</span>
<button class="sunmit-btn" data-action="submitRoll"{{#unless canRoll}} disabled{{/unless}}> <div class="roll-dialog-controls">
<i class="fa-solid fa-dice"></i> <select class="roll-mode-select" name="selectedRollMode">
<span class="label">Roll</span> {{selectOptions rollModes selected=selectedRollMode valueAttr="action" labelAttr="label" localize=true}}
</button> </select>
<button class="sunmit-btn" data-action="submitRoll"{{#unless canRoll}} disabled{{/unless}}>
<i class="fa-solid fa-dice"></i>
<span class="label">Roll</span>
</button>
</div>
{{else}} {{else}}
<button class="sunmit-btn" data-action="submitRoll"{{#unless canRoll}} disabled{{/unless}}> <button class="sunmit-btn" data-action="submitRoll"{{#unless canRoll}} disabled{{/unless}}>
<span class="label">Continue</span> <span class="label">Continue</span>