mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-04-22 07:23:37 +02:00
Merge branch 'main' into feature/tag-team-rework
This commit is contained in:
commit
43114187b9
96 changed files with 1942 additions and 664 deletions
|
|
@ -7,6 +7,7 @@ import EffectAction from './effectAction.mjs';
|
|||
import HealingAction from './healingAction.mjs';
|
||||
import MacroAction from './macroAction.mjs';
|
||||
import SummonAction from './summonAction.mjs';
|
||||
import TransformAction from './transformAction.mjs';
|
||||
|
||||
export const actionsTypes = {
|
||||
base: BaseAction,
|
||||
|
|
@ -17,5 +18,6 @@ export const actionsTypes = {
|
|||
summon: SummonAction,
|
||||
effect: EffectAction,
|
||||
macro: MacroAction,
|
||||
beastform: BeastformAction
|
||||
beastform: BeastformAction,
|
||||
transform: TransformAction
|
||||
};
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
async executeWorkflow(config) {
|
||||
for (const [key, part] of this.workflow) {
|
||||
if (Hooks.call(`${CONFIG.DH.id}.pre${key.capitalize()}Action`, this, config) === false) return;
|
||||
if ((await part.execute(config)) === false) return;
|
||||
if ((await part.execute(config)) === false) return false;
|
||||
if (Hooks.call(`${CONFIG.DH.id}.post${key.capitalize()}Action`, this, config) === false) return;
|
||||
}
|
||||
}
|
||||
|
|
@ -224,7 +224,9 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
}
|
||||
|
||||
// Execute the Action Worflow in order based of schema fields
|
||||
await this.executeWorkflow(config);
|
||||
const result = await this.executeWorkflow(config);
|
||||
if (result === false) return;
|
||||
|
||||
await config.resourceUpdates.updateResources();
|
||||
|
||||
if (Hooks.call(`${CONFIG.DH.id}.postUseAction`, this, config) === false) return;
|
||||
|
|
|
|||
5
module/data/action/transformAction.mjs
Normal file
5
module/data/action/transformAction.mjs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import DHBaseAction from './baseAction.mjs';
|
||||
|
||||
export default class DHTransformAction extends DHBaseAction {
|
||||
static extraSchemas = [...super.extraSchemas, 'transform'];
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ import DHAdversarySettings from '../../applications/sheets-configs/adversary-set
|
|||
import { ActionField } from '../fields/actionField.mjs';
|
||||
import { commonActorRules } from './base.mjs';
|
||||
import DhCreature from './creature.mjs';
|
||||
import { resourceField, bonusField } from '../fields/actorField.mjs';
|
||||
import { bonusField } from '../fields/actorField.mjs';
|
||||
import { calculateExpectedValue, parseTermsFromSimpleFormula } from '../../helpers/utils.mjs';
|
||||
import { adversaryExpectedDamage, adversaryScalingData } from '../../config/actorConfig.mjs';
|
||||
|
||||
|
|
@ -65,10 +65,6 @@ export default class DhpAdversary extends DhCreature {
|
|||
label: 'DAGGERHEART.GENERAL.DamageThresholds.severeThreshold'
|
||||
})
|
||||
}),
|
||||
resources: new fields.SchemaField({
|
||||
hitPoints: resourceField(0, 0, 'DAGGERHEART.GENERAL.HitPoints.plural', true),
|
||||
stress: resourceField(0, 0, 'DAGGERHEART.GENERAL.stress', true)
|
||||
}),
|
||||
rules: new fields.SchemaField({
|
||||
...commonActorRules()
|
||||
}),
|
||||
|
|
@ -191,6 +187,7 @@ export default class DhpAdversary extends DhCreature {
|
|||
}
|
||||
|
||||
prepareDerivedData() {
|
||||
super.prepareDerivedData();
|
||||
this.attack.roll.isStandardAttack = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -213,7 +213,7 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
|
|||
const textData = Object.keys(changes.system.resources).reduce((acc, key) => {
|
||||
const resource = changes.system.resources[key];
|
||||
if (resource.value !== undefined && resource.value !== this.resources[key].value) {
|
||||
acc.push(getScrollTextData(this.resources, resource, key));
|
||||
acc.push(getScrollTextData(this.parent, resource, key));
|
||||
}
|
||||
|
||||
return acc;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
|||
import DhLevelData from '../levelData.mjs';
|
||||
import { commonActorRules } from './base.mjs';
|
||||
import DhCreature from './creature.mjs';
|
||||
import { attributeField, resourceField, stressDamageReductionRule, bonusField } from '../fields/actorField.mjs';
|
||||
import { attributeField, stressDamageReductionRule, bonusField } from '../fields/actorField.mjs';
|
||||
import { ActionField } from '../fields/actionField.mjs';
|
||||
import DHCharacterSettings from '../../applications/sheets-configs/character-settings.mjs';
|
||||
|
||||
|
|
@ -27,28 +27,6 @@ export default class DhCharacter extends DhCreature {
|
|||
|
||||
return {
|
||||
...super.defineSchema(),
|
||||
resources: new fields.SchemaField({
|
||||
hitPoints: resourceField(
|
||||
0,
|
||||
0,
|
||||
'DAGGERHEART.GENERAL.HitPoints.plural',
|
||||
true,
|
||||
'DAGGERHEART.ACTORS.Character.maxHPBonus'
|
||||
),
|
||||
stress: resourceField(6, 0, 'DAGGERHEART.GENERAL.stress', true),
|
||||
hope: new fields.SchemaField(
|
||||
{
|
||||
value: new fields.NumberField({
|
||||
initial: 2,
|
||||
min: 0,
|
||||
integer: true,
|
||||
label: 'DAGGERHEART.GENERAL.hope'
|
||||
}),
|
||||
isReversed: new fields.BooleanField({ initial: false })
|
||||
},
|
||||
{ label: 'DAGGERHEART.GENERAL.hope' }
|
||||
)
|
||||
}),
|
||||
traits: new fields.SchemaField({
|
||||
agility: attributeField('DAGGERHEART.CONFIG.Traits.agility.name'),
|
||||
strength: attributeField('DAGGERHEART.CONFIG.Traits.strength.name'),
|
||||
|
|
@ -609,6 +587,7 @@ export default class DhCharacter extends DhCreature {
|
|||
}
|
||||
|
||||
prepareBaseData() {
|
||||
super.prepareBaseData();
|
||||
this.evasion += this.class.value?.system?.evasion ?? 0;
|
||||
|
||||
const currentLevel = this.levelData.level.current;
|
||||
|
|
@ -680,6 +659,7 @@ export default class DhCharacter extends DhCreature {
|
|||
}
|
||||
|
||||
prepareDerivedData() {
|
||||
super.prepareDerivedData();
|
||||
let baseHope = this.resources.hope.value;
|
||||
if (this.companion) {
|
||||
for (let levelKey in this.companion.system.levelData.levelups) {
|
||||
|
|
@ -699,6 +679,7 @@ export default class DhCharacter extends DhCreature {
|
|||
this.attack.roll.trait = this.rules.attack.roll.trait ?? this.attack.roll.trait;
|
||||
|
||||
this.resources.armor = {
|
||||
label: 'DAGGERHEART.GENERAL.armor',
|
||||
value: this.armor?.system?.marks?.value ?? 0,
|
||||
max: this.armorScore,
|
||||
isReversed: true
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
|||
import { ActionField } from '../fields/actionField.mjs';
|
||||
import { adjustDice, adjustRange } from '../../helpers/utils.mjs';
|
||||
import DHCompanionSettings from '../../applications/sheets-configs/companion-settings.mjs';
|
||||
import { resourceField, bonusField } from '../fields/actorField.mjs';
|
||||
import { bonusField } from '../fields/actorField.mjs';
|
||||
|
||||
export default class DhCompanion extends DhCreature {
|
||||
static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Companion'];
|
||||
|
|
@ -26,10 +26,6 @@ export default class DhCompanion extends DhCreature {
|
|||
return {
|
||||
...super.defineSchema(),
|
||||
partner: new ForeignDocumentUUIDField({ type: 'Actor' }),
|
||||
resources: new fields.SchemaField({
|
||||
stress: resourceField(3, 0, 'DAGGERHEART.GENERAL.stress', true),
|
||||
hope: new fields.NumberField({ initial: 0, integer: true, label: 'DAGGERHEART.GENERAL.hope' })
|
||||
}),
|
||||
evasion: new fields.NumberField({
|
||||
required: true,
|
||||
min: 1,
|
||||
|
|
@ -127,6 +123,7 @@ export default class DhCompanion extends DhCreature {
|
|||
}
|
||||
|
||||
prepareBaseData() {
|
||||
super.prepareBaseData();
|
||||
this.attack.roll.bonus = this.partner?.system?.spellcastModifier ?? 0;
|
||||
|
||||
for (let levelKey in this.levelData.levelups) {
|
||||
|
|
@ -161,6 +158,7 @@ export default class DhCompanion extends DhCreature {
|
|||
}
|
||||
|
||||
prepareDerivedData() {
|
||||
super.prepareDerivedData();
|
||||
/* Partner Related Setup */
|
||||
if (this.partner) {
|
||||
this.levelData.level.changed = this.partner.system.levelData.level.current;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { ResourcesField } from '../fields/actorField.mjs';
|
||||
import BaseDataActor from './base.mjs';
|
||||
|
||||
export default class DhCreature extends BaseDataActor {
|
||||
|
|
@ -7,6 +8,7 @@ export default class DhCreature extends BaseDataActor {
|
|||
|
||||
return {
|
||||
...super.defineSchema(),
|
||||
resources: new ResourcesField(this.metadata.type),
|
||||
advantageSources: new fields.ArrayField(new fields.StringField(), {
|
||||
label: 'DAGGERHEART.ACTORS.Character.advantageSources.label',
|
||||
hint: 'DAGGERHEART.ACTORS.Character.advantageSources.hint'
|
||||
|
|
@ -17,4 +19,45 @@ export default class DhCreature extends BaseDataActor {
|
|||
})
|
||||
};
|
||||
}
|
||||
|
||||
get isAutoVulnerableActive() {
|
||||
const vulnerableAppliedByOther = this.parent.effects.some(
|
||||
x => x.statuses.has('vulnerable') && !x.flags.daggerheart?.autoApplyFlagId
|
||||
);
|
||||
return !vulnerableAppliedByOther;
|
||||
}
|
||||
|
||||
async _preUpdate(changes, options, userId) {
|
||||
const allowed = await super._preUpdate(changes, options, userId);
|
||||
if (allowed === false) return;
|
||||
|
||||
const automationSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation);
|
||||
if (
|
||||
automationSettings.vulnerableAutomation &&
|
||||
this.parent.type !== 'companion' &&
|
||||
changes.system?.resources?.stress?.value
|
||||
) {
|
||||
const { name, description, img, autoApplyFlagId } = CONFIG.DH.GENERAL.conditions().vulnerable;
|
||||
const autoEffects = this.parent.effects.filter(
|
||||
x => x.flags.daggerheart?.autoApplyFlagId === autoApplyFlagId
|
||||
);
|
||||
if (changes.system.resources.stress.value >= this.resources.stress.max) {
|
||||
if (!autoEffects.length)
|
||||
this.parent.createEmbeddedDocuments('ActiveEffect', [
|
||||
{
|
||||
name: game.i18n.localize(name),
|
||||
description: game.i18n.localize(description),
|
||||
img: img,
|
||||
statuses: ['vulnerable'],
|
||||
flags: { daggerheart: { autoApplyFlagId } }
|
||||
}
|
||||
]);
|
||||
} else if (this.resources.stress.value >= this.resources.stress.max) {
|
||||
this.parent.deleteEmbeddedDocuments(
|
||||
'ActiveEffect',
|
||||
autoEffects.map(x => x.id)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,3 +10,4 @@ export { default as DamageField } from './damageField.mjs';
|
|||
export { default as RollField } from './rollField.mjs';
|
||||
export { default as MacroField } from './macroField.mjs';
|
||||
export { default as SummonField } from './summonField.mjs';
|
||||
export { default as TransformField } from './transformField.mjs';
|
||||
|
|
|
|||
103
module/data/fields/action/transformField.mjs
Normal file
103
module/data/fields/action/transformField.mjs
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
const fields = foundry.data.fields;
|
||||
|
||||
export default class DHSummonField extends fields.SchemaField {
|
||||
/**
|
||||
* Action Workflow order
|
||||
*/
|
||||
static order = 130;
|
||||
|
||||
constructor(options = {}, context = {}) {
|
||||
const transformFields = {
|
||||
actorUUID: new fields.DocumentUUIDField({
|
||||
type: 'Actor',
|
||||
required: true
|
||||
}),
|
||||
resourceRefresh: new fields.SchemaField({
|
||||
hitPoints: new fields.BooleanField({ initial: true }),
|
||||
stress: new fields.BooleanField({ initial: true })
|
||||
})
|
||||
};
|
||||
super(transformFields, options, context);
|
||||
}
|
||||
|
||||
static async execute() {
|
||||
if (!this.transform.actorUUID) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.transform.noTransformActor'));
|
||||
return false;
|
||||
}
|
||||
|
||||
const baseActor = await foundry.utils.fromUuid(this.transform.actorUUID);
|
||||
if (!baseActor) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.transform.transformActorMissing'));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!canvas.scene) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.transform.canvasError'));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.actor.prototypeToken.actorLink) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.transform.actorLinkError'));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.actor.token) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.transform.prototypeError'));
|
||||
return false;
|
||||
}
|
||||
|
||||
const actor = await DHSummonField.getWorldActor(baseActor);
|
||||
const tokenSizes = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes;
|
||||
const tokenSize = actor?.system.metadata.usesSize ? tokenSizes[actor.system.size] : actor.prototypeToken.width;
|
||||
|
||||
await this.actor.token.update(
|
||||
{ ...actor.prototypeToken.toJSON(), actorId: actor.id, width: tokenSize, height: tokenSize },
|
||||
{ diff: false, recursive: false, noHook: true }
|
||||
);
|
||||
|
||||
if (this.actor.token.combatant) {
|
||||
this.actor.token.combatant.update({ actorId: actor.id, img: actor.prototypeToken.texture.src });
|
||||
}
|
||||
|
||||
const marks = { hitPoints: 0, stress: 0 };
|
||||
if (!this.transform.resourceRefresh.hitPoints) {
|
||||
marks.hitPoints = Math.min(
|
||||
this.actor.system.resources.hitPoints.value,
|
||||
this.actor.token.actor.system.resources.hitPoints.max - 1
|
||||
);
|
||||
}
|
||||
if (!this.transform.resourceRefresh.stress) {
|
||||
marks.stress = Math.min(
|
||||
this.actor.system.resources.stress.value,
|
||||
this.actor.token.actor.system.resources.stress.max - 1
|
||||
);
|
||||
}
|
||||
if (marks.hitPoints || marks.stress) {
|
||||
this.actor.token.actor.update({
|
||||
'system.resources': {
|
||||
hitPoints: { value: marks.hitPoints },
|
||||
stress: { value: marks.stress }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const prevPosition = { ...this.actor.sheet.position };
|
||||
this.actor.sheet.close();
|
||||
this.actor.token.actor.sheet.render({ force: true, position: prevPosition });
|
||||
}
|
||||
|
||||
/* Check for any available instances of the actor present in the world, or create a world actor based on compendium */
|
||||
static async getWorldActor(baseActor) {
|
||||
if (!baseActor.inCompendium) return baseActor;
|
||||
|
||||
const dataType = game.system.api.data.actors[`Dh${baseActor.type.capitalize()}`];
|
||||
if (dataType && baseActor.img === dataType.DEFAULT_ICON) {
|
||||
const worldActorCopy = game.actors.find(x => x.name === baseActor.name);
|
||||
if (worldActorCopy) return worldActorCopy;
|
||||
}
|
||||
|
||||
const worldActor = await game.system.api.documents.DhpActor.create(baseActor.toObject());
|
||||
return worldActor;
|
||||
}
|
||||
}
|
||||
|
|
@ -6,22 +6,6 @@ const attributeField = label =>
|
|||
tierMarked: new fields.BooleanField({ initial: false })
|
||||
});
|
||||
|
||||
const resourceField = (max = 0, initial = 0, label, reverse = false, maxLabel) =>
|
||||
new fields.SchemaField(
|
||||
{
|
||||
value: new fields.NumberField({ initial: initial, min: 0, integer: true, label }),
|
||||
max: new fields.NumberField({
|
||||
initial: max,
|
||||
integer: true,
|
||||
label:
|
||||
maxLabel ??
|
||||
game.i18n.format('DAGGERHEART.GENERAL.maxWithThing', { thing: game.i18n.localize(label) })
|
||||
}),
|
||||
isReversed: new fields.BooleanField({ initial: reverse })
|
||||
},
|
||||
{ label }
|
||||
);
|
||||
|
||||
const stressDamageReductionRule = localizationPath =>
|
||||
new fields.SchemaField({
|
||||
cost: new fields.NumberField({
|
||||
|
|
@ -37,4 +21,67 @@ const bonusField = label =>
|
|||
dice: new fields.ArrayField(new fields.StringField(), { label: `${game.i18n.localize(label)} Dice` })
|
||||
});
|
||||
|
||||
export { attributeField, resourceField, stressDamageReductionRule, bonusField };
|
||||
/**
|
||||
* Field used for actor resources. It is a resource that validates dynamically based on the config.
|
||||
* Because "max" may be defined during runtime, we don't attempt to clamp the maximum value.
|
||||
*/
|
||||
class ResourcesField extends fields.TypedObjectField {
|
||||
constructor(actorType) {
|
||||
super(
|
||||
new fields.SchemaField({
|
||||
value: new fields.NumberField({ min: 0, initial: 0, integer: true }),
|
||||
// Some resources allow changing max. A null max means its the default
|
||||
max: new fields.NumberField({ initial: null, integer: true, nullable: true })
|
||||
})
|
||||
);
|
||||
this.actorType = actorType;
|
||||
}
|
||||
|
||||
getInitialValue() {
|
||||
const resources = CONFIG.DH.RESOURCE[this.actorType].all;
|
||||
return Object.values(resources).reduce((result, resource) => {
|
||||
result[resource.id] = {
|
||||
value: resource.initial,
|
||||
max: null
|
||||
};
|
||||
return result;
|
||||
}, {});
|
||||
}
|
||||
|
||||
_validateKey(key) {
|
||||
return key in CONFIG.DH.RESOURCE[this.actorType].all;
|
||||
}
|
||||
|
||||
_cleanType(value, options) {
|
||||
value = super._cleanType(value, options);
|
||||
|
||||
// If not partial, ensure all data exists
|
||||
if (!options.partial) {
|
||||
value = foundry.utils.mergeObject(this.getInitialValue(), value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/** Initializes the original source data, returning prepared data */
|
||||
initialize(...args) {
|
||||
const data = super.initialize(...args);
|
||||
const resources = CONFIG.DH.RESOURCE[this.actorType].all;
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
// TypedObjectField only calls _validateKey when persisting, so we also call it here
|
||||
if (!this._validateKey(key)) {
|
||||
delete value[key];
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add basic prepared data.
|
||||
const resource = resources[key];
|
||||
value.label = resource.label;
|
||||
value.isReversed = resources[key].reverse;
|
||||
value.max = typeof resource.max === 'number' ? value.max ?? resource.max : null;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
export { attributeField, ResourcesField, stressDamageReductionRule, bonusField };
|
||||
|
|
|
|||
|
|
@ -23,9 +23,7 @@ export default class DHArmor extends AttachableItem {
|
|||
armorFeatures: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
value: new fields.StringField({
|
||||
required: true,
|
||||
choices: CONFIG.DH.ITEM.allArmorFeatures,
|
||||
blank: true
|
||||
required: true
|
||||
}),
|
||||
effectIds: new fields.ArrayField(new fields.StringField({ required: true })),
|
||||
actionIds: new fields.ArrayField(new fields.StringField({ required: true }))
|
||||
|
|
@ -58,7 +56,7 @@ export default class DHArmor extends AttachableItem {
|
|||
async getDescriptionData() {
|
||||
const baseDescription = this.description;
|
||||
const allFeatures = CONFIG.DH.ITEM.allArmorFeatures();
|
||||
const features = this.armorFeatures.map(x => allFeatures[x.value]);
|
||||
const features = this.armorFeatures.map(x => allFeatures[x.value]).filter(x => x);
|
||||
|
||||
const prefix = await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/sheets/items/armor/description.hbs',
|
||||
|
|
|
|||
|
|
@ -224,7 +224,7 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
|||
const armorChanged =
|
||||
changed.system?.marks?.value !== undefined && changed.system.marks.value !== this.marks.value;
|
||||
if (armorChanged && autoSettings.resourceScrollTexts && this.parent.parent?.type === 'character') {
|
||||
const armorData = getScrollTextData(this.parent.parent.system.resources, changed.system.marks, 'armor');
|
||||
const armorData = getScrollTextData(this.parent.parent, changed.system.marks, 'armor');
|
||||
options.scrollingTextData = [armorData];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,9 +38,7 @@ export default class DHWeapon extends AttachableItem {
|
|||
weaponFeatures: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
value: new fields.StringField({
|
||||
required: true,
|
||||
choices: CONFIG.DH.ITEM.allWeaponFeatures,
|
||||
blank: true
|
||||
required: true
|
||||
}),
|
||||
effectIds: new fields.ArrayField(new fields.StringField({ required: true })),
|
||||
actionIds: new fields.ArrayField(new fields.StringField({ required: true }))
|
||||
|
|
@ -114,24 +112,14 @@ export default class DHWeapon extends AttachableItem {
|
|||
async getDescriptionData() {
|
||||
const baseDescription = this.description;
|
||||
|
||||
const tier = game.i18n.localize(`DAGGERHEART.GENERAL.Tiers.${this.tier}`);
|
||||
const trait = game.i18n.localize(CONFIG.DH.ACTOR.abilities[this.attack.roll.trait].label);
|
||||
const range = game.i18n.localize(`DAGGERHEART.CONFIG.Range.${this.attack.range}.name`);
|
||||
const damage = Roll.replaceFormulaData(this.attack.damageFormula, this.parent.parent ?? this.parent);
|
||||
const burden = game.i18n.localize(CONFIG.DH.GENERAL.burden[this.burden].label);
|
||||
|
||||
const allFeatures = CONFIG.DH.ITEM.allWeaponFeatures();
|
||||
const features = this.weaponFeatures.map(x => allFeatures[x.value]);
|
||||
const features = this.weaponFeatures.map(x => allFeatures[x.value]).filter(x => x);
|
||||
|
||||
const prefix = await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/sheets/items/weapon/description.hbs',
|
||||
{
|
||||
features,
|
||||
tier,
|
||||
trait,
|
||||
range,
|
||||
damage,
|
||||
burden
|
||||
item: this,
|
||||
features
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,10 @@ export default class DhAutomation extends foundry.abstract.DataModel {
|
|||
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.hopeFear.players.label'
|
||||
})
|
||||
}),
|
||||
vulnerableAutomation: new fields.BooleanField({
|
||||
initial: true,
|
||||
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.vulnerableAutomation.label'
|
||||
}),
|
||||
countdownAutomation: new fields.BooleanField({
|
||||
required: true,
|
||||
initial: true,
|
||||
|
|
|
|||
|
|
@ -145,6 +145,16 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
|
|||
description: new fields.StringField()
|
||||
})
|
||||
),
|
||||
resources: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
resources: new fields.TypedObjectField(new fields.EmbeddedDataField(Resource))
|
||||
}),
|
||||
{
|
||||
initial: {
|
||||
character: { resources: {} }
|
||||
}
|
||||
}
|
||||
),
|
||||
itemFeatures: new fields.SchemaField({
|
||||
weaponFeatures: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
|
|
@ -185,4 +195,117 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
|
|||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
/** Invoked by the setting when data changes */
|
||||
handleChange() {
|
||||
if (this.maxFear) {
|
||||
if (ui.resources) ui.resources.render({ force: true });
|
||||
}
|
||||
|
||||
this.refreshConfig();
|
||||
this.#resetActors();
|
||||
}
|
||||
|
||||
/** Update config values based on homebrew data. Make sure the references don't change */
|
||||
refreshConfig() {
|
||||
for (const [actorType, actorData] of Object.entries(this.resources)) {
|
||||
const config = CONFIG.DH.RESOURCE[actorType];
|
||||
for (const key of Object.keys(config.all)) {
|
||||
delete config.all[key];
|
||||
}
|
||||
Object.assign(config.all, {
|
||||
...Object.entries(actorData.resources).reduce((result, [key, value]) => {
|
||||
result[key] = value.toObject();
|
||||
result[key].id = key;
|
||||
return result;
|
||||
}, {}),
|
||||
...config.custom,
|
||||
...config.base,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers a reset and non-forced re-render on all given actors (if given)
|
||||
* or all world actors and actors in all scenes to show immediate results for a changed setting.
|
||||
*/
|
||||
#resetActors() {
|
||||
const actors = new Set(
|
||||
[
|
||||
game.actors.contents,
|
||||
game.scenes.contents.flatMap(s => s.tokens.contents).flatMap(t => t.actor ?? [])
|
||||
].flat()
|
||||
);
|
||||
for (const actor of actors) {
|
||||
for (const app of Object.values(actor.apps)) {
|
||||
for (const element of app.element?.querySelectorAll('prose-mirror.active')) {
|
||||
element.open = false; // This triggers a save
|
||||
}
|
||||
}
|
||||
|
||||
actor.reset();
|
||||
actor.render();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class Resource extends foundry.abstract.DataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
initial: new fields.NumberField({
|
||||
required: true,
|
||||
integer: true,
|
||||
initial: 0,
|
||||
min: 0,
|
||||
label: 'DAGGERHEART.GENERAL.initial'
|
||||
}),
|
||||
max: new fields.NumberField({
|
||||
nullable: true,
|
||||
initial: null,
|
||||
min: 0,
|
||||
label: 'DAGGERHEART.GENERAL.max'
|
||||
}),
|
||||
label: new fields.StringField({ label: 'DAGGERHEART.GENERAL.label' }),
|
||||
images: new fields.SchemaField({
|
||||
full: imageIconField('fa solid fa-circle'),
|
||||
empty: imageIconField('fa-regular fa-circle')
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
static getDefaultResourceData = label => {
|
||||
const images = Resource.schema.fields.images.getInitialValue();
|
||||
return {
|
||||
initial: 0,
|
||||
max: 0,
|
||||
label: label ?? '',
|
||||
images
|
||||
};
|
||||
};
|
||||
|
||||
static getDefaultImageData = imageKey => {
|
||||
return Resource.schema.fields.images.fields[imageKey].getInitialValue();
|
||||
};
|
||||
}
|
||||
|
||||
const imageIconField = defaultValue =>
|
||||
new foundry.data.fields.SchemaField(
|
||||
{
|
||||
value: new foundry.data.fields.StringField({
|
||||
initial: defaultValue,
|
||||
label: 'DAGGERHEART.SETTINGS.Homebrew.FIELDS.resources.resources.value.label'
|
||||
}),
|
||||
isIcon: new foundry.data.fields.BooleanField({
|
||||
required: true,
|
||||
initial: true,
|
||||
label: 'DAGGERHEART.SETTINGS.Homebrew.FIELDS.resources.resources.isIcon.label'
|
||||
}),
|
||||
noColorFilter: new foundry.data.fields.BooleanField({
|
||||
required: true,
|
||||
initial: false,
|
||||
label: 'DAGGERHEART.SETTINGS.Homebrew.FIELDS.resources.resources.noColorFilter.label'
|
||||
})
|
||||
},
|
||||
{ required: true }
|
||||
);
|
||||
|
|
|
|||
12
module/data/settings/Metagaming.mjs
Normal file
12
module/data/settings/Metagaming.mjs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
export default class DhMetagaming extends foundry.abstract.DataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
hideObserverPermissionInChat: new fields.BooleanField({
|
||||
initial: false,
|
||||
label: 'DAGGERHEART.SETTINGS.Metagaming.FIELDS.hideObserverPermissionInChat.label',
|
||||
hint: 'DAGGERHEART.SETTINGS.Metagaming.FIELDS.hideObserverPermissionInChat.hint'
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
export { default as DhAppearance } from './Appearance.mjs';
|
||||
export { default as DhAutomation } from './Automation.mjs';
|
||||
export { default as DhHomebrew } from './Homebrew.mjs';
|
||||
export { default as DhMetagaming } from './Metagaming.mjs';
|
||||
export { default as DhVariantRules } from './VariantRules.mjs';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue