Feature/163 actor subdatas (#346)

* Actor Roll bonuses

* Removed console log and comment

---------

Co-authored-by: WBHarry <williambjrklund@gmail.com>
This commit is contained in:
Dapoulp 2025-07-15 15:01:34 +02:00 committed by GitHub
parent 422f28c93c
commit 37c1d7ad88
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 210 additions and 103 deletions

View file

@ -686,11 +686,11 @@
} }
}, },
"RollTypes": { "RollTypes": {
"ability": { "trait": {
"name": "Ability" "name": "Trait"
}, },
"weapon": { "attack": {
"name": "Weapon" "name": "Attack"
}, },
"spellcast": { "spellcast": {
"name": "SpellCast" "name": "SpellCast"

View file

@ -14,8 +14,6 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
} }
addChatListeners = async (app, html, data) => { addChatListeners = async (app, html, data) => {
super.addChatListeners(app, html, data);
html.querySelectorAll('.duality-action-damage').forEach(element => html.querySelectorAll('.duality-action-damage').forEach(element =>
element.addEventListener('click', event => this.onRollDamage(event, data.message)) element.addEventListener('click', event => this.onRollDamage(event, data.message))
); );

View file

@ -388,17 +388,17 @@ export const countdownTypes = {
} }
}; };
export const rollTypes = { export const rollTypes = {
weapon: { attack: {
id: 'weapon', id: 'attack',
label: 'DAGGERHEART.CONFIG.RollTypes.weapon.name' label: 'DAGGERHEART.CONFIG.RollTypes.attack.name'
}, },
spellcast: { spellcast: {
id: 'spellcast', id: 'spellcast',
label: 'DAGGERHEART.CONFIG.RollTypes.spellcast.name' label: 'DAGGERHEART.CONFIG.RollTypes.spellcast.name'
}, },
ability: { trait: {
id: 'ability', id: 'trait',
label: 'DAGGERHEART.CONFIG.RollTypes.ability.name' label: 'DAGGERHEART.CONFIG.RollTypes.trait.name'
}, },
diceSet: { diceSet: {
id: 'diceSet', id: 'diceSet',

View file

@ -5,7 +5,7 @@ export default class DHAttackAction extends DHDamageAction {
static extraSchemas = [...super.extraSchemas, ...['roll', 'save']]; static extraSchemas = [...super.extraSchemas, ...['roll', 'save']];
static getRollType(parent) { static getRollType(parent) {
return parent.type === 'weapon' ? 'weapon' : 'spellcast'; return parent.type === 'weapon' ? 'attack' : 'spellcast';
} }
get chatTemplate() { get chatTemplate() {
@ -21,7 +21,7 @@ export default class DHAttackAction extends DHDamageAction {
} }
if (this.roll.useDefault) { if (this.roll.useDefault) {
this.roll.trait = this.item.system.attack.roll.trait; this.roll.trait = this.item.system.attack.roll.trait;
this.roll.type = 'weapon'; this.roll.type = 'attack';
} }
} }
} }
@ -37,4 +37,8 @@ export default class DHAttackAction extends DHDamageAction {
base: true base: true
}; };
} }
// get modifiers() {
// return [];
// }
} }

View file

@ -150,7 +150,7 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
} }
static getRollType(parent) { static getRollType(parent) {
return 'ability'; return 'trait';
} }
static getSourceConfig(parent) { static getSourceConfig(parent) {
@ -308,7 +308,7 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
prepareRoll() { prepareRoll() {
const roll = { const roll = {
modifiers: [], modifiers: this.modifiers,
trait: this.roll?.trait, trait: this.roll?.trait,
label: 'Attack', label: 'Attack',
type: this.actionType, type: this.actionType,
@ -362,6 +362,13 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
get hasRoll() { get hasRoll() {
return !!this.roll?.type || !!this.roll?.bonus; return !!this.roll?.type || !!this.roll?.bonus;
} }
get modifiers() {
if(!this.actor) return [];
const modifiers = [];
/** Placeholder for specific bonuses **/
return modifiers;
}
/* ROLL */ /* ROLL */
/* SAVE */ /* SAVE */

View file

@ -28,6 +28,7 @@ export default class DHDamageAction extends DHBaseAction {
hasSave: this.hasSave, hasSave: this.hasSave,
isCritical: data.system?.roll?.isCritical ?? false, isCritical: data.system?.roll?.isCritical ?? false,
source: data.system?.source, source: data.system?.source,
data: this.getRollData(),
damageTypes, damageTypes,
event event
}; };
@ -39,4 +40,8 @@ export default class DHDamageAction extends DHBaseAction {
roll = CONFIG.Dice.daggerheart.DamageRoll.build(config); roll = CONFIG.Dice.daggerheart.DamageRoll.build(config);
} }
// get modifiers() {
// return [];
// }
} }

View file

@ -39,4 +39,8 @@ export default class DHHealingAction extends DHBaseAction {
get chatTemplate() { get chatTemplate() {
return 'systems/daggerheart/templates/ui/chat/healing-roll.hbs'; return 'systems/daggerheart/templates/ui/chat/healing-roll.hbs';
} }
get modifiers() {
return [];
}
} }

View file

@ -1,13 +1,7 @@
import DHAdversarySettings from '../../applications/sheets-configs/adversary-settings.mjs'; import DHAdversarySettings from '../../applications/sheets-configs/adversary-settings.mjs';
import ActionField from '../fields/actionField.mjs'; import ActionField from '../fields/actionField.mjs';
import BaseDataActor from './base.mjs'; import BaseDataActor from './base.mjs';
import { resourceField, bonusField } from '../fields/actorField.mjs';
const resourceField = () =>
new foundry.data.fields.SchemaField({
value: new foundry.data.fields.NumberField({ initial: 0, integer: true }),
max: new foundry.data.fields.NumberField({ initial: 0, integer: true }),
isReversed: new foundry.data.fields.BooleanField({ initial: true })
});
export default class DhpAdversary extends BaseDataActor { export default class DhpAdversary extends BaseDataActor {
static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Adversary']; static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Adversary'];
@ -43,8 +37,8 @@ export default class DhpAdversary extends BaseDataActor {
severe: new fields.NumberField({ required: true, initial: 0, integer: true }) severe: new fields.NumberField({ required: true, initial: 0, integer: true })
}), }),
resources: new fields.SchemaField({ resources: new fields.SchemaField({
hitPoints: resourceField(), hitPoints: resourceField(0, true),
stress: resourceField() stress: resourceField(0, true)
}), }),
attack: new ActionField({ attack: new ActionField({
initial: { initial: {
@ -59,7 +53,7 @@ export default class DhpAdversary extends BaseDataActor {
amount: 1 amount: 1
}, },
roll: { roll: {
type: 'weapon' type: 'attack'
}, },
damage: { damage: {
parts: [ parts: [
@ -80,9 +74,14 @@ export default class DhpAdversary extends BaseDataActor {
}) })
), ),
bonuses: new fields.SchemaField({ bonuses: new fields.SchemaField({
difficulty: new fields.SchemaField({ roll: new fields.SchemaField({
all: new fields.NumberField({ integer: true, initial: 0 }), attack: bonusField(),
reaction: new fields.NumberField({ integer: true, initial: 0 }) action: bonusField(),
reaction: bonusField()
}),
damage: new fields.SchemaField({
physical: bonusField(),
magical: bonusField()
}) })
}) })
}; };

View file

@ -2,25 +2,7 @@ import { burden } from '../../config/generalConfig.mjs';
import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs'; import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
import DhLevelData from '../levelData.mjs'; import DhLevelData from '../levelData.mjs';
import BaseDataActor from './base.mjs'; import BaseDataActor from './base.mjs';
import { attributeField, resourceField, stressDamageReductionRule, bonusField } from '../fields/actorField.mjs';
const attributeField = () =>
new foundry.data.fields.SchemaField({
value: new foundry.data.fields.NumberField({ initial: 0, integer: true }),
tierMarked: new foundry.data.fields.BooleanField({ initial: false })
});
const resourceField = (max, reverse = false) =>
new foundry.data.fields.SchemaField({
value: new foundry.data.fields.NumberField({ initial: 0, integer: true }),
max: new foundry.data.fields.NumberField({ initial: max, integer: true }),
isReversed: new foundry.data.fields.BooleanField({ initial: reverse })
});
const stressDamageReductionRule = () =>
new foundry.data.fields.SchemaField({
enabled: new foundry.data.fields.BooleanField({ required: true, initial: false }),
cost: new foundry.data.fields.NumberField({ integer: true })
});
export default class DhCharacter extends BaseDataActor { export default class DhCharacter extends BaseDataActor {
static get metadata() { static get metadata() {
@ -94,22 +76,25 @@ export default class DhCharacter extends BaseDataActor {
levelData: new fields.EmbeddedDataField(DhLevelData), levelData: new fields.EmbeddedDataField(DhLevelData),
bonuses: new fields.SchemaField({ bonuses: new fields.SchemaField({
roll: new fields.SchemaField({ roll: new fields.SchemaField({
attack: new fields.NumberField({ integer: true, initial: 0 }), attack: bonusField(),
primaryWeapon: new fields.SchemaField({ spellcast: bonusField(),
attack: new fields.NumberField({ integer: true, initial: 0 }) trait: bonusField(),
}), action: bonusField(),
spellcast: new fields.NumberField({ integer: true, initial: 0 }), reaction: bonusField(),
action: new fields.NumberField({ integer: true, initial: 0 }), primaryWeapon: bonusField(),
hopeOrFear: new fields.NumberField({ integer: true, initial: 0 }) secondaryWeapon: bonusField()
}), }),
damage: new fields.SchemaField({ damage: new fields.SchemaField({
all: new fields.NumberField({ integer: true, initial: 0 }), physical: bonusField(),
physical: new fields.NumberField({ integer: true, initial: 0 }), magical: bonusField(),
magic: new fields.NumberField({ integer: true, initial: 0 }), primaryWeapon: bonusField(),
primaryWeapon: new fields.SchemaField({ secondaryWeapon: bonusField()
bonus: new fields.NumberField({ integer: true }), }),
extraDice: new fields.NumberField({ integer: true }) healing: bonusField(),
}) range: new fields.SchemaField({
weapon: new fields.NumberField({ integer: true, initial: 0 }),
spell: new fields.NumberField({ integer: true, initial: 0 }),
other: new fields.NumberField({ integer: true, initial: 0 })
}) })
}), }),
companion: new ForeignDocumentUUIDField({ type: 'Actor', nullable: true, initial: null }), companion: new ForeignDocumentUUIDField({ type: 'Actor', nullable: true, initial: null }),
@ -181,6 +166,11 @@ export default class DhCharacter extends BaseDataActor {
return !this.class.value || !this.class.subclass; return !this.class.value || !this.class.subclass;
} }
get spellcastModifier() {
const subClasses = this.parent.items.filter(x => x.type === 'subclass') ?? [];
return Math.max(subClasses?.map(sc => this.traits[sc.system.spellcastingTrait]?.value));
}
get spellcastingModifiers() { get spellcastingModifiers() {
return { return {
main: this.class.subclass?.system?.spellcastingTrait, main: this.class.subclass?.system?.spellcastingTrait,

View file

@ -4,6 +4,7 @@ import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
import ActionField from '../fields/actionField.mjs'; import ActionField from '../fields/actionField.mjs';
import { adjustDice, adjustRange } from '../../helpers/utils.mjs'; import { adjustDice, adjustRange } from '../../helpers/utils.mjs';
import DHCompanionSettings from '../../applications/sheets-configs/companion-settings.mjs'; import DHCompanionSettings from '../../applications/sheets-configs/companion-settings.mjs';
import { resourceField, bonusField } from '../fields/actorField.mjs';
export default class DhCompanion extends BaseDataActor { export default class DhCompanion extends BaseDataActor {
static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Companion']; static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Companion'];
@ -23,11 +24,7 @@ export default class DhCompanion extends BaseDataActor {
...super.defineSchema(), ...super.defineSchema(),
partner: new ForeignDocumentUUIDField({ type: 'Actor' }), partner: new ForeignDocumentUUIDField({ type: 'Actor' }),
resources: new fields.SchemaField({ resources: new fields.SchemaField({
stress: new fields.SchemaField({ stress: resourceField(3, true),
value: new fields.NumberField({ initial: 0, integer: true }),
max: new fields.NumberField({ initial: 3, integer: true }),
isReversed: new foundry.data.fields.BooleanField({ initial: true })
}),
hope: new fields.NumberField({ initial: 0, integer: true }) hope: new fields.NumberField({ initial: 0, integer: true })
}), }),
evasion: new fields.NumberField({ required: true, min: 1, initial: 10, integer: true }), evasion: new fields.NumberField({ required: true, min: 1, initial: 10, integer: true }),
@ -56,7 +53,7 @@ export default class DhCompanion extends BaseDataActor {
amount: 1 amount: 1
}, },
roll: { roll: {
type: 'weapon', type: 'attack',
bonus: 0, bonus: 0,
trait: 'instinct' trait: 'instinct'
}, },
@ -74,7 +71,13 @@ export default class DhCompanion extends BaseDataActor {
} }
}), }),
actions: new fields.ArrayField(new ActionField()), actions: new fields.ArrayField(new ActionField()),
levelData: new fields.EmbeddedDataField(DhLevelData) levelData: new fields.EmbeddedDataField(DhLevelData),
bonuses: new fields.SchemaField({
damage: new fields.SchemaField({
physical: bonusField(),
magical: bonusField()
})
})
}; };
} }
@ -89,7 +92,7 @@ export default class DhCompanion extends BaseDataActor {
} }
prepareBaseData() { prepareBaseData() {
const partnerSpellcastingModifier = this.partner?.system?.spellcastingModifiers?.main; const partnerSpellcastingModifier = this.partner?.system?.spellcastModifier;
const spellcastingModifier = this.partner?.system?.traits?.[partnerSpellcastingModifier]?.value; const spellcastingModifier = this.partner?.system?.traits?.[partnerSpellcastingModifier]?.value;
this.attack.roll.bonus = spellcastingModifier ?? 0; // Needs to expand on which modifier it is that should be used because of multiclassing; this.attack.roll.bonus = spellcastingModifier ?? 0; // Needs to expand on which modifier it is that should be used because of multiclassing;

View file

@ -0,0 +1,28 @@
const fields = foundry.data.fields;
const attributeField = () =>
new fields.SchemaField({
value: new fields.NumberField({ initial: 0, integer: true }),
tierMarked: new fields.BooleanField({ initial: false })
});
const resourceField = (max = 0, reverse = false) =>
new fields.SchemaField({
value: new fields.NumberField({ initial: 0, integer: true }),
max: new fields.NumberField({ initial: max, integer: true }),
isReversed: new fields.BooleanField({ initial: reverse })
});
const stressDamageReductionRule = () =>
new fields.SchemaField({
enabled: new fields.BooleanField({ required: true, initial: false }),
cost: new fields.NumberField({ integer: true })
});
const bonusField = () =>
new fields.SchemaField({
bonus: new fields.NumberField({ integer: true, initial: 0 }),
dice: new fields.ArrayField(new fields.StringField())
})
export { attributeField, resourceField, stressDamageReductionRule, bonusField };

View file

@ -50,7 +50,7 @@ export default class DHWeapon extends AttachableItem {
}, },
roll: { roll: {
trait: 'agility', trait: 'agility',
type: 'weapon' type: 'attack'
}, },
damage: { damage: {
parts: [ parts: [

View file

@ -74,7 +74,6 @@ export default class D20Roll extends DHRoll {
} }
constructFormula(config) { constructFormula(config) {
// this.terms = [];
this.createBaseDice(); this.createBaseDice();
this.configureModifiers(); this.configureModifiers();
this.resetFormula(); this.resetFormula();
@ -91,7 +90,10 @@ export default class D20Roll extends DHRoll {
configureModifiers() { configureModifiers() {
this.applyAdvantage(); this.applyAdvantage();
this.applyBaseBonus();
this.baseTerms = foundry.utils.deepClone(this.terms);
this.options.roll.modifiers = this.applyBaseBonus();
this.options.experiences?.forEach(m => { this.options.experiences?.forEach(m => {
if (this.options.data.experiences?.[m]) if (this.options.data.experiences?.[m])
@ -101,12 +103,7 @@ export default class D20Roll extends DHRoll {
}); });
}); });
this.options.roll.modifiers?.forEach(m => { this.addModifiers();
this.terms.push(...this.formatModifier(m.value));
});
this.baseTerms = foundry.utils.deepClone(this.terms);
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: '+' }),
@ -125,13 +122,18 @@ export default class D20Roll extends DHRoll {
} }
applyBaseBonus() { applyBaseBonus() {
this.options.roll.modifiers = []; const modifiers = [];
if (!this.options.roll.bonus) return;
this.options.roll.modifiers.push({ if(this.options.roll.bonus)
label: 'Bonus to Hit', modifiers.push({
value: this.options.roll.bonus label: 'Bonus to Hit',
// value: Roll.replaceFormulaData('@attackBonus', this.data) value: this.options.roll.bonus
}); });
modifiers.push(...this.getBonus(`roll.${this.options.type}`, `${this.options.type.capitalize()} Bonus`));
modifiers.push(...this.getBonus(`roll.${this.options.roll.type}`, `${this.options.roll.type.capitalize()} Bonus`));
return modifiers;
} }
static async buildEvaluate(roll, config = {}, message = {}) { static async buildEvaluate(roll, config = {}, message = {}) {

View file

@ -24,8 +24,26 @@ export default class DamageRoll extends DHRoll {
} }
} }
applyBaseBonus() {
const modifiers = [],
type = this.options.messageType ?? 'damage';
modifiers.push(...this.getBonus(`${type}`, `${type.capitalize()} Bonus`));
this.options.damageTypes?.forEach(t => {
modifiers.push(...this.getBonus(`${type}.${t}`, `${t.capitalize()} ${type.capitalize()} Bonus`));
});
const weapons = ['primaryWeapon', 'secondaryWeapon'];
weapons.forEach(w => {
if(this.options.source.item && this.options.source.item === this.data[w]?.id)
modifiers.push(...this.getBonus(`${type}.${w}`, 'Weapon Bonus'));
});
return modifiers;
}
constructFormula(config) { constructFormula(config) {
super.constructFormula(config); super.constructFormula(config);
if (config.isCritical) { if (config.isCritical) {
const tmpRoll = new Roll(this._formula)._evaluateSync({ maximize: true }), const tmpRoll = new Roll(this._formula)._evaluateSync({ maximize: true }),
criticalBonus = tmpRoll.total - this.constructor.calculateTotalModifiers(tmpRoll); criticalBonus = tmpRoll.total - this.constructor.calculateTotalModifiers(tmpRoll);

View file

@ -4,6 +4,7 @@ export default class DHRoll extends Roll {
baseTerms = []; baseTerms = [];
constructor(formula, data, options) { constructor(formula, data, options) {
super(formula, data, options); super(formula, data, options);
if(!this.data || !Object.keys(this.data).length) this.data = options.data;
} }
static messageType = 'adversaryRoll'; static messageType = 'adversaryRoll';
@ -99,11 +100,44 @@ export default class DHRoll extends Roll {
} }
formatModifier(modifier) { formatModifier(modifier) {
const numTerm = modifier < 0 ? '-' : '+'; if(Array.isArray(modifier)) {
return [ return [
new foundry.dice.terms.OperatorTerm({ operator: numTerm }), new foundry.dice.terms.OperatorTerm({ operator: '+' }),
new foundry.dice.terms.NumericTerm({ number: Math.abs(modifier) }) ...this.constructor.parse(modifier.join(' + '), this.options.data)
]; ];
} else {
const numTerm = modifier < 0 ? '-' : '+';
return [
new foundry.dice.terms.OperatorTerm({ operator: numTerm }),
new foundry.dice.terms.NumericTerm({ number: Math.abs(modifier) })
];
}
}
applyBaseBonus() {
return [];
}
addModifiers() {
this.options.roll.modifiers?.forEach(m => {
this.terms.push(...this.formatModifier(m.value));
});
}
getBonus(path, label) {
const bonus = foundry.utils.getProperty(this.data.bonuses, path),
modifiers = [];
if(bonus?.bonus)
modifiers.push({
label: label,
value: bonus?.bonus
});
if(bonus?.dice?.length)
modifiers.push({
label: label,
value: bonus?.dice
});
return modifiers;
} }
getFaces(faces) { getFaces(faces) {
@ -113,6 +147,9 @@ export default class DHRoll extends Roll {
constructFormula(config) { constructFormula(config) {
this.terms = Roll.parse(this.options.roll.formula, config.data); this.terms = Roll.parse(this.options.roll.formula, config.data);
this.options.roll.modifiers = this.applyBaseBonus();
this.addModifiers();
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: '+' }),

View file

@ -119,12 +119,21 @@ export default class DualityRoll extends D20Roll {
} }
applyBaseBonus() { applyBaseBonus() {
this.options.roll.modifiers = []; const modifiers = super.applyBaseBonus();
if (!this.options.roll.trait) return;
this.options.roll.modifiers.push({ if(this.options.roll.trait && this.data.traits[this.options.roll.trait])
label: `DAGGERHEART.CONFIG.Traits.${this.options.roll.trait}.name`, modifiers.unshift({
value: Roll.replaceFormulaData(`@traits.${this.options.roll.trait}.value`, this.data) label: `DAGGERHEART.CONFIG.Traits.${this.options.roll.trait}.name`,
value: this.data.traits[this.options.roll.trait].value
});
const weapons = ['primaryWeapon', 'secondaryWeapon'];
weapons.forEach(w => {
if(this.options.source.item && this.options.source.item === this.data[w]?.id)
modifiers.push(...this.getBonus(`roll.${w}`, 'Weapon Bonus'));
}); });
return modifiers;
} }
static postEvaluate(roll, config = {}) { static postEvaluate(roll, config = {}) {

View file

@ -371,7 +371,7 @@ export default class DhpActor extends Actor {
getRollData() { getRollData() {
const rollData = super.getRollData(); const rollData = super.getRollData();
rollData.prof = this.system.proficiency ?? 1; rollData.prof = this.system.proficiency ?? 1;
rollData.cast = this.system.spellcast ?? 1; rollData.cast = this.system.spellcastModifier ?? 1;
return rollData; return rollData;
} }

View file

@ -39,8 +39,9 @@ export default class RegisterHandlebarsHelpers {
} }
static damageSymbols(damageParts) { static damageSymbols(damageParts) {
const symbols = new Set(); const symbols = [...new Set(damageParts.reduce((a, c) => a.concat([...c.type]), []))].map(
damageParts.forEach(part => symbols.add(...CONFIG.DH.GENERAL.damageTypes[part.type].icon)); p => CONFIG.DH.GENERAL.damageTypes[p].icon
);
return new Handlebars.SafeString(Array.from(symbols).map(symbol => `<i class="fa-solid ${symbol}"></i>`)); return new Handlebars.SafeString(Array.from(symbols).map(symbol => `<i class="fa-solid ${symbol}"></i>`));
} }

View file

@ -306,7 +306,7 @@ export const itemAbleRollParse = (value, actor, item) => {
const isItemTarget = value.toLowerCase().startsWith('item.'); const isItemTarget = value.toLowerCase().startsWith('item.');
const slicedValue = isItemTarget ? value.slice(5) : value; const slicedValue = isItemTarget ? value.slice(5) : value;
try { try {
return Roll.safeEval(Roll.replaceFormulaData(slicedValue, isItemTarget ? item : actor)); return Roll.replaceFormulaData(slicedValue, isItemTarget ? item : actor);
} catch (_) { } catch (_) {
return ''; return '';
} }

View file

@ -44,12 +44,14 @@
display: flex; display: flex;
gap: 2px; gap: 2px;
margin-bottom: 4px; margin-bottom: 4px;
flex-wrap: wrap;
.duality-modifier { .duality-modifier {
padding: 2px; padding: 2px;
border-radius: 6px; border-radius: 6px;
border: 1px solid; border: 1px solid;
background: var(--color-dark-6); background: var(--color-dark-6);
font-size: 12px; font-size: 12px;
white-space: nowrap;
} }
} }
.dice-flavor { .dice-flavor {

View file

@ -5,8 +5,8 @@
<ul class="actions-list"> <ul class="actions-list">
{{#each actions}} {{#each actions}}
<li class="action-item"> <li class="action-item">
<input type="radio" name="actionId" value="{{_id}}" {{#if (eq @index 0)}}checked{{/if}}> <input type="radio" id="action-{{_id}}" name="actionId" value="{{_id}}" {{#if (eq @index 0)}}checked{{/if}}>
<span class="label">{{ name }}</span> <label class="label" for="action-{{_id}}">{{ name }}</label>
</li> </li>
{{/each}} {{/each}}
</ul> </ul>