mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-04-21 23:13:39 +02:00
[Feature] ArmorEffect reworked into ChangeType on BaseEffect (#1739)
* Initial * . * Single armor rework start * More fixes * Fixed DamageReductionDialog * Removed last traces of ArmorEffect * .
This commit is contained in:
parent
0b5de79ca8
commit
b5e0bb7c27
26 changed files with 339 additions and 416 deletions
|
|
@ -43,7 +43,7 @@ CONFIG.Item.dataModels = models.items.config;
|
||||||
|
|
||||||
CONFIG.ActiveEffect.documentClass = documents.DhActiveEffect;
|
CONFIG.ActiveEffect.documentClass = documents.DhActiveEffect;
|
||||||
CONFIG.ActiveEffect.dataModels = models.activeEffects.config;
|
CONFIG.ActiveEffect.dataModels = models.activeEffects.config;
|
||||||
CONFIG.ActiveEffect.changeTypes = { ...CONFIG.ActiveEffect.changeTypes, ...models.activeEffects.changeTypes };
|
CONFIG.ActiveEffect.changeTypes = { ...CONFIG.ActiveEffect.changeTypes, ...models.activeEffects.changeEffects };
|
||||||
|
|
||||||
CONFIG.Combat.documentClass = documents.DhpCombat;
|
CONFIG.Combat.documentClass = documents.DhpCombat;
|
||||||
CONFIG.Combat.dataModels = { base: models.DhCombat };
|
CONFIG.Combat.dataModels = { base: models.DhCombat };
|
||||||
|
|
@ -217,17 +217,6 @@ Hooks.once('init', () => {
|
||||||
label: sheetLabel('DOCUMENT.ActiveEffect')
|
label: sheetLabel('DOCUMENT.ActiveEffect')
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
DocumentSheetConfig.registerSheet(
|
|
||||||
CONFIG.ActiveEffect.documentClass,
|
|
||||||
SYSTEM.id,
|
|
||||||
applications.sheetConfigs.ArmorActiveEffectConfig,
|
|
||||||
{
|
|
||||||
types: ['armor'],
|
|
||||||
makeDefault: true,
|
|
||||||
label: () =>
|
|
||||||
`${game.i18n.localize('TYPES.ActiveEffect.armor')} ${game.i18n.localize('DAGGERHEART.GENERAL.effect')}`
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
game.socket.on(`system.${SYSTEM.id}`, socketRegistration.handleSocketEvent);
|
game.socket.on(`system.${SYSTEM.id}`, socketRegistration.handleSocketEvent);
|
||||||
|
|
||||||
|
|
@ -281,7 +270,6 @@ Hooks.on('setup', () => {
|
||||||
...damageThresholds,
|
...damageThresholds,
|
||||||
'proficiency',
|
'proficiency',
|
||||||
'evasion',
|
'evasion',
|
||||||
'armorScore',
|
|
||||||
'scars',
|
'scars',
|
||||||
'levelData.level.current'
|
'levelData.level.current'
|
||||||
]
|
]
|
||||||
|
|
|
||||||
32
lang/en.json
32
lang/en.json
|
|
@ -16,8 +16,7 @@
|
||||||
"ActiveEffect": {
|
"ActiveEffect": {
|
||||||
"base": "Standard",
|
"base": "Standard",
|
||||||
"beastform": "Beastform",
|
"beastform": "Beastform",
|
||||||
"horde": "Horde",
|
"horde": "Horde"
|
||||||
"armor": "Armor"
|
|
||||||
},
|
},
|
||||||
"Actor": {
|
"Actor": {
|
||||||
"character": "Character",
|
"character": "Character",
|
||||||
|
|
@ -778,8 +777,8 @@
|
||||||
},
|
},
|
||||||
"ArmorInteraction": {
|
"ArmorInteraction": {
|
||||||
"none": { "label": "Ignores Armor" },
|
"none": { "label": "Ignores Armor" },
|
||||||
"active": { "label": "Only Active With Armor" },
|
"active": { "label": "Active w/ Armor" },
|
||||||
"inactive": { "label": "Only Active Without Armor" }
|
"inactive": { "label": "Inactive w/ Armor" }
|
||||||
},
|
},
|
||||||
"ArmorFeature": {
|
"ArmorFeature": {
|
||||||
"burning": {
|
"burning": {
|
||||||
|
|
@ -1864,6 +1863,17 @@
|
||||||
"name": "Healing Roll"
|
"name": "Healing Roll"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ChangeTypes": {
|
||||||
|
"armor": {
|
||||||
|
"newArmorEffect": "Armor Effect",
|
||||||
|
"FIELDS": {
|
||||||
|
"armorInteraction": {
|
||||||
|
"label": "Armor Interaction",
|
||||||
|
"hint": "Does the character wearing armor suppress this effect?"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Duration": {
|
"Duration": {
|
||||||
"passive": "Passive",
|
"passive": "Passive",
|
||||||
"temporary": "Temporary"
|
"temporary": "Temporary"
|
||||||
|
|
@ -1885,15 +1895,6 @@
|
||||||
"Attachments": {
|
"Attachments": {
|
||||||
"attachHint": "Drop items here to attach them",
|
"attachHint": "Drop items here to attach them",
|
||||||
"transferHint": "If checked, this effect will be applied to any actor that owns this Effect's parent Item. The effect is always applied if this Item is attached to another one."
|
"transferHint": "If checked, this effect will be applied to any actor that owns this Effect's parent Item. The effect is always applied if this Item is attached to another one."
|
||||||
},
|
|
||||||
"Armor": {
|
|
||||||
"newArmorEffect": "Armor Effect",
|
|
||||||
"FIELDS": {
|
|
||||||
"armorInteraction": {
|
|
||||||
"label": "Armor Interaction",
|
|
||||||
"hint": "Does the character wearing armor suppress this effect?"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"GENERAL": {
|
"GENERAL": {
|
||||||
|
|
@ -3079,10 +3080,7 @@
|
||||||
"tokenActorsMissing": "[{names}] missing Actors",
|
"tokenActorsMissing": "[{names}] missing Actors",
|
||||||
"domainTouchRequirement": "This domain card requires {nr} {domain} cards in the loadout to be used",
|
"domainTouchRequirement": "This domain card requires {nr} {domain} cards in the loadout to be used",
|
||||||
"knowTheTide": "Know The Tide gained a token",
|
"knowTheTide": "Know The Tide gained a token",
|
||||||
"lackingItemTransferPermission": "User {user} lacks owner permission needed to transfer items to {target}",
|
"lackingItemTransferPermission": "User {user} lacks owner permission needed to transfer items to {target}"
|
||||||
"cannotAlterArmorEffectChanges": "You cannot alter the changes length of an armor effect",
|
|
||||||
"cannotAlterArmorEffectType": "You cannot alter the type of armor effect changes",
|
|
||||||
"cannotAlterArmorEffectKey": "You cannot alter they key of armor effect changes"
|
|
||||||
},
|
},
|
||||||
"Progress": {
|
"Progress": {
|
||||||
"migrationLabel": "Performing system migration. Please wait and do not close Foundry."
|
"migrationLabel": "Performing system migration. Please wait and do not close Foundry."
|
||||||
|
|
|
||||||
|
|
@ -21,13 +21,12 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
|
||||||
this.rulesDefault
|
this.rulesDefault
|
||||||
);
|
);
|
||||||
|
|
||||||
const allArmorEffects = Array.from(actor.allApplicableEffects()).filter(x => x.type === 'armor');
|
const allArmorEffects = Array.from(actor.allApplicableEffects()).filter(x => x.system.armorData);
|
||||||
const orderedArmorEffects = game.system.api.data.activeEffects.ArmorEffect.orderEffectsForAutoChange(
|
const orderedArmorEffects = game.system.api.data.activeEffects.changeTypes.armor.orderEffectsForAutoChange(
|
||||||
allArmorEffects,
|
allArmorEffects,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
const armor = orderedArmorEffects.reduce((acc, effect) => {
|
const armor = orderedArmorEffects.reduce((acc, effect) => {
|
||||||
if (effect.type !== 'armor') return acc;
|
|
||||||
const { value, max } = effect.system.armorData;
|
const { value, max } = effect.system.armorData;
|
||||||
acc.push({
|
acc.push({
|
||||||
effect: effect,
|
effect: effect,
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ export { default as CompanionSettings } from './companion-settings.mjs';
|
||||||
export { default as SettingActiveEffectConfig } from './setting-active-effect-config.mjs';
|
export { default as SettingActiveEffectConfig } from './setting-active-effect-config.mjs';
|
||||||
export { default as SettingFeatureConfig } from './setting-feature-config.mjs';
|
export { default as SettingFeatureConfig } from './setting-feature-config.mjs';
|
||||||
export { default as EnvironmentSettings } from './environment-settings.mjs';
|
export { default as EnvironmentSettings } from './environment-settings.mjs';
|
||||||
export { default as ArmorActiveEffectConfig } from './armorActiveEffectConfig.mjs';
|
|
||||||
export { default as ActiveEffectConfig } from './activeEffectConfig.mjs';
|
export { default as ActiveEffectConfig } from './activeEffectConfig.mjs';
|
||||||
export { default as DhTokenConfig } from './token-config.mjs';
|
export { default as DhTokenConfig } from './token-config.mjs';
|
||||||
export { default as DhPrototypeTokenConfig } from './prototype-token-config.mjs';
|
export { default as DhPrototypeTokenConfig } from './prototype-token-config.mjs';
|
||||||
|
|
|
||||||
|
|
@ -150,6 +150,10 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
||||||
minLength: 0
|
minLength: 0
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
htmlElement
|
||||||
|
.querySelector('.armor-change-checkbox')
|
||||||
|
?.addEventListener('change', this.armorChangeToggle.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
async _prepareContext(options) {
|
async _prepareContext(options) {
|
||||||
|
|
@ -187,38 +191,74 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
||||||
break;
|
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;
|
||||||
|
|
||||||
|
const singleTypes = ['armor'];
|
||||||
|
const { base, ...typedChanges } = context.source.changes.reduce((acc, change, index) => {
|
||||||
|
const type = CONFIG.DH.GENERAL.baseActiveEffectModes[change.type] ? 'base' : change.type;
|
||||||
|
if (singleTypes.includes(type)) {
|
||||||
|
acc[type] = { ...change, index };
|
||||||
|
} else {
|
||||||
|
if (!acc[type]) acc[type] = [];
|
||||||
|
acc[type].push({ ...change, index });
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
partContext.changes = await Promise.all(
|
partContext.changes = await Promise.all(
|
||||||
foundry.utils
|
foundry.utils.deepClone(base ?? []).map(c => this._prepareChangeContext(c, fields))
|
||||||
.deepClone(context.source.changes)
|
|
||||||
.map((c, i) => this._prepareChangeContext(c, i, fields))
|
|
||||||
);
|
);
|
||||||
|
partContext.typedChanges = typedChanges;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return partContext;
|
return partContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
_prepareChangeContext(change, index, fields) {
|
armorChangeToggle(event) {
|
||||||
|
if (event.target.checked) {
|
||||||
|
this.addArmorChange();
|
||||||
|
} else {
|
||||||
|
this.removeTypedChange(event.target.dataset.index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Could be generalised if needed later */
|
||||||
|
addArmorChange() {
|
||||||
|
const submitData = this._processFormData(null, this.form, new FormDataExtended(this.form));
|
||||||
|
const changes = Object.values(submitData.system?.changes ?? {});
|
||||||
|
changes.push(game.system.api.data.activeEffects.changeTypes.armor.getInitialValue());
|
||||||
|
return this.submit({ updateData: { system: { changes } } });
|
||||||
|
}
|
||||||
|
|
||||||
|
removeTypedChange(indexString) {
|
||||||
|
const submitData = this._processFormData(null, this.form, new FormDataExtended(this.form));
|
||||||
|
const changes = Object.values(submitData.system.changes);
|
||||||
|
const index = Number(indexString);
|
||||||
|
changes.splice(index, 1);
|
||||||
|
return this.submit({ updateData: { system: { changes } } });
|
||||||
|
}
|
||||||
|
|
||||||
|
_prepareChangeContext(change, fields) {
|
||||||
if (typeof change.value !== 'string') change.value = JSON.stringify(change.value);
|
if (typeof change.value !== 'string') change.value = JSON.stringify(change.value);
|
||||||
const defaultPriority = game.system.api.documents.DhActiveEffect.CHANGE_TYPES[change.type]?.defaultPriority;
|
const defaultPriority = game.system.api.documents.DhActiveEffect.CHANGE_TYPES[change.type]?.defaultPriority;
|
||||||
Object.assign(
|
Object.assign(
|
||||||
change,
|
change,
|
||||||
['key', 'type', 'value', 'priority'].reduce((paths, fieldName) => {
|
['key', 'type', 'value', 'priority'].reduce((paths, fieldName) => {
|
||||||
paths[`${fieldName}Path`] = `system.changes.${index}.${fieldName}`;
|
paths[`${fieldName}Path`] = `system.changes.${change.index}.${fieldName}`;
|
||||||
return paths;
|
return paths;
|
||||||
}, {})
|
}, {})
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
game.system.api.documents.DhActiveEffect.CHANGE_TYPES[change.type].render?.(
|
game.system.api.documents.DhActiveEffect.CHANGE_TYPES[change.type].render?.(
|
||||||
change,
|
change,
|
||||||
index,
|
change.index,
|
||||||
defaultPriority
|
defaultPriority
|
||||||
) ??
|
) ??
|
||||||
foundry.applications.handlebars.renderTemplate(
|
foundry.applications.handlebars.renderTemplate(
|
||||||
'systems/daggerheart/templates/sheets/activeEffect/change.hbs',
|
'systems/daggerheart/templates/sheets/activeEffect/change.hbs',
|
||||||
{
|
{
|
||||||
change,
|
change,
|
||||||
index,
|
index: change.index,
|
||||||
defaultPriority,
|
defaultPriority,
|
||||||
fields
|
fields
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,67 +0,0 @@
|
||||||
const { HandlebarsApplicationMixin, DocumentSheetV2 } = foundry.applications.api;
|
|
||||||
|
|
||||||
export default class ArmorActiveEffectConfig extends HandlebarsApplicationMixin(DocumentSheetV2) {
|
|
||||||
static DEFAULT_OPTIONS = {
|
|
||||||
tag: 'form',
|
|
||||||
classes: ['daggerheart', 'sheet', 'dh-style', 'active-effect-config', 'armor-effect-config'],
|
|
||||||
form: {
|
|
||||||
handler: this.updateForm,
|
|
||||||
submitOnChange: true,
|
|
||||||
closeOnSubmit: false
|
|
||||||
},
|
|
||||||
position: { width: 560 },
|
|
||||||
actions: {
|
|
||||||
finish: ArmorActiveEffectConfig.#finish
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static PARTS = {
|
|
||||||
header: { template: 'systems/daggerheart/templates/sheets/activeEffect/header.hbs' },
|
|
||||||
tabs: { template: 'templates/generic/tab-navigation.hbs' },
|
|
||||||
details: { template: 'systems/daggerheart/templates/sheets/activeEffect/armor/details.hbs' },
|
|
||||||
settings: { template: 'systems/daggerheart/templates/sheets/activeEffect/armor/settings.hbs' },
|
|
||||||
footer: { template: 'systems/daggerheart/templates/sheets/activeEffect/armor/footer.hbs' }
|
|
||||||
};
|
|
||||||
|
|
||||||
static TABS = {
|
|
||||||
sheet: {
|
|
||||||
tabs: [
|
|
||||||
{ id: 'details', icon: 'fa-solid fa-book' },
|
|
||||||
{ id: 'settings', icon: 'fa-solid fa-bars', label: 'DAGGERHEART.GENERAL.Tabs.settings' }
|
|
||||||
],
|
|
||||||
initial: 'details',
|
|
||||||
labelPrefix: 'EFFECT.TABS'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
async _prepareContext(options) {
|
|
||||||
const context = await super._prepareContext(options);
|
|
||||||
context.systemFields = context.document.system.schema.fields;
|
|
||||||
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @inheritDoc */
|
|
||||||
async _preparePartContext(partId, context) {
|
|
||||||
const partContext = await super._preparePartContext(partId, context);
|
|
||||||
if (partId in partContext.tabs) partContext.tab = partContext.tabs[partId];
|
|
||||||
|
|
||||||
switch (partId) {
|
|
||||||
case 'details':
|
|
||||||
partContext.isActorEffect = this.document.parent?.documentName === 'Actor';
|
|
||||||
partContext.isItemEffect = this.document.parent?.documentName === 'Item';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return partContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async updateForm(_event, _form, formData) {
|
|
||||||
await this.document.update(formData.object);
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
static #finish() {
|
|
||||||
this.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -966,10 +966,13 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
const armorSources = [];
|
const armorSources = [];
|
||||||
for (var effect of Array.from(this.document.allApplicableEffects())) {
|
for (var effect of Array.from(this.document.allApplicableEffects())) {
|
||||||
const origin = effect.origin ? await foundry.utils.fromUuid(effect.origin) : effect.parent;
|
const origin = effect.origin ? await foundry.utils.fromUuid(effect.origin) : effect.parent;
|
||||||
if (effect.type !== 'armor' || effect.disabled || effect.isSuppressed) continue;
|
if (!effect.system.armorData || effect.disabled || effect.isSuppressed) continue;
|
||||||
|
|
||||||
|
const originIsActor = origin instanceof Actor;
|
||||||
|
const name = originIsActor ? effect.name : origin.name;
|
||||||
armorSources.push({
|
armorSources.push({
|
||||||
uuid: effect.uuid,
|
uuid: effect.uuid,
|
||||||
name: origin.name,
|
name,
|
||||||
...effect.system.armorData
|
...effect.system.armorData
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -1018,15 +1021,14 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
/** Update specific armor source */
|
/** Update specific armor source */
|
||||||
static async armorSourceUpdate(event) {
|
static async armorSourceUpdate(event) {
|
||||||
const effect = await foundry.utils.fromUuid(event.target.dataset.uuid);
|
const effect = await foundry.utils.fromUuid(event.target.dataset.uuid);
|
||||||
if (effect.system.changes.length !== 1) return;
|
const armorChange = effect.system.armorChange;
|
||||||
|
if (!armorChange) return;
|
||||||
const value = Math.max(Math.min(Number.parseInt(event.target.value), effect.system.armorData.max), 0);
|
const value = Math.max(Math.min(Number.parseInt(event.target.value), effect.system.armorData.max), 0);
|
||||||
|
|
||||||
const newChanges = [
|
const newChanges = effect.system.changes.map(change => ({
|
||||||
{
|
...change,
|
||||||
...effect.system.changes[0],
|
value: change.type === 'armor' ? value : change.value
|
||||||
value
|
}));
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
event.target.value = value;
|
event.target.value = value;
|
||||||
const progressBar = event.target.closest('.status-bar.armor-slots').querySelector('progress');
|
const progressBar = event.target.closest('.status-bar.armor-slots').querySelector('progress');
|
||||||
|
|
@ -1038,19 +1040,19 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
static async armorSourcePipUpdate(event) {
|
static async armorSourcePipUpdate(event) {
|
||||||
const target = event.target.closest('.armor-slot');
|
const target = event.target.closest('.armor-slot');
|
||||||
const effect = await foundry.utils.fromUuid(target.dataset.uuid);
|
const effect = await foundry.utils.fromUuid(target.dataset.uuid);
|
||||||
if (effect.system.changes.length !== 1) return;
|
const armorChange = effect.system.armorChange;
|
||||||
const { value, max } = effect.system.armorData;
|
if (!armorChange) return;
|
||||||
|
|
||||||
|
const { value } = effect.system.armorData;
|
||||||
|
|
||||||
const inputValue = Number.parseInt(target.dataset.value);
|
const inputValue = Number.parseInt(target.dataset.value);
|
||||||
const decreasing = value >= inputValue;
|
const decreasing = value >= inputValue;
|
||||||
const newValue = decreasing ? inputValue - 1 : inputValue;
|
const newValue = decreasing ? inputValue - 1 : inputValue;
|
||||||
|
|
||||||
const newChanges = [
|
const newChanges = effect.system.changes.map(change => ({
|
||||||
{
|
...change,
|
||||||
...effect.system.changes[0],
|
value: change.type === 'armor' ? newValue : change.value
|
||||||
value: newValue
|
}));
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const container = target.closest('.slot-bar');
|
const container = target.closest('.slot-bar');
|
||||||
for (const armorSlot of container.querySelectorAll('.armor-slot i')) {
|
for (const armorSlot of container.querySelectorAll('.armor-slot i')) {
|
||||||
|
|
|
||||||
|
|
@ -762,10 +762,6 @@ export default function DHApplicationMixin(Base) {
|
||||||
data.system.domain = parent.system.domains[0];
|
data.system.domain = parent.system.domains[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (documentClass === 'ActiveEffect') {
|
|
||||||
return cls.createDialog(data, { parent: this.document });
|
|
||||||
}
|
|
||||||
|
|
||||||
const doc = await cls.create(data, { parent, renderSheet: !event.shiftKey });
|
const doc = await cls.create(data, { parent, renderSheet: !event.shiftKey });
|
||||||
if (parentIsItem && type === 'feature') {
|
if (parentIsItem && type === 'feature') {
|
||||||
await this.document.update({
|
await this.document.update({
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ export default class ArmorSheet extends ItemAttachmentSheet(DHBaseItemSheet) {
|
||||||
const armorEffect = this.document.system.armorEffect;
|
const armorEffect = this.document.system.armorEffect;
|
||||||
if (Number.isNaN(value) || !armorEffect) return;
|
if (Number.isNaN(value) || !armorEffect) return;
|
||||||
|
|
||||||
await armorEffect.system.updateArmorMax(value);
|
await armorEffect.system.armorChange.typeData.updateArmorMax(value);
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -959,12 +959,7 @@ export const sceneRangeMeasurementSetting = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const activeEffectModes = {
|
export const baseActiveEffectModes = {
|
||||||
armor: {
|
|
||||||
id: 'armor',
|
|
||||||
priority: 20,
|
|
||||||
label: 'TYPES.ActiveEffect.armor'
|
|
||||||
},
|
|
||||||
custom: {
|
custom: {
|
||||||
id: 'custom',
|
id: 'custom',
|
||||||
priority: 0,
|
priority: 0,
|
||||||
|
|
@ -1002,6 +997,15 @@ export const activeEffectModes = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const activeEffectModes = {
|
||||||
|
armor: {
|
||||||
|
id: 'armor',
|
||||||
|
priority: 20,
|
||||||
|
label: 'TYPES.ActiveEffect.armor'
|
||||||
|
},
|
||||||
|
...baseActiveEffectModes
|
||||||
|
};
|
||||||
|
|
||||||
export const activeEffectArmorInteraction = {
|
export const activeEffectArmorInteraction = {
|
||||||
none: { id: 'none', label: 'DAGGERHEART.CONFIG.ArmorInteraction.none.label' },
|
none: { id: 'none', label: 'DAGGERHEART.CONFIG.ArmorInteraction.none.label' },
|
||||||
active: { id: 'active', label: 'DAGGERHEART.CONFIG.ArmorInteraction.active.label' },
|
active: { id: 'active', label: 'DAGGERHEART.CONFIG.ArmorInteraction.active.label' },
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,12 @@
|
||||||
import BaseEffect from './baseEffect.mjs';
|
import BaseEffect from './baseEffect.mjs';
|
||||||
import BeastformEffect from './beastformEffect.mjs';
|
import BeastformEffect from './beastformEffect.mjs';
|
||||||
import HordeEffect from './hordeEffect.mjs';
|
import HordeEffect from './hordeEffect.mjs';
|
||||||
import ArmorEffect from './armorEffect.mjs';
|
export { changeTypes, changeEffects } from './changeTypes/_module.mjs';
|
||||||
|
|
||||||
export { BaseEffect, BeastformEffect, HordeEffect, ArmorEffect };
|
export { BaseEffect, BeastformEffect, HordeEffect };
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
base: BaseEffect,
|
base: BaseEffect,
|
||||||
beastform: BeastformEffect,
|
beastform: BeastformEffect,
|
||||||
horde: HordeEffect,
|
horde: HordeEffect
|
||||||
armor: ArmorEffect
|
|
||||||
};
|
|
||||||
|
|
||||||
export const changeTypes = {
|
|
||||||
armor: ArmorEffect.armorChangeEffect
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,244 +0,0 @@
|
||||||
import { getScrollTextData, itemAbleRollParse } from '../../helpers/utils.mjs';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ArmorEffects are ActiveEffects that have a static changes field of length 1. It includes current and maximum armor.
|
|
||||||
* When applied to a character, it adds to their currently marked and maximum armor.
|
|
||||||
*/
|
|
||||||
export default class ArmorEffect extends foundry.data.ActiveEffectTypeDataModel {
|
|
||||||
static defineSchema() {
|
|
||||||
const fields = foundry.data.fields;
|
|
||||||
|
|
||||||
return {
|
|
||||||
...super.defineSchema(),
|
|
||||||
changes: new fields.ArrayField(
|
|
||||||
new fields.SchemaField({
|
|
||||||
key: new fields.StringField({
|
|
||||||
required: true,
|
|
||||||
nullable: false,
|
|
||||||
initial: 'system.armorScore'
|
|
||||||
}),
|
|
||||||
type: new fields.StringField({
|
|
||||||
required: true,
|
|
||||||
blank: false,
|
|
||||||
initial: CONFIG.DH.GENERAL.activeEffectModes.armor.id,
|
|
||||||
validate: ArmorEffect.#validateType
|
|
||||||
}),
|
|
||||||
phase: new fields.StringField({ required: true, blank: false, initial: 'initial' }),
|
|
||||||
priority: new fields.NumberField({ integer: true, initial: 20 }),
|
|
||||||
value: new fields.NumberField({
|
|
||||||
required: true,
|
|
||||||
integer: true,
|
|
||||||
initial: 0,
|
|
||||||
min: 0,
|
|
||||||
label: 'DAGGERHEART.GENERAL.value'
|
|
||||||
}),
|
|
||||||
max: new fields.StringField({
|
|
||||||
required: true,
|
|
||||||
nullable: false,
|
|
||||||
initial: '1',
|
|
||||||
label: 'DAGGERHEART.GENERAL.max'
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
initial: [
|
|
||||||
{
|
|
||||||
key: 'system.armorScore',
|
|
||||||
type: CONFIG.DH.GENERAL.activeEffectModes.armor.id,
|
|
||||||
phase: 'initial',
|
|
||||||
priority: 20,
|
|
||||||
value: 0,
|
|
||||||
max: '1'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
),
|
|
||||||
armorInteraction: new fields.StringField({
|
|
||||||
required: true,
|
|
||||||
choices: CONFIG.DH.GENERAL.activeEffectArmorInteraction,
|
|
||||||
initial: CONFIG.DH.GENERAL.activeEffectArmorInteraction.none.id,
|
|
||||||
label: 'DAGGERHEART.EFFECTS.Armor.FIELDS.armorInteraction.label',
|
|
||||||
hint: 'DAGGERHEART.EFFECTS.Armor.FIELDS.armorInteraction.hint'
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
get isSuppressed() {
|
|
||||||
if (this.parent.actor?.type !== 'character') return false;
|
|
||||||
|
|
||||||
switch (this.armorInteraction) {
|
|
||||||
case CONFIG.DH.GENERAL.activeEffectArmorInteraction.active.id:
|
|
||||||
return !this.parent.actor.system.armor;
|
|
||||||
case CONFIG.DH.GENERAL.activeEffectArmorInteraction.inactive.id:
|
|
||||||
return Boolean(this.parent.actor.system.armor);
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Type Functions */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate that an {@link EffectChangeData#type} string is well-formed.
|
|
||||||
* @param {string} type The string to be validated
|
|
||||||
* @returns {true}
|
|
||||||
* @throws {Error} An error if the type string is malformed
|
|
||||||
*/
|
|
||||||
static #validateType(type) {
|
|
||||||
if (type !== CONFIG.DH.GENERAL.activeEffectModes.armor.id)
|
|
||||||
throw new Error('An armor effect must have change.type "armor"');
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static armorChangeEffect = {
|
|
||||||
label: 'Armor',
|
|
||||||
defaultPriortiy: 20,
|
|
||||||
handler: (actor, change, _options, _field, replacementData) => {
|
|
||||||
game.system.api.documents.DhActiveEffect.applyChange(
|
|
||||||
actor,
|
|
||||||
{
|
|
||||||
...change,
|
|
||||||
key: 'system.armorScore.value',
|
|
||||||
type: CONFIG.DH.GENERAL.activeEffectModes.add.id,
|
|
||||||
value: change.value
|
|
||||||
},
|
|
||||||
replacementData
|
|
||||||
);
|
|
||||||
game.system.api.documents.DhActiveEffect.applyChange(
|
|
||||||
actor,
|
|
||||||
{
|
|
||||||
...change,
|
|
||||||
key: 'system.armorScore.max',
|
|
||||||
type: CONFIG.DH.GENERAL.activeEffectModes.add.id,
|
|
||||||
value: change.max
|
|
||||||
},
|
|
||||||
replacementData
|
|
||||||
);
|
|
||||||
return {};
|
|
||||||
},
|
|
||||||
render: null
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Helpers */
|
|
||||||
|
|
||||||
get armorChange() {
|
|
||||||
if (this.changes.length !== 1)
|
|
||||||
throw new Error('Unexpected error. An armor effect should have a changes field of length 1.');
|
|
||||||
|
|
||||||
const actor = this.parent.actor?.type === 'character' ? this.parent.actor : null;
|
|
||||||
const changeData = this.changes[0];
|
|
||||||
const maxParse = actor ? itemAbleRollParse(changeData.max, actor, this.parent.parent) : null;
|
|
||||||
const maxRoll = maxParse ? new Roll(maxParse).evaluateSync() : null;
|
|
||||||
const maxEvaluated = maxRoll ? (maxRoll.isDeterministic ? maxRoll.total : null) : null;
|
|
||||||
|
|
||||||
return {
|
|
||||||
...changeData,
|
|
||||||
max: maxEvaluated ?? changeData.max
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
get armorData() {
|
|
||||||
return { value: this.armorChange.value, max: this.armorChange.max };
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateArmorMax(newMax) {
|
|
||||||
const { effect, ...baseChange } = this.armorChange;
|
|
||||||
const newChanges = [
|
|
||||||
{
|
|
||||||
...baseChange,
|
|
||||||
max: newMax,
|
|
||||||
value: Math.min(this.armorChange.value, newMax)
|
|
||||||
}
|
|
||||||
];
|
|
||||||
await this.parent.update({ 'system.changes': newChanges });
|
|
||||||
}
|
|
||||||
|
|
||||||
static orderEffectsForAutoChange(armorEffects, increasing) {
|
|
||||||
const getEffectWeight = effect => {
|
|
||||||
switch (effect.parent.type) {
|
|
||||||
case 'class':
|
|
||||||
case 'subclass':
|
|
||||||
case 'ancestry':
|
|
||||||
case 'community':
|
|
||||||
case 'feature':
|
|
||||||
case 'domainCard':
|
|
||||||
return 2;
|
|
||||||
case 'armor':
|
|
||||||
return 3;
|
|
||||||
case 'loot':
|
|
||||||
case 'consumable':
|
|
||||||
return 4;
|
|
||||||
case 'weapon':
|
|
||||||
return 5;
|
|
||||||
case 'character':
|
|
||||||
return 6;
|
|
||||||
default:
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return armorEffects
|
|
||||||
.filter(x => !x.disabled && !x.isSuppressed)
|
|
||||||
.sort((a, b) =>
|
|
||||||
increasing ? getEffectWeight(b) - getEffectWeight(a) : getEffectWeight(a) - getEffectWeight(b)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Overrides */
|
|
||||||
|
|
||||||
static getDefaultObject() {
|
|
||||||
return {
|
|
||||||
key: 'system.armorScore',
|
|
||||||
type: 'armor',
|
|
||||||
name: game.i18n.localize('DAGGERHEART.EFFECTS.Armor.newArmorEffect'),
|
|
||||||
img: 'icons/equipment/chest/breastplate-helmet-metal.webp'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async _preUpdate(changes, options, user) {
|
|
||||||
const allowed = await super._preUpdate(changes, options, user);
|
|
||||||
if (allowed === false) return false;
|
|
||||||
|
|
||||||
if (changes.system?.changes) {
|
|
||||||
const changesChanged = changes.system.changes.length !== this.changes.length;
|
|
||||||
if (changesChanged) {
|
|
||||||
ui.notifications.error(
|
|
||||||
game.i18n.localize('DAGGERHEART.UI.Notifications.cannotAlterArmorEffectChanges')
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changes.system.changes.length === 1) {
|
|
||||||
if (changes.system.changes[0].type !== CONFIG.DH.GENERAL.activeEffectModes.armor.id) {
|
|
||||||
ui.notifications.error(
|
|
||||||
game.i18n.localize('DAGGERHEART.UI.Notifications.cannotAlterArmorEffectType')
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changes.system.changes[0].key !== 'system.armorScore') {
|
|
||||||
ui.notifications.error(
|
|
||||||
game.i18n.localize('DAGGERHEART.UI.Notifications.cannotAlterArmorEffectKey')
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
changes.system.changes[0].value !== this.armorChange.value &&
|
|
||||||
this.parent.actor?.type === 'character'
|
|
||||||
) {
|
|
||||||
options.scrollingTextData = [
|
|
||||||
getScrollTextData(this.parent.actor, changes.system.changes[0], 'armor')
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_onUpdate(changes, options, userId) {
|
|
||||||
super._onUpdate(changes, options, userId);
|
|
||||||
|
|
||||||
if (options.scrollingTextData && this.parent.actor?.type === 'character')
|
|
||||||
this.parent.actor.queueScrollText(options.scrollingTextData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -12,6 +12,8 @@
|
||||||
* "Anything that uses another data model value as its value": +1 - Effects that increase traits have to be calculated first at Base priority. (EX: Raise evasion by half your agility)
|
* "Anything that uses another data model value as its value": +1 - Effects that increase traits have to be calculated first at Base priority. (EX: Raise evasion by half your agility)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { changeTypes } from './_module.mjs';
|
||||||
|
|
||||||
export default class BaseEffect extends foundry.data.ActiveEffectTypeDataModel {
|
export default class BaseEffect extends foundry.data.ActiveEffectTypeDataModel {
|
||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
@ -30,7 +32,8 @@ export default class BaseEffect extends foundry.data.ActiveEffectTypeDataModel {
|
||||||
}),
|
}),
|
||||||
value: new fields.AnyField({ required: true, nullable: true, serializable: true, initial: '' }),
|
value: new fields.AnyField({ required: true, nullable: true, serializable: true, initial: '' }),
|
||||||
phase: new fields.StringField({ required: true, blank: false, initial: 'initial' }),
|
phase: new fields.StringField({ required: true, blank: false, initial: 'initial' }),
|
||||||
priority: new fields.NumberField()
|
priority: new fields.NumberField(),
|
||||||
|
typeData: new fields.TypedSchemaField(changeTypes, { nullable: true, initial: null })
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
duration: new fields.SchemaField({
|
duration: new fields.SchemaField({
|
||||||
|
|
@ -86,6 +89,17 @@ export default class BaseEffect extends foundry.data.ActiveEffectTypeDataModel {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get armorChange() {
|
||||||
|
return this.changes.find(x => x.type === CONFIG.DH.GENERAL.activeEffectModes.armor.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
get armorData() {
|
||||||
|
const armorChange = this.armorChange;
|
||||||
|
if (!armorChange) return null;
|
||||||
|
|
||||||
|
return armorChange.typeData.getArmorData(armorChange);
|
||||||
|
}
|
||||||
|
|
||||||
static getDefaultObject() {
|
static getDefaultObject() {
|
||||||
return {
|
return {
|
||||||
name: 'New Effect',
|
name: 'New Effect',
|
||||||
|
|
|
||||||
9
module/data/activeEffect/changeTypes/_module.mjs
Normal file
9
module/data/activeEffect/changeTypes/_module.mjs
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import Armor from './armor.mjs';
|
||||||
|
|
||||||
|
export const changeEffects = {
|
||||||
|
armor: Armor.changeEffect
|
||||||
|
};
|
||||||
|
|
||||||
|
export const changeTypes = {
|
||||||
|
armor: Armor
|
||||||
|
};
|
||||||
145
module/data/activeEffect/changeTypes/armor.mjs
Normal file
145
module/data/activeEffect/changeTypes/armor.mjs
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
import { itemAbleRollParse } from '../../../helpers/utils.mjs';
|
||||||
|
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
|
export default class Armor extends foundry.abstract.DataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
return {
|
||||||
|
type: new fields.StringField({ required: true, initial: 'armor', blank: false }),
|
||||||
|
max: new fields.StringField({
|
||||||
|
required: true,
|
||||||
|
nullable: false,
|
||||||
|
initial: '1',
|
||||||
|
label: 'DAGGERHEART.GENERAL.max'
|
||||||
|
}),
|
||||||
|
armorInteraction: new fields.StringField({
|
||||||
|
required: true,
|
||||||
|
choices: CONFIG.DH.GENERAL.activeEffectArmorInteraction,
|
||||||
|
initial: CONFIG.DH.GENERAL.activeEffectArmorInteraction.none.id,
|
||||||
|
label: 'DAGGERHEART.EFFECTS.ChangeTypes.armor.FIELDS.armorInteraction.label',
|
||||||
|
hint: 'DAGGERHEART.EFFECTS.ChangeTypes.armor.FIELDS.armorInteraction.hint'
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static changeEffect = {
|
||||||
|
label: 'Armor',
|
||||||
|
defaultPriortiy: 20,
|
||||||
|
handler: (actor, change, _options, _field, replacementData) => {
|
||||||
|
game.system.api.documents.DhActiveEffect.applyChange(
|
||||||
|
actor,
|
||||||
|
{
|
||||||
|
...change,
|
||||||
|
key: 'system.armorScore.value',
|
||||||
|
type: CONFIG.DH.GENERAL.activeEffectModes.add.id,
|
||||||
|
value: change.value
|
||||||
|
},
|
||||||
|
replacementData
|
||||||
|
);
|
||||||
|
game.system.api.documents.DhActiveEffect.applyChange(
|
||||||
|
actor,
|
||||||
|
{
|
||||||
|
...change,
|
||||||
|
key: 'system.armorScore.max',
|
||||||
|
type: CONFIG.DH.GENERAL.activeEffectModes.add.id,
|
||||||
|
value: change.typeData.max
|
||||||
|
},
|
||||||
|
replacementData
|
||||||
|
);
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
render: null
|
||||||
|
};
|
||||||
|
|
||||||
|
get isSuppressed() {
|
||||||
|
switch (this.armorInteraction) {
|
||||||
|
case CONFIG.DH.GENERAL.activeEffectArmorInteraction.active.id:
|
||||||
|
return !this.parent.parent?.actor.system.armor;
|
||||||
|
case CONFIG.DH.GENERAL.activeEffectArmorInteraction.inactive.id:
|
||||||
|
return Boolean(this.parent.parent?.actor.system.armor);
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static getInitialValue(locked) {
|
||||||
|
return {
|
||||||
|
key: 'Armor',
|
||||||
|
type: CONFIG.DH.GENERAL.activeEffectModes.armor.id,
|
||||||
|
value: 0,
|
||||||
|
typeData: {
|
||||||
|
type: 'armor',
|
||||||
|
max: 0,
|
||||||
|
locked
|
||||||
|
},
|
||||||
|
phase: 'initial',
|
||||||
|
priority: 20
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static getDefaultArmorEffect() {
|
||||||
|
return {
|
||||||
|
name: game.i18n.localize('DAGGERHEART.EFFECTS.ChangeTypes.armor.newArmorEffect'),
|
||||||
|
img: 'icons/equipment/chest/breastplate-helmet-metal.webp',
|
||||||
|
system: {
|
||||||
|
changes: [Armor.getInitialValue(true)]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helpers */
|
||||||
|
|
||||||
|
getArmorData(parentChange) {
|
||||||
|
const actor = this.parent.parent?.actor?.type === 'character' ? this.parent.parent.actor : null;
|
||||||
|
const maxParse = actor ? itemAbleRollParse(this.max, actor, this.parent.parent.parent) : null;
|
||||||
|
const maxRoll = maxParse ? new Roll(maxParse).evaluateSync() : null;
|
||||||
|
const maxEvaluated = maxRoll ? (maxRoll.isDeterministic ? maxRoll.total : null) : null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
value: parentChange.value,
|
||||||
|
max: maxEvaluated ?? this.max
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateArmorMax(newMax) {
|
||||||
|
const newChanges = [
|
||||||
|
...this.parent.changes.map(change => ({
|
||||||
|
...change,
|
||||||
|
value: change.type === 'armor' ? Math.min(change.value, newMax) : change.value,
|
||||||
|
typeData: change.type === 'armor' ? { ...change.typeData, max: newMax } : change.typeData
|
||||||
|
}))
|
||||||
|
];
|
||||||
|
await this.parent.parent.update({ 'system.changes': newChanges });
|
||||||
|
}
|
||||||
|
|
||||||
|
static orderEffectsForAutoChange(armorEffects, increasing) {
|
||||||
|
const getEffectWeight = effect => {
|
||||||
|
switch (effect.parent.type) {
|
||||||
|
case 'class':
|
||||||
|
case 'subclass':
|
||||||
|
case 'ancestry':
|
||||||
|
case 'community':
|
||||||
|
case 'feature':
|
||||||
|
case 'domainCard':
|
||||||
|
return 2;
|
||||||
|
case 'armor':
|
||||||
|
return 3;
|
||||||
|
case 'loot':
|
||||||
|
case 'consumable':
|
||||||
|
return 4;
|
||||||
|
case 'weapon':
|
||||||
|
return 5;
|
||||||
|
case 'character':
|
||||||
|
return 6;
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return armorEffects
|
||||||
|
.filter(x => !x.disabled && !x.isSuppressed)
|
||||||
|
.sort((a, b) =>
|
||||||
|
increasing ? getEffectWeight(b) - getEffectWeight(a) : getEffectWeight(a) - getEffectWeight(b)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -469,8 +469,8 @@ export default class DhCharacter extends DhCreature {
|
||||||
|
|
||||||
const increasing = armorChange >= 0;
|
const increasing = armorChange >= 0;
|
||||||
let remainingChange = Math.abs(armorChange);
|
let remainingChange = Math.abs(armorChange);
|
||||||
const armorEffects = Array.from(this.parent.allApplicableEffects()).filter(x => x.type === 'armor');
|
const armorEffects = Array.from(this.parent.allApplicableEffects()).filter(x => x.system.armorData);
|
||||||
const orderedEffects = game.system.api.data.activeEffects.ArmorEffect.orderEffectsForAutoChange(
|
const orderedEffects = game.system.api.data.activeEffects.changeTypes.armor.orderEffectsForAutoChange(
|
||||||
armorEffects,
|
armorEffects,
|
||||||
increasing
|
increasing
|
||||||
);
|
);
|
||||||
|
|
@ -482,11 +482,11 @@ export default class DhCharacter extends DhCreature {
|
||||||
usedArmorChange -= armorEffect.system.armorChange.value;
|
usedArmorChange -= armorEffect.system.armorChange.value;
|
||||||
} else {
|
} else {
|
||||||
if (increasing) {
|
if (increasing) {
|
||||||
const remainingArmor = armorEffect.system.armorChange.max - armorEffect.system.armorChange.value;
|
const remainingArmor = armorEffect.system.armorData.max - armorEffect.system.armorData.value;
|
||||||
usedArmorChange = Math.min(remainingChange, remainingArmor);
|
usedArmorChange = Math.min(remainingChange, remainingArmor);
|
||||||
remainingChange -= usedArmorChange;
|
remainingChange -= usedArmorChange;
|
||||||
} else {
|
} else {
|
||||||
const changeChange = Math.min(armorEffect.system.armorChange.value, remainingChange);
|
const changeChange = Math.min(armorEffect.system.armorData.value, remainingChange);
|
||||||
usedArmorChange -= changeChange;
|
usedArmorChange -= changeChange;
|
||||||
remainingChange -= changeChange;
|
remainingChange -= changeChange;
|
||||||
}
|
}
|
||||||
|
|
@ -499,12 +499,13 @@ export default class DhCharacter extends DhCreature {
|
||||||
|
|
||||||
embeddedUpdates[armorEffect.parent.id].updates.push({
|
embeddedUpdates[armorEffect.parent.id].updates.push({
|
||||||
'_id': armorEffect.id,
|
'_id': armorEffect.id,
|
||||||
'system.changes': [
|
'system.changes': armorEffect.system.changes.map(change => ({
|
||||||
{
|
...change,
|
||||||
...armorEffect.system.armorChange,
|
value:
|
||||||
value: armorEffect.system.armorChange.value + usedArmorChange
|
change.type === 'armor'
|
||||||
}
|
? armorEffect.system.armorChange.value + usedArmorChange
|
||||||
]
|
: change.value
|
||||||
|
}))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ export default class DHArmor extends AttachableItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
get armorEffect() {
|
get armorEffect() {
|
||||||
return this.parent.effects.find(x => x.type === 'armor');
|
return this.parent.effects.find(x => x.system.armorData);
|
||||||
}
|
}
|
||||||
|
|
||||||
get armorData() {
|
get armorData() {
|
||||||
|
|
@ -80,9 +80,9 @@ export default class DHArmor extends AttachableItem {
|
||||||
async _onCreate(_data, _options, userId) {
|
async _onCreate(_data, _options, userId) {
|
||||||
if (userId !== game.user.id) return;
|
if (userId !== game.user.id) return;
|
||||||
|
|
||||||
if (!this.parent.effects.some(x => x.type === 'armor')) {
|
if (!this.parent.effects.some(x => x.system.armorData)) {
|
||||||
this.parent.createEmbeddedDocuments('ActiveEffect', [
|
this.parent.createEmbeddedDocuments('ActiveEffect', [
|
||||||
game.system.api.data.activeEffects.ArmorEffect.getDefaultObject()
|
game.system.api.data.activeEffects.changeTypes.armor.getDefaultArmorEffect()
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
|
||||||
throw new Error('The array of sub-types to restrict to must not be empty.');
|
throw new Error('The array of sub-types to restrict to must not be empty.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const creatableEffects = types || ['base', 'armor'];
|
const creatableEffects = types || ['base'];
|
||||||
const documentTypes = this.TYPES.filter(type => creatableEffects.includes(type)).map(type => {
|
const documentTypes = this.TYPES.filter(type => creatableEffects.includes(type)).map(type => {
|
||||||
const labelKey = `TYPES.ActiveEffect.${type}`;
|
const labelKey = `TYPES.ActiveEffect.${type}`;
|
||||||
const label = game.i18n.has(labelKey) ? game.i18n.localize(labelKey) : type;
|
const label = game.i18n.has(labelKey) ? game.i18n.localize(labelKey) : type;
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ export const preloadHandlebarsTemplates = async function () {
|
||||||
'systems/daggerheart/templates/ui/chat/parts/button-part.hbs',
|
'systems/daggerheart/templates/ui/chat/parts/button-part.hbs',
|
||||||
'systems/daggerheart/templates/ui/itemBrowser/itemContainer.hbs',
|
'systems/daggerheart/templates/ui/itemBrowser/itemContainer.hbs',
|
||||||
'systems/daggerheart/templates/scene/dh-config.hbs',
|
'systems/daggerheart/templates/scene/dh-config.hbs',
|
||||||
'systems/daggerheart/templates/settings/appearance-settings/diceSoNiceTab.hbs'
|
'systems/daggerheart/templates/settings/appearance-settings/diceSoNiceTab.hbs',
|
||||||
|
'systems/daggerheart/templates/sheets/activeEffect/typeChanges/armorChange.hbs'
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -374,16 +374,19 @@ export async function runMigrations() {
|
||||||
if (migrationArmorScore !== undefined && !hasArmorEffect) {
|
if (migrationArmorScore !== undefined && !hasArmorEffect) {
|
||||||
await item.createEmbeddedDocuments('ActiveEffect', [
|
await item.createEmbeddedDocuments('ActiveEffect', [
|
||||||
{
|
{
|
||||||
...game.system.api.data.activeEffects.ArmorEffect.getDefaultObject(),
|
...game.system.api.data.activeEffects.changeTypes.armor.getDefaultArmorEffect(),
|
||||||
changes: [
|
changes: [
|
||||||
{
|
{
|
||||||
key: 'system.armorScore',
|
key: 'Armor',
|
||||||
type: CONFIG.DH.GENERAL.activeEffectModes.armor.id,
|
type: CONFIG.DH.GENERAL.activeEffectModes.armor,
|
||||||
phase: 'initial',
|
phase: 'initial',
|
||||||
priority: 20,
|
priority: 20,
|
||||||
value: 0,
|
value: 0,
|
||||||
|
typeData: {
|
||||||
|
type: 'armor',
|
||||||
max: migrationArmorScore.toString()
|
max: migrationArmorScore.toString()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
|
|
@ -31,5 +31,27 @@
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.armor-change-container {
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
row-gap: 0;
|
||||||
|
|
||||||
|
legend {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding-left: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
padding: 0;
|
||||||
|
left: -0.25rem; // TODO: Find why this header is offset 0.25rem to the right so this can be removed.
|
||||||
|
}
|
||||||
|
|
||||||
|
header,
|
||||||
|
ol {
|
||||||
|
grid-template-columns: 6rem 6rem 12rem 4rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
.application.sheet.daggerheart.dh-style.armor-effect-config {
|
|
||||||
.tab-form-footer {
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -44,4 +44,3 @@
|
||||||
@import './actions/actions.less';
|
@import './actions/actions.less';
|
||||||
|
|
||||||
@import './activeEffects/activeEffects.less';
|
@import './activeEffects/activeEffects.less';
|
||||||
@import './activeEffects/armorEffects.less';
|
|
||||||
|
|
|
||||||
|
|
@ -278,8 +278,7 @@
|
||||||
},
|
},
|
||||||
"ActiveEffect": {
|
"ActiveEffect": {
|
||||||
"beastform": {},
|
"beastform": {},
|
||||||
"horde": {},
|
"horde": {}
|
||||||
"armor": {}
|
|
||||||
},
|
},
|
||||||
"Combat": {
|
"Combat": {
|
||||||
"combat": {}
|
"combat": {}
|
||||||
|
|
|
||||||
|
|
@ -13,4 +13,19 @@
|
||||||
{{{change}}}
|
{{{change}}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
|
<fieldset class="armor-change-container">
|
||||||
|
<legend>{{localize "DAGGERHEART.GENERAL.armor"}} <input type="checkbox" class="armor-change-checkbox" data-index="{{typedChanges.armor.index}}" {{checked typedChanges.armor}} /></legend>
|
||||||
|
{{#if typedChanges.armor}}
|
||||||
|
<header>
|
||||||
|
<div>{{localize "EFFECT.FIELDS.changes.element.value.label"}}</div>
|
||||||
|
<div>{{localize "DAGGERHEART.GENERAL.max"}}</div>
|
||||||
|
<div>{{localize "DAGGERHEART.EFFECTS.Armor.FIELDS.armorInteraction.label"}}</div>
|
||||||
|
<div>{{localize "EFFECT.FIELDS.changes.element.priority.label"}}</div>
|
||||||
|
</header>
|
||||||
|
<ol class="scrollable">
|
||||||
|
{{> "systems/daggerheart/templates/sheets/activeEffect/typeChanges/armorChange.hbs" typedChanges.armor fields=@root.systemFields.changes.element.fields}}
|
||||||
|
</ol>
|
||||||
|
{{/if}}
|
||||||
|
</fieldset>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
10
templates/sheets/activeEffect/typeChanges/armorChange.hbs
Normal file
10
templates/sheets/activeEffect/typeChanges/armorChange.hbs
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<li data-index="{{index}}">
|
||||||
|
<input type="hidden" name="{{concat "system.changes." index ".key"}}" value="{{key}}" />
|
||||||
|
<input type="hidden" name="{{concat "system.changes." index ".type"}}" value="{{type}}" />
|
||||||
|
<input type="hidden" name="{{concat "system.changes." index ".phase"}}" value="{{phase}}" />
|
||||||
|
<input type="hidden" name="{{concat "system.changes." index ".typeData.type"}}" value="{{typeData.type}}" />
|
||||||
|
{{formInput fields.value name=(concat "system.changes." index ".value") value=value elementType="input" data-dtype="Number"}}
|
||||||
|
{{formInput fields.typeData.types.armor.fields.max name=(concat "system.changes." index ".typeData.max") value=typeData.max data-dtype="Number"}}
|
||||||
|
{{formInput fields.typeData.types.armor.fields.armorInteraction name=(concat "system.changes." index ".typeData.armorInteraction") value=typeData.armorInteraction localize=true}}
|
||||||
|
{{formInput fields.priority name=(concat "system.changes." index ".priority") value=priority}}
|
||||||
|
</li>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue