mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-03-07 14:36:13 +01:00
Added daggerheart durations and auto expiration of them
This commit is contained in:
parent
115a31423e
commit
0ba17117ea
14 changed files with 183 additions and 42 deletions
12
lang/en.json
12
lang/en.json
|
|
@ -659,6 +659,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"CONFIG": {
|
"CONFIG": {
|
||||||
|
"ActiveEffectDuration": {
|
||||||
|
"temporary": "Temporary",
|
||||||
|
"scene": "Next Scene",
|
||||||
|
"shortRest": "Next Rest",
|
||||||
|
"longRest": "Next Long Rest",
|
||||||
|
"session": "Next Session",
|
||||||
|
"custom": "Custom"
|
||||||
|
},
|
||||||
"AdversaryTrait": {
|
"AdversaryTrait": {
|
||||||
"relentless": {
|
"relentless": {
|
||||||
"name": "Relentless",
|
"name": "Relentless",
|
||||||
|
|
@ -2455,6 +2463,10 @@
|
||||||
"hint": "Automatically increase the GM's fear pool on a fear duality roll result."
|
"hint": "Automatically increase the GM's fear pool on a fear duality roll result."
|
||||||
},
|
},
|
||||||
"FIELDS": {
|
"FIELDS": {
|
||||||
|
"autoExpireActiveEffects": {
|
||||||
|
"label": "Auto Expire Active Effects",
|
||||||
|
"hint": "Active Effects with set durations will automatically be removed when their durations are up"
|
||||||
|
},
|
||||||
"damageReductionRulesDefault": {
|
"damageReductionRulesDefault": {
|
||||||
"label": "Damage Reduction Rules Default",
|
"label": "Damage Reduction Rules Default",
|
||||||
"hint": "Wether using armor and reductions has rules on by default"
|
"hint": "Wether using armor and reductions has rules on by default"
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { refreshIsAllowed } from '../../helpers/utils.mjs';
|
import { expireActiveEffects, refreshIsAllowed } from '../../helpers/utils.mjs';
|
||||||
|
|
||||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
||||||
|
|
@ -260,6 +260,8 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
||||||
await feature.update({ 'system.resource.value': resetValue });
|
await feature.update({ 'system.resource.value': resetValue });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expireActiveEffects(this.actor, [this.shortRest ? 'shortRest' : 'longRest']);
|
||||||
|
|
||||||
this.close();
|
this.close();
|
||||||
} else {
|
} else {
|
||||||
this.render();
|
this.render();
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,17 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'settings':
|
||||||
|
const groups = {
|
||||||
|
time: _loc('EFFECT.DURATION.UNITS.GROUPS.time'),
|
||||||
|
combat: _loc('EFFECT.DURATION.UNITS.GROUPS.combat')
|
||||||
|
};
|
||||||
|
partContext.durationUnits = CONST.ACTIVE_EFFECT_DURATION_UNITS.map(value => ({
|
||||||
|
value,
|
||||||
|
label: _loc(`EFFECT.DURATION.UNITS.${value}`),
|
||||||
|
group: CONST.ACTIVE_EFFECT_TIME_DURATION_UNITS.includes(value) ? groups.time : groups.combat
|
||||||
|
}));
|
||||||
|
break;
|
||||||
case 'changes':
|
case 'changes':
|
||||||
const fields = this.document.system.schema.fields.changes.element.fields;
|
const fields = this.document.system.schema.fields.changes.element.fields;
|
||||||
partContext.changes = await Promise.all(
|
partContext.changes = await Promise.all(
|
||||||
|
|
@ -162,4 +173,17 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
_onChangeForm(_formConfig, event) {
|
||||||
|
if (foundry.utils.isElementInstanceOf(event.target, 'select') && event.target.name === 'system.duration.type') {
|
||||||
|
const durationSection = this.element.querySelector('.custom-duration-section');
|
||||||
|
if (event.target.value === 'custom') durationSection.classList.add('visible');
|
||||||
|
else durationSection.classList.remove('visible');
|
||||||
|
|
||||||
|
const durationDescription = this.element.querySelector('.duration-description');
|
||||||
|
if (event.target.value === 'temporary') durationDescription.classList.add('visible');
|
||||||
|
else durationDescription.classList.remove('visible');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { refreshIsAllowed } from '../../../helpers/utils.mjs';
|
import { expireActiveEffects, refreshIsAllowed } from '../../../helpers/utils.mjs';
|
||||||
|
|
||||||
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
||||||
const { AbstractSidebarTab } = foundry.applications.sidebar;
|
const { AbstractSidebarTab } = foundry.applications.sidebar;
|
||||||
|
|
@ -58,6 +58,8 @@ export default class DaggerheartMenu extends HandlebarsApplicationMixin(Abstract
|
||||||
const refreshedActors = {};
|
const refreshedActors = {};
|
||||||
for (let actor of game.actors) {
|
for (let actor of game.actors) {
|
||||||
if (['character', 'adversary'].includes(actor.type) && actor.prototypeToken.actorLink) {
|
if (['character', 'adversary'].includes(actor.type) && actor.prototypeToken.actorLink) {
|
||||||
|
expireActiveEffects(actor, types);
|
||||||
|
|
||||||
const updates = {};
|
const updates = {};
|
||||||
for (let item of actor.items) {
|
for (let item of actor.items) {
|
||||||
if (item.system.metadata?.hasResource && refreshIsAllowed(types, item.system.resource?.recovery)) {
|
if (item.system.metadata?.hasResource && refreshIsAllowed(types, item.system.resource?.recovery)) {
|
||||||
|
|
|
||||||
|
|
@ -783,3 +783,30 @@ export const activeEffectModes = {
|
||||||
label: 'EFFECT.CHANGES.TYPES.override'
|
label: 'EFFECT.CHANGES.TYPES.override'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const activeEffectDurations = {
|
||||||
|
temporary: {
|
||||||
|
id: 'temporary',
|
||||||
|
label: 'DAGGERHEART.CONFIG.ActiveEffectDuration.temporary'
|
||||||
|
},
|
||||||
|
scene: {
|
||||||
|
id: 'scene',
|
||||||
|
label: 'DAGGERHEART.CONFIG.ActiveEffectDuration.scene'
|
||||||
|
},
|
||||||
|
shortRest: {
|
||||||
|
id: 'shortRest',
|
||||||
|
label: 'DAGGERHEART.CONFIG.ActiveEffectDuration.shortRest'
|
||||||
|
},
|
||||||
|
longRest: {
|
||||||
|
id: 'longRest',
|
||||||
|
label: 'DAGGERHEART.CONFIG.ActiveEffectDuration.longRest'
|
||||||
|
},
|
||||||
|
session: {
|
||||||
|
id: 'session',
|
||||||
|
label: 'DAGGERHEART.CONFIG.ActiveEffectDuration.session'
|
||||||
|
},
|
||||||
|
custom: {
|
||||||
|
id: 'custom',
|
||||||
|
label: 'DAGGERHEART.CONFIG.ActiveEffectDuration.custom'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,14 @@ export default class BaseEffect extends foundry.data.ActiveEffectTypeDataModel {
|
||||||
priority: new fields.NumberField()
|
priority: new fields.NumberField()
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
|
duration: new fields.SchemaField({
|
||||||
|
type: new fields.StringField({
|
||||||
|
choices: CONFIG.DH.GENERAL.activeEffectDurations,
|
||||||
|
blank: true,
|
||||||
|
label: 'DAGGERHEART.GENERAL.type'
|
||||||
|
}),
|
||||||
|
description: new fields.HTMLField({ label: 'DAGGERHEART.GENERAL.description' })
|
||||||
|
}),
|
||||||
rangeDependence: new fields.SchemaField({
|
rangeDependence: new fields.SchemaField({
|
||||||
enabled: new fields.BooleanField({
|
enabled: new fields.BooleanField({
|
||||||
required: true,
|
required: true,
|
||||||
|
|
|
||||||
|
|
@ -192,6 +192,11 @@ export default class DhAutomation extends foundry.abstract.DataModel {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
|
autoExpireActiveEffects: new fields.BooleanField({
|
||||||
|
required: true,
|
||||||
|
initial: true,
|
||||||
|
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.autoExpireActiveEffects.label'
|
||||||
|
}),
|
||||||
triggers: new fields.SchemaField({
|
triggers: new fields.SchemaField({
|
||||||
enabled: new fields.BooleanField({
|
enabled: new fields.BooleanField({
|
||||||
nullable: false,
|
nullable: false,
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,20 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this Active Effect is eligible to be registered with the {@link ActiveEffectRegistry}
|
||||||
|
*/
|
||||||
|
get isExpiryTrackable() {
|
||||||
|
return (
|
||||||
|
this.persisted &&
|
||||||
|
!this.inCompendium &&
|
||||||
|
this.modifiesActor &&
|
||||||
|
this.start &&
|
||||||
|
this.isTemporary &&
|
||||||
|
!this.isExpired
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
/* Event Handlers */
|
/* Event Handlers */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
|
||||||
|
|
@ -473,6 +473,8 @@ export async function waitForDiceSoNice(message) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function refreshIsAllowed(allowedTypes, typeToCheck) {
|
export function refreshIsAllowed(allowedTypes, typeToCheck) {
|
||||||
|
if (!allowedTypes) return true;
|
||||||
|
|
||||||
switch (typeToCheck) {
|
switch (typeToCheck) {
|
||||||
case CONFIG.DH.GENERAL.refreshTypes.scene.id:
|
case CONFIG.DH.GENERAL.refreshTypes.scene.id:
|
||||||
case CONFIG.DH.GENERAL.refreshTypes.session.id:
|
case CONFIG.DH.GENERAL.refreshTypes.session.id:
|
||||||
|
|
@ -489,6 +491,28 @@ export function refreshIsAllowed(allowedTypes, typeToCheck) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function expireActiveEffects(actor, allowedTypes = null) {
|
||||||
|
const shouldExpireEffects = game.settings.get(
|
||||||
|
CONFIG.DH.id,
|
||||||
|
CONFIG.DH.SETTINGS.gameSettings.Automation
|
||||||
|
).autoExpireActiveEffects;
|
||||||
|
if (!shouldExpireEffects) return;
|
||||||
|
|
||||||
|
const effectsToExpire = actor
|
||||||
|
.getActiveEffects()
|
||||||
|
.filter(effect => {
|
||||||
|
if (!effect.system?.duration.type) return false;
|
||||||
|
|
||||||
|
const { temporary, custom } = CONFIG.DH.GENERAL.activeEffectDurations;
|
||||||
|
if ([temporary.id, custom.id].includes(effect.system.duration.type)) return false;
|
||||||
|
|
||||||
|
return refreshIsAllowed(allowedTypes, effect.system.duration.type);
|
||||||
|
})
|
||||||
|
.map(x => x.id);
|
||||||
|
|
||||||
|
actor.deleteEmbeddedDocuments('ActiveEffect', effectsToExpire);
|
||||||
|
}
|
||||||
|
|
||||||
export async function getCritDamageBonus(formula) {
|
export async function getCritDamageBonus(formula) {
|
||||||
const critRoll = new Roll(formula);
|
const critRoll = new Roll(formula);
|
||||||
return critRoll.dice.reduce((acc, dice) => acc + dice.faces * dice.number, 0);
|
return critRoll.dice.reduce((acc, dice) => acc + dice.faces * dice.number, 0);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,28 @@
|
||||||
.application.sheet.daggerheart.dh-style.active-effect-config {
|
.application.sheet.daggerheart.dh-style.active-effect-config {
|
||||||
|
.custom-duration-section {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
height: 0;
|
||||||
|
transition: height ease-in-out 0.3s;
|
||||||
|
|
||||||
|
&.visible {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.duration-description {
|
||||||
|
height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: height ease-in-out 0.3s;
|
||||||
|
|
||||||
|
&.visible {
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.tab.changes {
|
.tab.changes {
|
||||||
gap: 0;
|
gap: 0;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,5 +42,12 @@
|
||||||
color: @golden;
|
color: @golden;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.duration-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
{{formGroup settingFields.schema.fields.summaryMessages.fields.effects value=settingFields._source.summaryMessages.effects localize=true}}
|
{{formGroup settingFields.schema.fields.summaryMessages.fields.effects value=settingFields._source.summaryMessages.effects localize=true}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{formGroup settingFields.schema.fields.autoExpireActiveEffects value=settingFields._source.autoExpireActiveEffects localize=true}}
|
||||||
{{formGroup settingFields.schema.fields.countdownAutomation value=settingFields._source.countdownAutomation localize=true}}
|
{{formGroup settingFields.schema.fields.countdownAutomation value=settingFields._source.countdownAutomation localize=true}}
|
||||||
{{formGroup settingFields.schema.fields.actionPoints value=settingFields._source.actionPoints localize=true}}
|
{{formGroup settingFields.schema.fields.actionPoints value=settingFields._source.actionPoints localize=true}}
|
||||||
{{formGroup settingFields.schema.fields.hordeDamage value=settingFields._source.hordeDamage localize=true}}
|
{{formGroup settingFields.schema.fields.hordeDamage value=settingFields._source.hordeDamage localize=true}}
|
||||||
|
|
|
||||||
|
|
@ -7,47 +7,31 @@
|
||||||
{{formGroup systemFields.rangeDependence.fields.target value=source.system.rangeDependence.target localize=true }}
|
{{formGroup systemFields.rangeDependence.fields.target value=source.system.rangeDependence.target localize=true }}
|
||||||
{{formGroup systemFields.rangeDependence.fields.range value=source.system.rangeDependence.range localize=true }}
|
{{formGroup systemFields.rangeDependence.fields.range value=source.system.rangeDependence.range localize=true }}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{{!--
|
<fieldset class="one-column">
|
||||||
{{#if start}}
|
<legend>{{localize "EFFECT.DURATION.Label"}}</legend>
|
||||||
<fieldset class="start-data">
|
|
||||||
<legend>{{localize "EFFECT.START.Header"}}</legend>
|
|
||||||
<div class="form-group">
|
|
||||||
<span class="label">{{localize "EFFECT.FIELDS.start.time.label"}}</span>
|
|
||||||
<div class="form-fields">{{start.time}}</div>
|
|
||||||
</div>
|
|
||||||
{{#if start.combat}}
|
|
||||||
<div class="form-group">
|
|
||||||
<span class="label">{{localize "DOCUMENT.Combat"}}</span>
|
|
||||||
<div class="form-fields">
|
|
||||||
{{localize "EFFECT.START.Combat" round=start.round turn=start.turn}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{#if start.combatant}}
|
|
||||||
<div class="form-group">
|
|
||||||
<span class="label">{{localize "DOCUMENT.Combatant"}}</span>
|
|
||||||
<div class="form-fields">
|
|
||||||
{{localize "EFFECT.START.Combatant" combatant=start.combatantName initiative=start.combatantInitiative}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
</fieldset>
|
|
||||||
{{/if}} --}}
|
|
||||||
|
|
||||||
{{!-- <fieldset>
|
{{formGroup systemFields.duration.fields.type value=source.system.duration.type localize=true }}
|
||||||
<legend>{{localize "EFFECT.DURATION.Header"}}</legend>
|
|
||||||
<div class="form-group slim duration" data-duration>
|
<div class="form-group slim duration-description">
|
||||||
<label class="flexrow" for="{{rootId}}-duration.value">
|
<div class="form-fields">
|
||||||
<span>{{localize "EFFECT.DURATION.Label"}}</span>
|
{{formInput systemFields.duration.fields.description value=source.system.duration.description localize=true }}
|
||||||
<span class="warning" hidden><i class="fa-duotone fa-triangle-exclamation"></i></span>
|
</div>
|
||||||
</label>
|
</div>
|
||||||
<div class="form-fields">
|
|
||||||
{{formInput fields.duration.fields.value value=source.duration.value id=(concat rootId "-duration.value")
|
<div class="custom-duration-section">
|
||||||
aria=(object label=(localize "EFFECT.FIELDS.duration.value.label"))}}
|
{{formGroup fields.start.fields.time value=source.start.time localize=true }}
|
||||||
{{formInput fields.duration.fields.units value=source.duration.units id=(concat rootId "-duration.units")
|
<div class="form-group slim duration" data-duration>
|
||||||
options=durationUnits aria=(object label=(localize "EFFECT.FIELDS.duration.units.label"))}}
|
<label class="flexrow" for="{{rootId}}-duration.value">
|
||||||
|
<span>{{localize "EFFECT.DURATION.Label"}}</span>
|
||||||
|
<span class="warning" hidden><i class="fa-duotone fa-triangle-exclamation"></i></span>
|
||||||
|
</label>
|
||||||
|
<div class="form-fields">
|
||||||
|
{{formInput fields.duration.fields.value value=source.duration.value id=(concat rootId "-duration.value")
|
||||||
|
aria=(object label=(localize "EFFECT.FIELDS.duration.value.label"))}}
|
||||||
|
{{formInput fields.duration.fields.units value=source.duration.units id=(concat rootId "-duration.units")
|
||||||
|
options=durationUnits aria=(object label=(localize "EFFECT.FIELDS.duration.units.label")) localize=true }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{formGroup fields.duration.fields.expiry choices=expiryEvents value=source.duration.expiry rootId=rootId}}
|
</fieldset>
|
||||||
</fieldset> --}}
|
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -16,6 +16,13 @@
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if effect.system.duration.type}}
|
||||||
|
<div class="duration-container">
|
||||||
|
<strong>{{localize "EFFECT.DURATION.Label"}}</strong>
|
||||||
|
<span>{{localize (concat "DAGGERHEART.CONFIG.ActiveEffectDuration." effect.system.duration.type )}}</span>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
{{#unless effect.isLockedCondition}}
|
{{#unless effect.isLockedCondition}}
|
||||||
<p class="close-hint">
|
<p class="close-hint">
|
||||||
<i class="fa-solid fa-computer-mouse"></i> {{localize "DAGGERHEART.UI.EffectsDisplay.removeThing" thing=(localize "DAGGERHEART.GENERAL.Effect.single")}}
|
<i class="fa-solid fa-computer-mouse"></i> {{localize "DAGGERHEART.UI.EffectsDisplay.removeThing" thing=(localize "DAGGERHEART.GENERAL.Effect.single")}}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue