mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-04-21 23:13:39 +02:00
Initial
This commit is contained in:
parent
0b5de79ca8
commit
5ab0a6d3e7
12 changed files with 231 additions and 27 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 };
|
||||||
|
|
|
||||||
|
|
@ -778,8 +778,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": {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,10 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
||||||
}
|
}
|
||||||
|
|
||||||
static DEFAULT_OPTIONS = {
|
static DEFAULT_OPTIONS = {
|
||||||
classes: ['daggerheart', 'sheet', 'dh-style']
|
classes: ['daggerheart', 'sheet', 'dh-style'],
|
||||||
|
actions: {
|
||||||
|
addTypedChange: DhActiveEffectConfig.#addTypedChange
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static PARTS = {
|
static PARTS = {
|
||||||
|
|
@ -187,38 +190,51 @@ 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 { base, ...typedChanges } = context.source.changes.reduce((acc, change, index) => {
|
||||||
|
const type = CONFIG.DH.GENERAL.baseActiveEffectModes[change.type] ? 'base' : change.type;
|
||||||
|
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) {
|
/* Could be generalised if needed later */
|
||||||
|
static #addTypedChange() {
|
||||||
|
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 } } });
|
||||||
|
}
|
||||||
|
|
||||||
|
_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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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({
|
||||||
|
|
|
||||||
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
|
||||||
|
};
|
||||||
141
module/data/activeEffect/changeTypes/armor.mjs
Normal file
141
module/data/activeEffect/changeTypes/armor.mjs
Normal file
|
|
@ -0,0 +1,141 @@
|
||||||
|
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.Armor.FIELDS.armorInteraction.label',
|
||||||
|
hint: 'DAGGERHEART.EFFECTS.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.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() {
|
||||||
|
return {
|
||||||
|
key: 'Armor',
|
||||||
|
type: CONFIG.DH.GENERAL.activeEffectModes.armor.id,
|
||||||
|
value: 0,
|
||||||
|
typeData: {
|
||||||
|
type: 'armor',
|
||||||
|
max: 0
|
||||||
|
},
|
||||||
|
phase: 'initial',
|
||||||
|
priority: 20
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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'
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -31,5 +31,12 @@
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.armor-change-container {
|
||||||
|
header,
|
||||||
|
ol {
|
||||||
|
grid-template-columns: 6rem 6rem 12rem 4rem 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,4 +13,21 @@
|
||||||
{{{change}}}
|
{{{change}}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
|
<fieldset class="armor-change-container">
|
||||||
|
<legend>{{localize "DAGGERHEART.GENERAL.armor"}} <a data-action="addTypedChange"><i class="fa-solid fa-plus"></i></a></legend>
|
||||||
|
|
||||||
|
<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">
|
||||||
|
{{#each typedChanges.armor as |armor|}}
|
||||||
|
{{> "systems/daggerheart/templates/sheets/activeEffect/typeChanges/armorChange.hbs" armor fields=@root.systemFields.changes.element.fields}}
|
||||||
|
{{/each}}
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
11
templates/sheets/activeEffect/typeChanges/armorChange.hbs
Normal file
11
templates/sheets/activeEffect/typeChanges/armorChange.hbs
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<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}}
|
||||||
|
<a data-action="deleteChange"><i class="fa-solid fa-trash"></i></a>
|
||||||
|
</li>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue