mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-03-07 14:36:13 +01:00
[V14] 1604 - ActiveEffect Durations (#1634)
* Added daggerheart durations and auto expiration of them * Added duration to all tier1 adversaries * Finished all adversaries and environments * Remaining compendiums updated * Improved styling of duration in tooltips * .
This commit is contained in:
parent
e2eb31c12e
commit
4aab5d315a
120 changed files with 2514 additions and 1256 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { refreshIsAllowed } from '../../helpers/utils.mjs';
|
||||
import { expireActiveEffects, refreshIsAllowed } from '../../helpers/utils.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
|
|
@ -264,6 +264,8 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
|||
await feature.update({ 'system.resource.value': resetValue });
|
||||
}
|
||||
|
||||
expireActiveEffects(this.actor, [this.shortRest ? 'shortRest' : 'longRest']);
|
||||
|
||||
this.close();
|
||||
} else {
|
||||
this.render();
|
||||
|
|
|
|||
|
|
@ -166,6 +166,17 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
|||
}));
|
||||
}
|
||||
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':
|
||||
const fields = this.document.system.schema.fields.changes.element.fields;
|
||||
partContext.changes = await Promise.all(
|
||||
|
|
@ -206,4 +217,26 @@ 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');
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
_processFormData(event, form, formData) {
|
||||
const submitData = super._processFormData(event, form, formData);
|
||||
if (submitData.start && !submitData.start.time) submitData.start.time = '0';
|
||||
else if (!submitData) submitData.start = null;
|
||||
|
||||
return submitData;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { refreshIsAllowed } from '../../../helpers/utils.mjs';
|
||||
import { expireActiveEffects, refreshIsAllowed } from '../../../helpers/utils.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
const { AbstractSidebarTab } = foundry.applications.sidebar;
|
||||
|
|
@ -58,6 +58,8 @@ export default class DaggerheartMenu extends HandlebarsApplicationMixin(Abstract
|
|||
const refreshedActors = {};
|
||||
for (let actor of game.actors) {
|
||||
if (['character', 'adversary'].includes(actor.type) && actor.prototypeToken.actorLink) {
|
||||
expireActiveEffects(actor, types);
|
||||
|
||||
const updates = {};
|
||||
for (let item of actor.items) {
|
||||
if (item.system.metadata?.hasResource && refreshIsAllowed(types, item.system.resource?.recovery)) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { AdversaryBPPerEncounter } from '../../config/encounterConfig.mjs';
|
||||
import { expireActiveEffects } from '../../helpers/utils.mjs';
|
||||
|
||||
export default class DhCombatTracker extends foundry.applications.sidebar.tabs.CombatTracker {
|
||||
static DEFAULT_OPTIONS = {
|
||||
|
|
@ -177,6 +178,8 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
|
|||
if (autoPoints) {
|
||||
update.system.actionTokens = Math.max(combatant.system.actionTokens - 1, 0);
|
||||
}
|
||||
|
||||
if (combatant.actor) expireActiveEffects(combatant.actor, [CONFIG.DH.GENERAL.activeEffectDurations.act.id]);
|
||||
}
|
||||
|
||||
await this.viewed.update({
|
||||
|
|
|
|||
|
|
@ -885,3 +885,34 @@ export const activeEffectModes = {
|
|||
label: 'EFFECT.CHANGES.TYPES.override'
|
||||
}
|
||||
};
|
||||
|
||||
export const activeEffectDurations = {
|
||||
temporary: {
|
||||
id: 'temporary',
|
||||
label: 'DAGGERHEART.CONFIG.ActiveEffectDuration.temporary'
|
||||
},
|
||||
act: {
|
||||
id: 'act',
|
||||
label: 'DAGGERHEART.CONFIG.ActiveEffectDuration.act'
|
||||
},
|
||||
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()
|
||||
})
|
||||
),
|
||||
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({
|
||||
enabled: new fields.BooleanField({
|
||||
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({
|
||||
enabled: new fields.BooleanField({
|
||||
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 */
|
||||
/* -------------------------------------------- */
|
||||
|
|
|
|||
|
|
@ -473,6 +473,8 @@ export async function waitForDiceSoNice(message) {
|
|||
}
|
||||
|
||||
export function refreshIsAllowed(allowedTypes, typeToCheck) {
|
||||
if (!allowedTypes) return true;
|
||||
|
||||
switch (typeToCheck) {
|
||||
case CONFIG.DH.GENERAL.refreshTypes.scene.id:
|
||||
case CONFIG.DH.GENERAL.refreshTypes.session.id:
|
||||
|
|
@ -489,6 +491,34 @@ export function refreshIsAllowed(allowedTypes, typeToCheck) {
|
|||
}
|
||||
}
|
||||
|
||||
function expireActiveEffectIsAllowed(allowedTypes, typeToCheck) {
|
||||
if (typeToCheck === CONFIG.DH.GENERAL.activeEffectDurations.act.id) return true;
|
||||
|
||||
return 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 expireActiveEffectIsAllowed(allowedTypes, effect.system.duration.type);
|
||||
})
|
||||
.map(x => x.id);
|
||||
|
||||
actor.deleteEmbeddedDocuments('ActiveEffect', effectsToExpire);
|
||||
}
|
||||
|
||||
export async function getCritDamageBonus(formula) {
|
||||
const critRoll = new Roll(formula);
|
||||
return critRoll.dice.reduce((acc, dice) => acc + dice.faces * dice.number, 0);
|
||||
|
|
@ -503,6 +533,8 @@ export function htmlToText(html) {
|
|||
|
||||
export function getIconVisibleActiveEffects(effects) {
|
||||
return effects.filter(effect => {
|
||||
if (!(effect instanceof game.system.api.documents.DhActiveEffect)) return true;
|
||||
|
||||
const alwaysShown = effect.showIcon === CONST.ACTIVE_EFFECT_SHOW_ICON.ALWAYS;
|
||||
const conditionalShown = effect.showIcon === CONST.ACTIVE_EFFECT_SHOW_ICON.CONDITIONAL && !effect.transfer; // TODO: system specific logic
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue