Damage Types

This commit is contained in:
Dapoolp 2025-07-11 17:26:11 +02:00
parent 081ebf5bb3
commit 4246e3c9b5
19 changed files with 112 additions and 34 deletions

View file

@ -11,7 +11,8 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
this.actor = actor; this.actor = actor;
this.damage = damage; this.damage = damage;
const canApplyArmor = actor.system.armorApplicableDamageTypes[damageType]; // const canApplyArmor = actor.system.armorApplicableDamageTypes[damageType];
const canApplyArmor = damageType.every(t => actor.system.armorApplicableDamageTypes[t] === true);
const maxArmorMarks = canApplyArmor const maxArmorMarks = canApplyArmor
? Math.min( ? Math.min(
actor.system.armorScore - actor.system.armor.system.marks.value, actor.system.armorScore - actor.system.armor.system.marks.value,

View file

@ -56,10 +56,6 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
id: 'effect', id: 'effect',
template: 'systems/daggerheart/templates/sheets-settings/action-settings/effect.hbs' template: 'systems/daggerheart/templates/sheets-settings/action-settings/effect.hbs'
} }
/* form: {
id: 'action',
template: 'systems/daggerheart/templates/config/action.hbs'
} */
}; };
static TABS = { static TABS = {
@ -148,6 +144,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
_prepareSubmitData(event, formData) { _prepareSubmitData(event, formData) {
const submitData = foundry.utils.expandObject(formData.object); const submitData = foundry.utils.expandObject(formData.object);
console.log(formData, submitData)
for (const keyPath of this.constructor.CLEAN_ARRAYS) { for (const keyPath of this.constructor.CLEAN_ARRAYS) {
const data = foundry.utils.getProperty(submitData, keyPath); const data = foundry.utils.getProperty(submitData, keyPath);
if (data) foundry.utils.setProperty(submitData, keyPath, Object.values(data)); if (data) foundry.utils.setProperty(submitData, keyPath, Object.values(data));
@ -161,7 +158,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
container = foundry.utils.getProperty(this.action.parent, this.action.systemPath); container = foundry.utils.getProperty(this.action.parent, this.action.systemPath);
let newActions; let newActions;
if (Array.isArray(container)) { if (Array.isArray(container)) {
newActions = foundry.utils.getProperty(this.action.parent, this.action.systemPath).map(x => x.toObject()); // Find better way newActions = foundry.utils.getProperty(this.action.parent, this.action.systemPath).map(x => x.toObject());
if (!newActions.findSplice(x => x._id === data._id, data)) newActions.push(data); if (!newActions.findSplice(x => x._id === data._id, data)) newActions.push(data);
} else newActions = data; } else newActions = data;

View file

@ -215,7 +215,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
if (message.system.onSave && message.system.targets.find(t => t.id === target.id)?.saved?.success === true) if (message.system.onSave && message.system.targets.find(t => t.id === target.id)?.saved?.success === true)
damage = Math.ceil(damage * (CONFIG.DH.ACTIONS.damageOnSave[message.system.onSave]?.mod ?? 1)); damage = Math.ceil(damage * (CONFIG.DH.ACTIONS.damageOnSave[message.system.onSave]?.mod ?? 1));
target.actor.takeDamage(damage, message.system.roll.type); target.actor.takeDamage(damage, message.system.damage.damageType);
} }
}; };

View file

@ -59,13 +59,13 @@ export const damageTypes = {
id: 'physical', id: 'physical',
label: 'DAGGERHEART.CONFIG.DamageType.physical.name', label: 'DAGGERHEART.CONFIG.DamageType.physical.name',
abbreviation: 'DAGGERHEART.CONFIG.DamageType.physical.abbreviation', abbreviation: 'DAGGERHEART.CONFIG.DamageType.physical.abbreviation',
icon: ['fa-hand-fist'] icon: 'fa-hand-fist'
}, },
magical: { magical: {
id: 'magical', id: 'magical',
label: 'DAGGERHEART.CONFIG.DamageType.magical.name', label: 'DAGGERHEART.CONFIG.DamageType.magical.name',
abbreviation: 'DAGGERHEART.CONFIG.DamageType.magical.abbreviation', abbreviation: 'DAGGERHEART.CONFIG.DamageType.magical.abbreviation',
icon: ['fa-wand-sparkles'] icon: 'fa-wand-sparkles'
} }
}; };

View file

@ -99,13 +99,24 @@ export class DHDamageData extends foundry.abstract.DataModel {
return { return {
// ...super.defineSchema(), // ...super.defineSchema(),
base: new fields.BooleanField({ initial: false, readonly: true, label: 'Base' }), base: new fields.BooleanField({ initial: false, readonly: true, label: 'Base' }),
type: new fields.StringField({ /* type: new fields.StringField({
choices: CONFIG.DH.GENERAL.damageTypes, choices: CONFIG.DH.GENERAL.damageTypes,
initial: 'physical', initial: 'physical',
label: 'Type', label: 'Type',
nullable: false, nullable: false,
required: true required: true
}), }), */
type: new fields.SetField(
new fields.StringField({
choices: CONFIG.DH.GENERAL.damageTypes,
initial: 'physical',
nullable: false,
required: true
}),
{
label: 'Type'
}
),
resultBased: new fields.BooleanField({ resultBased: new fields.BooleanField({
initial: false, initial: false,
label: 'DAGGERHEART.ACTIONS.Settings.resultBased.label' label: 'DAGGERHEART.ACTIONS.Settings.resultBased.label'

View file

@ -180,7 +180,7 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
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?.total ?? 1;
actorData.cast = actorData.spellcast?.value ?? 1; actorData.cast = actorData.spellcast?.value ?? 1;
actorData.result = data.roll?.total ?? 1; actorData.result = data.roll?.total ?? 1;
/* actorData.scale = data.costs?.length /* actorData.scale = data.costs?.length

View file

@ -10,8 +10,9 @@ export default class DHDamageAction extends DHBaseAction {
} }
async rollDamage(event, data) { async rollDamage(event, 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]), []))];
if (!formula || formula == '') return; if (!formula || formula == '') return;
let roll = { formula: formula, total: formula }, let roll = { formula: formula, total: formula },
bonusDamage = []; bonusDamage = [];
@ -25,6 +26,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,
damageTypes,
event event
}; };
if (this.hasSave) config.onSave = this.save.damageMod; if (this.hasSave) config.onSave = this.save.damageMod;
@ -32,7 +34,7 @@ export default class DHDamageAction extends DHBaseAction {
config.source.message = data._id; config.source.message = data._id;
config.directDamage = false; config.directDamage = false;
} }
roll = CONFIG.Dice.daggerheart.DamageRoll.build(config); roll = CONFIG.Dice.daggerheart.DamageRoll.build(config);
} }
} }

View file

@ -64,6 +64,7 @@ export default class DhpAdversary extends BaseDataActor {
damage: { damage: {
parts: [ parts: [
{ {
type: ['physical'],
value: { value: {
multiplier: 'flat' multiplier: 'flat'
} }

View file

@ -3,7 +3,8 @@ import DHBaseActorSettings from "../../applications/sheets/api/actor-setting.mjs
const resistanceField = () => const resistanceField = () =>
new foundry.data.fields.SchemaField({ new foundry.data.fields.SchemaField({
resistance: new foundry.data.fields.BooleanField({ initial: false }), resistance: new foundry.data.fields.BooleanField({ initial: false }),
immunity: new foundry.data.fields.BooleanField({ initial: false }) immunity: new foundry.data.fields.BooleanField({ initial: false }),
reduction: new foundry.data.fields.NumberField({ integer: true, initial: 0 })
}); });
/** /**
@ -41,8 +42,7 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
if(this.metadata.hasResistances) if(this.metadata.hasResistances)
schema.resistance = new fields.SchemaField({ schema.resistance = new fields.SchemaField({
physical: resistanceField(), physical: resistanceField(),
magical: resistanceField(), magical: resistanceField()
resistance: new fields.NumberField({ integer: true, initial: 0 })
}) })
return schema; return schema;
} }

View file

@ -67,7 +67,7 @@ export default class DhCompanion extends BaseDataActor {
damage: { damage: {
parts: [ parts: [
{ {
multiplier: 'flat', type: ['physical'],
value: { value: {
dice: 'd6', dice: 'd6',
multiplier: 'flat' multiplier: 'flat'

View file

@ -56,6 +56,7 @@ export default class DHWeapon extends BaseDataItem {
damage: { damage: {
parts: [ parts: [
{ {
type: ['physical'],
value: { value: {
multiplier: 'prof', multiplier: 'prof',
dice: 'd8' dice: 'd8'

View file

@ -14,6 +14,10 @@ export default class DamageRoll extends DHRoll {
super.postEvaluate(roll, config); super.postEvaluate(roll, config);
config.roll.type = config.type; config.roll.type = config.type;
config.roll.modifierTotal = this.calculateTotalModifiers(roll); config.roll.modifierTotal = this.calculateTotalModifiers(roll);
}
static async buildPost(roll, config, message) {
await super.buildPost(roll, config, message);
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

@ -56,8 +56,8 @@ export default class DHRoll extends Roll {
// Create Chat Message // Create Chat Message
if (config.source?.message) { if (config.source?.message) {
if(game.modules.get('dice-so-nice')?.active) await game.dice3d.showForRoll(roll, game.user, true);
} else { } else {
const messageData = {};
config.message = await this.toMessage(roll, config); config.message = await this.toMessage(roll, config);
} }
} }

View file

@ -462,7 +462,7 @@ export default class DhpActor extends Actor {
const canUseArmor = const canUseArmor =
this.system.armor && this.system.armor &&
this.system.armor.system.marks.value < this.system.armorScore && this.system.armor.system.marks.value < this.system.armorScore &&
this.system.armorApplicableDamageTypes[type]; type.every(t => this.system.armorApplicableDamageTypes[t] === true);
const canUseStress = Object.keys(this.system.rules.damageReduction.stressDamageReduction).reduce((acc, x) => { const canUseStress = Object.keys(this.system.rules.damageReduction.stressDamageReduction).reduce((acc, x) => {
const rule = this.system.rules.damageReduction.stressDamageReduction[x]; const rule = this.system.rules.damageReduction.stressDamageReduction[x];
if (damageKeyToNumber(x) <= hpDamage) return acc || (rule.enabled && availableStress >= rule.cost); if (damageKeyToNumber(x) <= hpDamage) return acc || (rule.enabled && availableStress >= rule.cost);
@ -480,6 +480,8 @@ export default class DhpActor extends Actor {
return; return;
} }
type = !Array.isArray(type) ? [type] : type;
const hpDamage = this.calculateDamage(baseDamage, type); const hpDamage = this.calculateDamage(baseDamage, type);
if (!hpDamage) return; if (!hpDamage) return;
@ -510,10 +512,13 @@ export default class DhpActor extends Actor {
calculateDamage(baseDamage, type) { calculateDamage(baseDamage, type) {
if (Hooks.call(`${CONFIG.DH.id}.preCalculateDamage`, this, baseDamage, type) === false) return null; if (Hooks.call(`${CONFIG.DH.id}.preCalculateDamage`, this, baseDamage, type) === false) return null;
if(this.system.resistance[type]?.immunity) return 0; /* if(this.system.resistance[type]?.immunity) return 0;
if(this.system.resistance[type]?.resistance) baseDamage = Math.ceil(baseDamage / 2); if(this.system.resistance[type]?.resistance) baseDamage = Math.ceil(baseDamage / 2); */
if(this.canResist(type, 'immunity')) return 0;
if(this.canResist(type, 'resistance')) baseDamage = Math.ceil(baseDamage / 2);
const flatReduction = this.system.resistance[type].reduction; // const flatReduction = this.system.resistance[type].reduction;
const flatReduction = this.getDamageTypeReduction(type);
const damage = Math.max(baseDamage - (flatReduction ?? 0), 0); const damage = Math.max(baseDamage - (flatReduction ?? 0), 0);
const hpDamage = this.convertDamageToThreshold(damage); const hpDamage = this.convertDamageToThreshold(damage);
@ -522,6 +527,17 @@ export default class DhpActor extends Actor {
return hpDamage; return hpDamage;
} }
canResist(type, resistance) {
if(!type) return 0;
return type.every(t => this.system.resistance[t]?.[resistance] === true);
}
getDamageTypeReduction(type) {
if(!type) return 0;
const reduction = Object.entries(this.system.resistance).reduce((a, [index, value]) => type.includes(index) ? Math.min(value.reduction, a) : a, Infinity);
return reduction === Infinity ? 0 : reduction;
}
async takeHealing(resources) { async takeHealing(resources) {
resources.forEach(r => (r.value *= -1)); resources.forEach(r => (r.value *= -1));
await this.modifyResource(resources); await this.modifyResource(resources);

View file

@ -12,7 +12,6 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
if (this.type === 'dualityRoll') { if (this.type === 'dualityRoll') {
html.classList.add('duality'); html.classList.add('duality');
console.log(this.system)
switch (this.system.roll.result.duality) { switch (this.system.roll.result.duality) {
case 1: case 1:
html.classList.add('hope'); html.classList.add('hope');

View file

@ -103,6 +103,40 @@
} }
} }
multi-select {
position: relative;
height: 34px;
.tags {
justify-content: flex-start;
margin: 5px;
height: inherit;
.tag {
box-shadow: 0 0 0 1.1em #E5E5E5 inset;
vertical-align: top;
box-sizing: border-box;
max-width: 100%;
padding: 0.3em 0 0.3em 0.5em;
color: black;
border-radius: 3px;
white-space: nowrap;
transition: .13s ease-out;
height: 22px;
font-size: .9rem;
gap: 0.5em;
z-index: 1;
.remove {
font-size: 10px;
margin-inline: auto 4.6666666667px;
}
}
}
select {
position: absolute;
height: inherit;
outline: initial;
}
}
p { p {
margin: 0; margin: 0;
} }

View file

@ -46,8 +46,12 @@
{{> formula fields=../../fields.value.fields type=../fields.type dmg=dmg source=dmg.value target="value" realIndex=realIndex}} {{> formula fields=../../fields.value.fields type=../fields.type dmg=dmg source=dmg.value target="value" realIndex=realIndex}}
</fieldset> </fieldset>
{{/if}} {{/if}}
{{formField ../../fields.type value=dmg.type name=(concat "damage.parts." realIndex ".type") localize=true}} {{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}}"> <input type="hidden" name="damage.parts.{{realIndex}}.base" value="{{dmg.base}}">
{{!-- <input{{#if dmg.base}} disabled{{/if}} type="text" class="damage-types-input" value="{{dmg.type}}" name={{concat "damage.parts." realIndex ".type"}} /> --}}
{{!-- <multi-select name={{concat "damage.parts." realIndex ".type"}}>
</multi-select> --}}
</fieldset> </fieldset>
{{#unless dmg.base}}<div class="fas fa-trash" data-action="removeDamage" data-index="{{realIndex}}"></div>{{/unless}} {{#unless dmg.base}}<div class="fas fa-trash" data-action="removeDamage" data-index="{{realIndex}}"></div>{{/unless}}
</div> </div>

View file

@ -6,7 +6,7 @@
{{else}} {{else}}
<div class="item-name">{{item.name}}</div> <div class="item-name">{{item.name}}</div>
{{/if}} {{/if}}
{{#if (eq type 'weapon')}} {{#if (eq type 'weapon')}}
<div class="item-tags"> <div class="item-tags">
{{#if isSidebar}} {{#if isSidebar}}
<div class="item-labels"> <div class="item-labels">
@ -16,11 +16,11 @@
<span> - </span> <span> - </span>
{{item.system.attack.damage.parts.0.value.dice}}{{#if item.system.attack.damage.parts.0.value.bonus}} + {{item.system.attack.damage.parts.0.value.bonus}}{{/if}} {{item.system.attack.damage.parts.0.value.dice}}{{#if item.system.attack.damage.parts.0.value.bonus}} + {{item.system.attack.damage.parts.0.value.bonus}}{{/if}}
{{!-- ({{localize (concat 'DAGGERHEART.CONFIG.DamageType.' item.system.attack.damage.parts.0.type '.abbreviation')}}) --}} {{!-- ({{localize (concat 'DAGGERHEART.CONFIG.DamageType.' item.system.attack.damage.parts.0.type '.abbreviation')}}) --}}
{{#with (lookup @root.config.GENERAL.damageTypes item.system.attack.damage.parts.0.type)}} {{#each item.system.attack.damage.parts.0.type as | type | }}
{{#each icon}} {{#with (lookup @root.config.GENERAL.damageTypes type)}}
<i class="fa-solid {{this}}"></i> <i class="fa-solid {{icon}}"></i>
{{/each}} {{/with}}
{{/with}} {{/each}}
</div> </div>
</div> </div>
{{else}} {{else}}
@ -32,7 +32,11 @@
</div> </div>
<div class="tag"> <div class="tag">
{{item.system.attack.damage.parts.0.value.dice}}{{#if item.system.attack.damage.parts.0.value.bonus}} + {{item.system.attack.damage.parts.0.value.bonus}}{{/if}} {{item.system.attack.damage.parts.0.value.dice}}{{#if item.system.attack.damage.parts.0.value.bonus}} + {{item.system.attack.damage.parts.0.value.bonus}}{{/if}}
({{localize (concat 'DAGGERHEART.CONFIG.DamageType.' item.system.attack.damage.parts.0.type '.abbreviation')}}) (
{{#each item.system.attack.damage.parts.0.type}}
{{localize (concat 'DAGGERHEART.CONFIG.DamageType.' this '.abbreviation')}}
{{/each}}
)
</div> </div>
<div class="tag"> <div class="tag">
{{localize (concat 'DAGGERHEART.CONFIG.Burden.' item.system.burden)}} {{localize (concat 'DAGGERHEART.CONFIG.Burden.' item.system.burden)}}

View file

@ -15,7 +15,11 @@
{{localize (concat 'DAGGERHEART.CONFIG.Range.' source.system.attack.range '.name')}} {{localize (concat 'DAGGERHEART.CONFIG.Range.' source.system.attack.range '.name')}}
<span>-</span> <span>-</span>
{{source.system.attack.damage.parts.0.value.dice}}{{#if source.system.attack.damage.parts.0.value.bonus}} + {{source.system.attack.damage.parts.0.value.bonus}}{{/if}} {{source.system.attack.damage.parts.0.value.dice}}{{#if source.system.attack.damage.parts.0.value.bonus}} + {{source.system.attack.damage.parts.0.value.bonus}}{{/if}}
({{localize (concat 'DAGGERHEART.CONFIG.DamageType.' source.system.attack.damage.parts.0.type '.abbreviation')}}) (
{{#each source.system.attack.damage.parts.0.type}}
{{localize (concat 'DAGGERHEART.CONFIG.DamageType.' this '.abbreviation')}}
{{/each}}
)
<span>-</span> <span>-</span>
{{localize (concat 'DAGGERHEART.CONFIG.Burden.' source.system.burden)}} {{localize (concat 'DAGGERHEART.CONFIG.Burden.' source.system.burden)}}
</h3> </h3>