mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-18 07:59:03 +01:00
More rules
This commit is contained in:
parent
e53b0a8a73
commit
323fff73cd
10 changed files with 194 additions and 33 deletions
23
lang/en.json
23
lang/en.json
|
|
@ -318,6 +318,7 @@
|
|||
"DamageReduction": {
|
||||
"armorMarks": "Armor Marks",
|
||||
"armorWithStress": "Spend 1 stress to use an extra mark",
|
||||
"thresholdImmunities": "Threshold Immunities",
|
||||
"stress": "Stress",
|
||||
"stressReduction": "Reduce By Stress",
|
||||
"title": "Damage Reduction",
|
||||
|
|
@ -952,6 +953,12 @@
|
|||
"name": "Dice Set"
|
||||
}
|
||||
},
|
||||
"RuleChoice": {
|
||||
"off": "Off",
|
||||
"offWithToggle": "Off With Toggle",
|
||||
"on": "On",
|
||||
"onWithToggle": "On With Toggle"
|
||||
},
|
||||
"SelectAction": {
|
||||
"selectType": "Select Action Type",
|
||||
"selectAction": "Action Selection"
|
||||
|
|
@ -1662,7 +1669,8 @@
|
|||
"major": "Major",
|
||||
"severe": "Severe",
|
||||
"majorThreshold": "Major Damage Threshold",
|
||||
"severeThreshold": "Severe Damage Threshold"
|
||||
"severeThreshold": "Severe Damage Threshold",
|
||||
"with": "{threshold} Damage Threshold"
|
||||
},
|
||||
"Dice": {
|
||||
"single": "Die",
|
||||
|
|
@ -1772,6 +1780,10 @@
|
|||
"hint": "If this value is set you can use up to that much stress to spend additional Armor Marks beyond your normal maximum."
|
||||
},
|
||||
"stress": {
|
||||
"any": {
|
||||
"label": "Stress Damage Reduction: Any",
|
||||
"hint": "The cost in stress you can pay to reduce incoming damage down one threshold"
|
||||
},
|
||||
"severe": {
|
||||
"label": "Stress Damage Reduction: Severe",
|
||||
"hint": "The cost in stress you can pay to reduce severe damage down to major."
|
||||
|
|
@ -1850,6 +1862,7 @@
|
|||
},
|
||||
"actorName": "Actor Name",
|
||||
"amount": "Amount",
|
||||
"any": "Any",
|
||||
"armorScore": "Armor Score",
|
||||
"activeEffects": "Active Effects",
|
||||
"armorSlots": "Armor Slots",
|
||||
|
|
@ -2059,6 +2072,10 @@
|
|||
"hint": "Automatically increase the GM's fear pool on a fear duality roll result."
|
||||
},
|
||||
"FIELDS": {
|
||||
"damageReductionRulesDefault": {
|
||||
"label": "Damage Reduction Rules Default",
|
||||
"hint": "Wether using armor and reductions has rules on by default"
|
||||
},
|
||||
"hopeFear": {
|
||||
"label": "Hope & Fear",
|
||||
"gm": { "label": "GM" },
|
||||
|
|
@ -2312,7 +2329,9 @@
|
|||
"appliedEvenIfSuccessful": "Applied even if save succeeded",
|
||||
"diceIsRerolled": "The dice has been rerolled (x{times})",
|
||||
"pendingSaves": "Pending Reaction Rolls",
|
||||
"openSheetSettings": "Open Settings"
|
||||
"openSheetSettings": "Open Settings",
|
||||
"rulesOn": "Rules On",
|
||||
"rulesOff": "Rules Off"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,14 +10,18 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
|
|||
this.reject = reject;
|
||||
this.actor = actor;
|
||||
this.damage = damage;
|
||||
this.rulesDefault = game.settings.get(
|
||||
CONFIG.DH.id,
|
||||
CONFIG.DH.SETTINGS.gameSettings.Automation
|
||||
).damageReductionRulesDefault;
|
||||
|
||||
this.rulesOn = [CONFIG.DH.GENERAL.ruleChoice.on.id, CONFIG.DH.GENERAL.ruleChoice.onWithToggle.id].includes(
|
||||
this.rulesDefault
|
||||
);
|
||||
|
||||
const canApplyArmor = damageType.every(t => actor.system.armorApplicableDamageTypes[t] === true);
|
||||
const maxArmorMarks = canApplyArmor
|
||||
? Math.min(
|
||||
actor.system.armorScore - actor.system.armor.system.marks.value,
|
||||
actor.system.rules.damageReduction.maxArmorMarked.value
|
||||
)
|
||||
: 0;
|
||||
const availableArmor = actor.system.armorScore - actor.system.armor.system.marks.value;
|
||||
const maxArmorMarks = canApplyArmor ? availableArmor : 0;
|
||||
|
||||
const armor = [...Array(maxArmorMarks).keys()].reduce((acc, _) => {
|
||||
acc[foundry.utils.randomID()] = { selected: false };
|
||||
|
|
@ -42,6 +46,7 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
|
|||
acc[damage] = {
|
||||
cost: dr.cost,
|
||||
selected: false,
|
||||
any: key === 'any',
|
||||
from: getDamageLabel(damage),
|
||||
to: getDamageLabel(damage - 1)
|
||||
};
|
||||
|
|
@ -51,16 +56,28 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
|
|||
},
|
||||
null
|
||||
);
|
||||
|
||||
this.thresholdImmunities = Object.keys(actor.system.rules.damageReduction.thresholdImmunities).reduce(
|
||||
(acc, key) => {
|
||||
if (actor.system.rules.damageReduction.thresholdImmunities[key])
|
||||
acc[damageKeyToNumber(key)] = game.i18n.format(`DAGGERHEART.GENERAL.DamageThresholds.with`, {
|
||||
threshold: game.i18n.localize(`DAGGERHEART.GENERAL.DamageThresholds.${key}`)
|
||||
});
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
classes: ['daggerheart', 'views', 'damage-reduction'],
|
||||
position: {
|
||||
width: 240,
|
||||
width: 280,
|
||||
height: 'auto'
|
||||
},
|
||||
actions: {
|
||||
toggleRules: this.toggleRules,
|
||||
setMarks: this.setMarks,
|
||||
useStressReduction: this.useStressReduction,
|
||||
takeDamage: this.takeDamage
|
||||
|
|
@ -89,6 +106,12 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
|
|||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.rulesOn = this.rulesOn;
|
||||
context.rulesToggleable = [
|
||||
CONFIG.DH.GENERAL.ruleChoice.onWithToggle.id,
|
||||
CONFIG.DH.GENERAL.ruleChoice.offWithToggle.id
|
||||
].includes(this.rulesDefault);
|
||||
context.thresholdImmunities = this.thresholdImmunities;
|
||||
|
||||
const { selectedArmorMarks, selectedStressMarks, stressReductions, currentMarks, currentDamage } =
|
||||
this.getDamageInfo();
|
||||
|
|
@ -110,12 +133,22 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
|
|||
}
|
||||
: null;
|
||||
|
||||
context.marks = this.marks;
|
||||
const maxArmor = this.actor.system.rules.damageReduction.maxArmorMarked.value;
|
||||
context.marks = {
|
||||
armor: Object.keys(this.marks.armor).reduce((acc, key, index) => {
|
||||
const mark = this.marks.armor[key];
|
||||
if (!this.rulesOn || index + 1 <= maxArmor) acc[key] = mark;
|
||||
|
||||
return acc;
|
||||
}, {}),
|
||||
stress: this.marks.stress
|
||||
};
|
||||
context.availableStressReductions = this.availableStressReductions;
|
||||
|
||||
context.damage = getDamageLabel(this.damage);
|
||||
context.reducedDamage = currentDamage !== this.damage ? getDamageLabel(currentDamage) : null;
|
||||
context.currentDamage = context.reducedDamage ?? context.damage;
|
||||
context.currentDamageNr = currentDamage;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
|
@ -136,22 +169,48 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
|
|||
|
||||
const armorMarkReduction =
|
||||
selectedArmorMarks.length * this.actor.system.rules.damageReduction.increasePerArmorMark;
|
||||
const currentDamage = this.damage - armorMarkReduction - selectedStressMarks.length - stressReductions.length;
|
||||
let currentDamage = Math.max(
|
||||
this.damage - armorMarkReduction - selectedStressMarks.length - stressReductions.length,
|
||||
0
|
||||
);
|
||||
|
||||
if (this.thresholdImmunities[currentDamage]) currentDamage = 0;
|
||||
|
||||
return { selectedArmorMarks, selectedStressMarks, stressReductions, currentMarks, currentDamage };
|
||||
};
|
||||
|
||||
static toggleRules() {
|
||||
this.rulesOn = !this.rulesOn;
|
||||
|
||||
const maxArmor = this.actor.system.rules.damageReduction.maxArmorMarked.value;
|
||||
this.marks = {
|
||||
armor: Object.keys(this.marks.armor).reduce((acc, key, index) => {
|
||||
const mark = this.marks.armor[key];
|
||||
const keepSelectValue = !this.rulesOn || index + 1 <= maxArmor;
|
||||
acc[key] = { ...mark, selected: keepSelectValue ? mark.selected : false };
|
||||
|
||||
return acc;
|
||||
}, {}),
|
||||
stress: this.marks.stress
|
||||
};
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static setMarks(_, target) {
|
||||
const currentMark = this.marks[target.dataset.type][target.dataset.key];
|
||||
const { selectedStressMarks, stressReductions, currentMarks, currentDamage } = this.getDamageInfo();
|
||||
|
||||
if (!currentMark.selected && currentDamage === 0) {
|
||||
ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.damageAlreadyNone'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!currentMark.selected && currentMarks === this.actor.system.armorScore) {
|
||||
ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noAvailableArmorMarks'));
|
||||
return;
|
||||
if (this.rulesOn) {
|
||||
if (!currentMark.selected && currentMarks === this.actor.system.armorScore) {
|
||||
ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noAvailableArmorMarks'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentMark.selected) {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,25 @@ export const compendiumJournals = {
|
|||
welcome: 'Compendium.daggerheart.journals.JournalEntry.g7NhKvwltwafmMyR'
|
||||
};
|
||||
|
||||
export const ruleChoice = {
|
||||
on: {
|
||||
id: 'on',
|
||||
label: 'DAGGERHEART.CONFIG.RuleChoice.on'
|
||||
},
|
||||
of: {
|
||||
id: 'off',
|
||||
label: 'DAGGERHEART.CONFIG.RuleChoice.off'
|
||||
},
|
||||
onWithToggle: {
|
||||
id: 'onWithToggle',
|
||||
label: 'DAGGERHEART.CONFIG.RuleChoice.onWithToggle'
|
||||
},
|
||||
offWithToggle: {
|
||||
id: 'offWithToggle',
|
||||
label: 'DAGGERHEART.CONFIG.RuleChoice.offWithToggle'
|
||||
}
|
||||
};
|
||||
|
||||
export const range = {
|
||||
self: {
|
||||
id: 'self',
|
||||
|
|
|
|||
|
|
@ -239,7 +239,8 @@ export default class DhCharacter extends BaseDataActor {
|
|||
stressDamageReduction: new fields.SchemaField({
|
||||
severe: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.severe'),
|
||||
major: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.major'),
|
||||
minor: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.minor')
|
||||
minor: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.minor'),
|
||||
any: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.any')
|
||||
}),
|
||||
increasePerArmorMark: new fields.NumberField({
|
||||
integer: true,
|
||||
|
|
@ -248,7 +249,11 @@ export default class DhCharacter extends BaseDataActor {
|
|||
hint: 'DAGGERHEART.GENERAL.Rules.damageReduction.increasePerArmorMark.hint'
|
||||
}),
|
||||
magical: new fields.BooleanField({ initial: false }),
|
||||
physical: new fields.BooleanField({ initial: false })
|
||||
physical: new fields.BooleanField({ initial: false }),
|
||||
thresholdImmunities: new fields.SchemaField({
|
||||
minor: new fields.BooleanField({ initial: false })
|
||||
}),
|
||||
disabledArmor: new fields.BooleanField({ intial: false })
|
||||
}),
|
||||
attack: new fields.SchemaField({
|
||||
damage: new fields.SchemaField({
|
||||
|
|
|
|||
|
|
@ -34,6 +34,12 @@ export default class DhAutomation extends foundry.abstract.DataModel {
|
|||
initial: true,
|
||||
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.effects.rangeDependent.label'
|
||||
})
|
||||
}),
|
||||
damageReductionRulesDefault: new fields.StringField({
|
||||
required: true,
|
||||
choices: CONFIG.DH.GENERAL.ruleChoice,
|
||||
initial: CONFIG.DH.GENERAL.ruleChoice.onWithToggle.id,
|
||||
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.damageReductionRulesDefault.label'
|
||||
})
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -464,14 +464,17 @@ export default class DhpActor extends Actor {
|
|||
}
|
||||
|
||||
#canReduceDamage(hpDamage, type) {
|
||||
const { stressDamageReduction, disabledArmor } = this.system.rules.damageReduction;
|
||||
if (disabledArmor) return false;
|
||||
|
||||
const availableStress = this.system.resources.stress.max - this.system.resources.stress.value;
|
||||
|
||||
const canUseArmor =
|
||||
this.system.armor &&
|
||||
this.system.armor.system.marks.value < this.system.armorScore &&
|
||||
type.every(t => this.system.armorApplicableDamageTypes[t] === true);
|
||||
const canUseStress = Object.keys(this.system.rules.damageReduction.stressDamageReduction).reduce((acc, x) => {
|
||||
const rule = this.system.rules.damageReduction.stressDamageReduction[x];
|
||||
const canUseStress = Object.keys(stressDamageReduction).reduce((acc, x) => {
|
||||
const rule = stressDamageReduction[x];
|
||||
if (damageKeyToNumber(x) <= hpDamage) return acc || (rule.enabled && availableStress >= rule.cost);
|
||||
return acc;
|
||||
}, false);
|
||||
|
|
|
|||
|
|
@ -2,11 +2,35 @@
|
|||
|
||||
.daggerheart.views.damage-reduction {
|
||||
.damage-reduction-container {
|
||||
position: relative;
|
||||
padding: 8px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
|
||||
.rules-button {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
border-radius: 50%;
|
||||
|
||||
&.inactive {
|
||||
opacity: 0.4;
|
||||
|
||||
::after {
|
||||
position: absolute;
|
||||
content: '/';
|
||||
color: red;
|
||||
font-weight: 700;
|
||||
font-size: 1.8em;
|
||||
left: 5px;
|
||||
top: 0;
|
||||
rotate: 13deg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.section-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
@ -44,7 +68,7 @@
|
|||
|
||||
.mark-selection-inner {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
gap: 8px;
|
||||
|
||||
.mark-container {
|
||||
cursor: pointer;
|
||||
|
|
@ -58,10 +82,6 @@
|
|||
justify-content: center;
|
||||
opacity: 0.4;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
opacity: 1;
|
||||
}
|
||||
|
|
@ -79,11 +99,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
.stress-reduction-container {
|
||||
.chip-container {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
|
||||
.stress-reduction {
|
||||
.chip-inner-container {
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border-radius: 6px;
|
||||
height: 26px;
|
||||
|
|
@ -113,6 +133,14 @@
|
|||
}
|
||||
}
|
||||
|
||||
.threshold-label {
|
||||
opacity: 0.6;
|
||||
|
||||
&.active {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.markers-subtitle {
|
||||
margin: -4px 0 0 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,6 @@
|
|||
|
||||
.daggerheart.views.damage-reduction {
|
||||
.window-content {
|
||||
padding: 8px 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
<div class="damage-reduction-container">
|
||||
{{#if rulesToggleable}}
|
||||
<button type="button" class="rules-button {{#unless rulesOn}}inactive{{/unless}}" data-action="toggleRules" data-tooltip-text="{{#if rulesOn}}{{localize "DAGGERHEART.UI.Tooltip.rulesOn"}}{{else}}{{localize "DAGGERHEART.UI.Tooltip.rulesOff"}}{{/if}}">
|
||||
<i class="fa-solid fa-book"></i>
|
||||
</button>
|
||||
{{/if}}
|
||||
<div class="section-container padded">
|
||||
<div class="resources-container">
|
||||
<div class="resource-container">
|
||||
|
|
@ -25,8 +30,6 @@
|
|||
<i class="fa-solid fa-shield"></i>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="mark-selection-inner">
|
||||
{{#each marks.stress}}
|
||||
<div
|
||||
class="mark-container {{#if this.selected}}selected{{/if}} {{#if (not @root.basicMarksUsed)}}inactive{{/if}}"
|
||||
|
|
@ -40,19 +43,25 @@
|
|||
<div class="markers-subtitle bold">{{localize "DAGGERHEART.APPLICATIONS.DamageReduction.usedMarks"}}</div>
|
||||
</div>
|
||||
|
||||
{{#if availableStressReductions}}
|
||||
<div class="resources-container">
|
||||
<div class="resource-container">
|
||||
<h4 class="armor-title">{{localize "DAGGERHEART.APPLICATIONS.DamageReduction.stressReduction"}}</h4>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#each availableStressReductions}}
|
||||
<div class="section-container">
|
||||
<h4 class="stress-reduction-container divider">
|
||||
<div class="stress-reduction {{#if (eq this.from @root.currentDamage)}}active{{/if}} {{#if this.selected}}selected{{/if}}" data-action="useStressReduction" data-reduction="{{@key}}">
|
||||
{{this.from}}
|
||||
<i class="fa-solid fa-arrow-right-long"></i>
|
||||
{{this.to}}
|
||||
<h4 class="chip-container divider">
|
||||
<div class="chip-inner-container selectable {{#if (or this.any (eq this.from @root.currentDamage))}}active{{/if}} {{#if this.selected}}selected{{/if}}" data-action="useStressReduction" data-reduction="{{@key}}">
|
||||
{{#if this.any}}
|
||||
{{localize "DAGGERHEART.GENERAL.any"}}
|
||||
{{else}}
|
||||
{{this.from}}
|
||||
<i class="fa-solid fa-arrow-right-long"></i>
|
||||
{{this.to}}
|
||||
{{/if}}
|
||||
<div class="stress-reduction-cost">
|
||||
{{this.cost}}
|
||||
<i class="fa-solid fa-bolt"></i>
|
||||
|
|
@ -62,6 +71,18 @@
|
|||
</div>
|
||||
{{/each}}
|
||||
|
||||
{{#if thresholdImmunities}}
|
||||
<div class="resources-container">
|
||||
<div class="resource-container">
|
||||
<h4 class="armor-title">{{localize "DAGGERHEART.APPLICATIONS.DamageReduction.thresholdImmunities"}}</h4>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#each thresholdImmunities as | immunity key |}}
|
||||
<div class="threshold-label {{#if (gte key @root.currentDamageNr)}}active{{/if}}">{{immunity}}</div>
|
||||
{{/each}}
|
||||
|
||||
<footer class="padded">
|
||||
<button type="button" data-action="takeDamage">
|
||||
{{localize "Take"}}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
{{formGroup settingFields.schema.fields.hordeDamage value=settingFields._source.hordeDamage localize=true}}
|
||||
{{formGroup settingFields.schema.fields.effects.fields.rangeDependent value=settingFields._source.effects.rangeDependent localize=true}}
|
||||
{{formGroup settingFields.schema.fields.levelupAuto value=settingFields._source.levelupAuto localize=true}}
|
||||
{{formGroup settingFields.schema.fields.damageReductionRulesDefault value=settingFields._source.damageReductionRulesDefault localize=true}}
|
||||
|
||||
<footer class="form-footer">
|
||||
<button data-action="reset">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue