mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-17 15:39:02 +01:00
Fixed armor
This commit is contained in:
parent
f633c42aa8
commit
b3472177da
8 changed files with 86 additions and 46 deletions
|
|
@ -478,8 +478,8 @@
|
|||
"name": "Impenetrable",
|
||||
"description": "Once per short rest, when you would mark your last Hit Point, you can instead mark a Stress."
|
||||
},
|
||||
"magic": {
|
||||
"name": "Magic",
|
||||
"magical": {
|
||||
"name": "Magical",
|
||||
"description": "You can't mark an Armor Slot to reduce physical damage."
|
||||
},
|
||||
"physical": {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { damageKeyToNumber, getDamageLabel } from '../../helpers/utils.mjs';
|
|||
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
|
||||
export default class DamageReductionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor(resolve, reject, actor, damage) {
|
||||
constructor(resolve, reject, actor, damage, damageType) {
|
||||
super({});
|
||||
|
||||
this.resolve = resolve;
|
||||
|
|
@ -11,10 +11,13 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
|
|||
this.actor = actor;
|
||||
this.damage = damage;
|
||||
|
||||
const maxArmorMarks = Math.min(
|
||||
const canApplyArmor = actor.system.armorApplicableDamageTypes[damageType];
|
||||
const maxArmorMarks = canApplyArmor
|
||||
? Math.min(
|
||||
actor.system.armorScore - actor.system.armor.system.marks.value,
|
||||
actor.system.rules.damageReduction.maxArmorMarked.total
|
||||
);
|
||||
)
|
||||
: 0;
|
||||
|
||||
const armor = [...Array(maxArmorMarks).keys()].reduce((acc, _) => {
|
||||
acc[foundry.utils.randomID()] = { selected: false };
|
||||
|
|
@ -129,12 +132,15 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
|
|||
getDamageInfo = () => {
|
||||
const selectedArmorMarks = Object.values(this.marks.armor).filter(x => x.selected);
|
||||
const selectedStressMarks = Object.values(this.marks.stress).filter(x => x.selected);
|
||||
const stressReductions = Object.values(this.availableStressReductions).filter(red => red.selected);
|
||||
const stressReductions = this.availableStressReductions
|
||||
? Object.values(this.availableStressReductions).filter(red => red.selected)
|
||||
: [];
|
||||
const currentMarks =
|
||||
this.actor.system.armor.system.marks.value + selectedArmorMarks.length + selectedStressMarks.length;
|
||||
|
||||
const currentDamage =
|
||||
this.damage - selectedArmorMarks.length - selectedStressMarks.length - stressReductions.length;
|
||||
const armorMarkReduction =
|
||||
selectedArmorMarks.length * this.actor.system.rules.damageReduction.increasePerArmorMark;
|
||||
const currentDamage = this.damage - armorMarkReduction - selectedStressMarks.length - stressReductions.length;
|
||||
|
||||
return { selectedArmorMarks, selectedStressMarks, stressReductions, currentMarks, currentDamage };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ export default class ArmorSheet extends DHBaseItemSheet {
|
|||
|
||||
switch (partId) {
|
||||
case 'settings':
|
||||
context.features = this.document.system.features.map(x => x.value);
|
||||
context.features = this.document.system.armorFeatures.map(x => x.value);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -47,6 +47,6 @@ export default class ArmorSheet extends DHBaseItemSheet {
|
|||
* @param {Array<Object>} selectedOptions - The currently selected tag objects.
|
||||
*/
|
||||
static async #onFeatureSelect(selectedOptions) {
|
||||
await this.document.update({ 'system.features': selectedOptions.map(x => ({ value: x.value })) });
|
||||
await this.document.update({ 'system.armorFeatures': selectedOptions.map(x => ({ value: x.value })) });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -190,19 +190,19 @@ export const armorFeatures = {
|
|||
}
|
||||
]
|
||||
},
|
||||
magic: {
|
||||
label: 'DAGGERHEART.CONFIG.ArmorFeature.magic.name',
|
||||
description: 'DAGGERHEART.CONFIG.ArmorFeature.magic.description',
|
||||
magical: {
|
||||
label: 'DAGGERHEART.CONFIG.ArmorFeature.magical.name',
|
||||
description: 'DAGGERHEART.CONFIG.ArmorFeature.magical.description',
|
||||
effects: [
|
||||
{
|
||||
name: 'DAGGERHEART.CONFIG.ArmorFeature.magic.name',
|
||||
description: 'DAGGERHEART.CONFIG.ArmorFeature.magic.description',
|
||||
name: 'DAGGERHEART.CONFIG.ArmorFeature.magical.name',
|
||||
description: 'DAGGERHEART.CONFIG.ArmorFeature.magical.description',
|
||||
img: 'icons/magic/defensive/barrier-shield-dome-blue-purple.webp',
|
||||
changes: [
|
||||
{
|
||||
key: 'system.rules.damageReduction.magic',
|
||||
key: 'system.rules.damageReduction.magical',
|
||||
mode: 5,
|
||||
value: '1'
|
||||
value: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -220,7 +220,7 @@ export const armorFeatures = {
|
|||
{
|
||||
key: 'system.rules.damageReduction.physical',
|
||||
mode: 5,
|
||||
value: '1'
|
||||
value: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -152,7 +152,15 @@ export default class DhCharacter extends BaseDataActor {
|
|||
initial: null
|
||||
}),
|
||||
weapon: new fields.SchemaField({
|
||||
/* Unimplemented
|
||||
-> Should remove the lowest damage dice from weapon damage
|
||||
-> Reflect this in the chat message somehow so players get feedback that their choice is helping them.
|
||||
*/
|
||||
dropLowestDamageDice: new fields.BooleanField({ initial: false }),
|
||||
/* Unimplemented
|
||||
-> Should flip any lowest possible dice rolls for weapon damage to highest
|
||||
-> Reflect this in the chat message somehow so players get feedback that their choice is helping them.
|
||||
*/
|
||||
flipMinDiceValue: new fields.BooleanField({ intial: false })
|
||||
}),
|
||||
runeWard: new fields.BooleanField({ initial: false })
|
||||
|
|
@ -302,6 +310,13 @@ export default class DhCharacter extends BaseDataActor {
|
|||
);
|
||||
}
|
||||
|
||||
get armorApplicableDamageTypes() {
|
||||
return {
|
||||
physical: !this.rules.damageReduction.magical,
|
||||
magical: !this.rules.damageReduction.physical
|
||||
};
|
||||
}
|
||||
|
||||
static async unequipBeforeEquip(itemToEquip) {
|
||||
const primary = this.primaryWeapon,
|
||||
secondary = this.secondaryWeapon;
|
||||
|
|
@ -368,6 +383,7 @@ export default class DhCharacter extends BaseDataActor {
|
|||
}
|
||||
|
||||
const armor = this.armor;
|
||||
this.armorScore = this.armor ? this.armor.system.baseScore + (this.bonuses.armorScore ?? 0) : 0; // Bonuses to armorScore won't have been applied yet. Need to solve in documentPreparation somehow
|
||||
this.damageThresholds = {
|
||||
major: armor
|
||||
? armor.system.baseThresholds.major + this.levelData.level.current
|
||||
|
|
@ -395,7 +411,6 @@ export default class DhCharacter extends BaseDataActor {
|
|||
this.rules.damageReduction.maxArmorMarked.total =
|
||||
this.rules.damageReduction.maxArmorMarked.value + this.rules.damageReduction.maxArmorMarked.bonus;
|
||||
|
||||
this.armorScore = this.armor ? this.armor.system.baseScore + (this.bonuses.armorScore ?? 0) : 0;
|
||||
this.resources.hitPoints.maxTotal = (this.class.value?.system?.hitPoints ?? 0) + this.resources.hitPoints.bonus;
|
||||
this.resources.stress.maxTotal = this.resources.stress.max + this.resources.stress.bonus;
|
||||
this.evasion.total = (this.class?.evasion ?? 0) + this.evasion.bonus;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export default class DHArmor extends BaseDataItem {
|
|||
tier: new fields.NumberField({ required: true, integer: true, initial: 1, min: 1 }),
|
||||
equipped: new fields.BooleanField({ initial: false }),
|
||||
baseScore: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
features: new fields.ArrayField(
|
||||
armorFeatures: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
value: new fields.StringField({
|
||||
required: true,
|
||||
|
|
@ -44,25 +44,22 @@ export default class DHArmor extends BaseDataItem {
|
|||
};
|
||||
}
|
||||
|
||||
get featureInfo() {
|
||||
return this.feature ? CONFIG.DH.ITEM.armorFeatures[this.feature] : null;
|
||||
}
|
||||
|
||||
async _preUpdate(changes, options, user) {
|
||||
const allowed = await super._preUpdate(changes, options, user);
|
||||
if (allowed === false) return false;
|
||||
|
||||
if (changes.system.features) {
|
||||
const removed = this.features.filter(x => !changes.system.features.includes(x));
|
||||
const added = changes.system.features.filter(x => !this.features.includes(x));
|
||||
if (changes.system.armorFeatures) {
|
||||
const removed = this.armorFeatures.filter(x => !changes.system.armorFeatures.includes(x));
|
||||
const added = changes.system.armorFeatures.filter(x => !this.armorFeatures.includes(x));
|
||||
|
||||
const effectIds = [];
|
||||
const actionIds = [];
|
||||
for (var feature of removed) {
|
||||
for (var effectId of feature.effectIds) {
|
||||
await this.parent.effects.get(effectId).delete();
|
||||
}
|
||||
|
||||
changes.system.actions = this.actions.filter(x => !feature.actionIds.includes(x._id));
|
||||
effectIds.push(...feature.effectIds);
|
||||
actionIds.push(...feature.actionIds);
|
||||
}
|
||||
await this.parent.deleteEmbeddedDocuments('ActiveEffect', effectIds);
|
||||
changes.system.actions = this.actions.filter(x => !actionIds.includes(x._id));
|
||||
|
||||
for (var feature of added) {
|
||||
const featureData = armorFeatures[feature.value];
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { GMUpdateEvent, socketEvent } from '../systemRegistration/socket.mjs';
|
|||
import DamageReductionDialog from '../applications/dialogs/damageReductionDialog.mjs';
|
||||
import { LevelOptionType } from '../data/levelTier.mjs';
|
||||
import DHFeature from '../data/item/feature.mjs';
|
||||
import { damageKeyToNumber, getDamageKey } from '../helpers/utils.mjs';
|
||||
|
||||
export default class DhpActor extends Actor {
|
||||
async _preCreate(data, options, user) {
|
||||
|
|
@ -430,12 +431,31 @@ export default class DhpActor extends Actor {
|
|||
cls.create(msg.toObject());
|
||||
}
|
||||
|
||||
async takeDamage(damage, type) {
|
||||
#canReduceDamage(hpDamage, type) {
|
||||
const availableStress = this.system.resources.stress.maxTotal - this.system.resources.stress.value;
|
||||
|
||||
const canUseArmor =
|
||||
this.system.armor &&
|
||||
this.system.armor.system.marks.value < this.system.armorScore &&
|
||||
this.system.armorApplicableDamageTypes[type];
|
||||
const canUseStress = Object.keys(this.system.rules.damageReduction.stressDamageReduction).reduce((acc, x) => {
|
||||
const rule = this.system.rules.damageReduction.stressDamageReduction[x];
|
||||
if (damageKeyToNumber(x) <= hpDamage) return acc || (rule.enabled && availableStress >= rule.cost);
|
||||
return acc;
|
||||
}, false);
|
||||
|
||||
return canUseArmor || canUseStress;
|
||||
}
|
||||
|
||||
async takeDamage(baseDamage, type) {
|
||||
if (this.type === 'companion') {
|
||||
await this.modifyResource([{ value: 1, type: 'stress' }]);
|
||||
return;
|
||||
}
|
||||
|
||||
const flatReduction = this.system.bonuses.damageReduction[type];
|
||||
const damage = Math.max(baseDamage - (flatReduction ?? 0), 0);
|
||||
|
||||
const hpDamage =
|
||||
damage >= this.system.damageThresholds.severe
|
||||
? 3
|
||||
|
|
@ -445,13 +465,9 @@ export default class DhpActor extends Actor {
|
|||
? 1
|
||||
: 0;
|
||||
|
||||
if (
|
||||
this.type === 'character' &&
|
||||
this.system.armor &&
|
||||
this.system.armor.system.marks.value < this.system.armorScore
|
||||
) {
|
||||
if (hpDamage && this.type === 'character' && this.#canReduceDamage(hpDamage, type)) {
|
||||
new Promise((resolve, reject) => {
|
||||
new DamageReductionDialog(resolve, reject, this, hpDamage).render(true);
|
||||
new DamageReductionDialog(resolve, reject, this, hpDamage, type).render(true);
|
||||
})
|
||||
.then(async ({ modifiedDamage, armorSpent, stressSpent }) => {
|
||||
const resources = [
|
||||
|
|
|
|||
|
|
@ -190,6 +190,8 @@ export const tagifyElement = (element, options, onChange, tagifyOptions = {}) =>
|
|||
});
|
||||
|
||||
tagifyElement.on('add', event => {
|
||||
if (event.detail.data.__isValid === 'not allowed') return;
|
||||
|
||||
const input = event.detail.tagify.DOM.originalInput;
|
||||
const currentList = input.value ? JSON.parse(input.value) : [];
|
||||
onChange([...currentList, event.detail.data], { option: event.detail.data.value, removed: false }, input);
|
||||
|
|
@ -233,19 +235,23 @@ Roll.replaceFormulaData = function (formula, data = {}, { missing, warn = false
|
|||
return nativeReplaceFormulaData(formula, data, { missing, warn });
|
||||
};
|
||||
|
||||
export const getDamageLabel = damage => {
|
||||
export const getDamageKey = damage => {
|
||||
switch (damage) {
|
||||
case 3:
|
||||
return game.i18n.localize('DAGGERHEART.GENERAL.Damage.severe');
|
||||
return 'severe';
|
||||
case 2:
|
||||
return game.i18n.localize('DAGGERHEART.GENERAL.Damage.major');
|
||||
return 'major';
|
||||
case 1:
|
||||
return game.i18n.localize('DAGGERHEART.GENERAL.Damage.minor');
|
||||
return 'minor';
|
||||
case 0:
|
||||
return game.i18n.localize('DAGGERHEART.GENERAL.Damage.none');
|
||||
return 'none';
|
||||
}
|
||||
};
|
||||
|
||||
export const getDamageLabel = damage => {
|
||||
return game.i18n.localize(`DAGGERHEART.GENERAL.Damage.${getDamageKey(damage)}`);
|
||||
};
|
||||
|
||||
export const damageKeyToNumber = key => {
|
||||
switch (key) {
|
||||
case 'severe':
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue