mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-17 23:49:02 +01:00
Merge branch 'main' into bug/103-enrich-htmlfield-content-before-its-used-in-applications
This commit is contained in:
commit
d39d3d37b3
106 changed files with 1556 additions and 532 deletions
|
|
@ -76,11 +76,7 @@ export class DHActionDiceData extends foundry.abstract.DataModel {
|
|||
};
|
||||
}
|
||||
|
||||
getFormula(actor) {
|
||||
/* const multiplier = this.multiplier === 'flat' ? this.flatMultiplier : actor.system[this.multiplier]?.total;
|
||||
return this.custom.enabled
|
||||
? this.custom.formula
|
||||
: `${multiplier ?? 1}${this.dice}${this.bonus ? (this.bonus < 0 ? ` - ${Math.abs(this.bonus)}` : ` + ${this.bonus}`) : ''}`; */
|
||||
getFormula() {
|
||||
const multiplier = this.multiplier === 'flat' ? this.flatMultiplier : `@${this.multiplier}`,
|
||||
bonus = this.bonus ? (this.bonus < 0 ? ` - ${Math.abs(this.bonus)}` : ` + ${this.bonus}`) : '';
|
||||
return this.custom.enabled ? this.custom.formula : `${multiplier ?? 1}${this.dice}${bonus}`;
|
||||
|
|
@ -93,7 +89,6 @@ export class DHDamageField extends fields.SchemaField {
|
|||
parts: new fields.ArrayField(new fields.EmbeddedDataField(DHDamageData)),
|
||||
includeBase: new fields.BooleanField({ initial: false })
|
||||
};
|
||||
// if (hasBase) damageFields.includeBase = new fields.BooleanField({ initial: true });
|
||||
super(damageFields, options, context);
|
||||
}
|
||||
}
|
||||
|
|
@ -102,15 +97,19 @@ export class DHDamageData extends foundry.abstract.DataModel {
|
|||
/** @override */
|
||||
static defineSchema() {
|
||||
return {
|
||||
// ...super.defineSchema(),
|
||||
base: new fields.BooleanField({ initial: false, readonly: true, label: 'Base' }),
|
||||
type: new fields.StringField({
|
||||
choices: CONFIG.DH.GENERAL.damageTypes,
|
||||
initial: 'physical',
|
||||
label: 'Type',
|
||||
nullable: false,
|
||||
required: true
|
||||
}),
|
||||
type: new fields.SetField(
|
||||
new fields.StringField({
|
||||
choices: CONFIG.DH.GENERAL.damageTypes,
|
||||
initial: 'physical',
|
||||
nullable: false,
|
||||
required: true
|
||||
}),
|
||||
{
|
||||
label: 'Type',
|
||||
initial: 'physical',
|
||||
}
|
||||
),
|
||||
resultBased: new fields.BooleanField({
|
||||
initial: false,
|
||||
label: 'DAGGERHEART.ACTIONS.Settings.resultBased.label'
|
||||
|
|
|
|||
|
|
@ -179,16 +179,9 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
|||
getRollData(data = {}) {
|
||||
const actorData = this.actor.getRollData(false);
|
||||
|
||||
// Remove when included directly in Actor getRollData
|
||||
actorData.prof = actorData.proficiency?.value ?? 1;
|
||||
actorData.cast = actorData.spellcast?.value ?? 1;
|
||||
// Add Roll results to RollDatas
|
||||
actorData.result = data.roll?.total ?? 1;
|
||||
/* actorData.scale = data.costs?.length
|
||||
? data.costs.reduce((a, c) => {
|
||||
a[c.type] = c.value;
|
||||
return a;
|
||||
}, {})
|
||||
: 1; */
|
||||
|
||||
actorData.scale = data.costs?.length // Right now only return the first scalable cost.
|
||||
? (data.costs.find(c => c.scalable)?.total ?? 1)
|
||||
: 1;
|
||||
|
|
@ -338,7 +331,7 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
|||
.filter(c => c.enabled !== false)
|
||||
.map(c => {
|
||||
const resource = this.actor.system.resources[c.type];
|
||||
return { type: c.type, value: (c.total ?? c.value) * (resource.hasOwnProperty('maxTotal') ? 1 : -1) };
|
||||
return { type: c.type, value: (c.total ?? c.value) * (resource.isReversed ? 1 : -1) };
|
||||
});
|
||||
|
||||
await this.actor.modifyResource(resources);
|
||||
|
|
@ -391,12 +384,12 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
|||
return false;
|
||||
}
|
||||
|
||||
/* maxTotal is a sign that the resource is inverted, IE it counts upwards instead of down */
|
||||
/* isReversed is a sign that the resource is inverted, IE it counts upwards instead of down */
|
||||
const resources = this.actor.system.resources;
|
||||
return realCosts.reduce(
|
||||
(a, c) =>
|
||||
a && resources[c.type].hasOwnProperty('maxTotal')
|
||||
? resources[c.type].value + (c.total ?? c.value) <= resources[c.type].maxTotal
|
||||
a && resources[c.type].isReversed
|
||||
? resources[c.type].value + (c.total ?? c.value) <= resources[c.type].max
|
||||
: resources[c.type]?.value >= (c.total ?? c.value),
|
||||
true
|
||||
);
|
||||
|
|
@ -439,7 +432,7 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
|||
name: actor.actor.name,
|
||||
img: actor.actor.img,
|
||||
difficulty: actor.actor.system.difficulty,
|
||||
evasion: actor.actor.system.evasion?.total
|
||||
evasion: actor.actor.system.evasion
|
||||
};
|
||||
}
|
||||
/* TARGET */
|
||||
|
|
|
|||
|
|
@ -10,7 +10,10 @@ export default class DHDamageAction extends DHBaseAction {
|
|||
}
|
||||
|
||||
async rollDamage(event, data) {
|
||||
let formula = this.damage.parts.map(p => this.getFormulaValue(p, data).getFormula(this.actor)).join(' + ');
|
||||
let formula = this.damage.parts.map(p => this.getFormulaValue(p, data).getFormula(this.actor)).join(' + '),
|
||||
damageTypes = [...new Set(this.damage.parts.reduce((a,c) => a.concat([...c.type]), []))];
|
||||
|
||||
damageTypes = !damageTypes.length ? ['physical'] : damageTypes;
|
||||
|
||||
if (!formula || formula == '') return;
|
||||
let roll = { formula: formula, total: formula },
|
||||
|
|
@ -25,6 +28,7 @@ export default class DHDamageAction extends DHBaseAction {
|
|||
hasSave: this.hasSave,
|
||||
isCritical: data.system?.roll?.isCritical ?? false,
|
||||
source: data.system?.source,
|
||||
damageTypes,
|
||||
event
|
||||
};
|
||||
if (this.hasSave) config.onSave = this.save.damageMod;
|
||||
|
|
@ -32,7 +36,7 @@ export default class DHDamageAction extends DHBaseAction {
|
|||
config.source.message = data._id;
|
||||
config.directDamage = false;
|
||||
}
|
||||
|
||||
|
||||
roll = CONFIG.Dice.daggerheart.DamageRoll.build(config);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ import BaseDataActor from './base.mjs';
|
|||
const resourceField = () =>
|
||||
new foundry.data.fields.SchemaField({
|
||||
value: new foundry.data.fields.NumberField({ initial: 0, integer: true }),
|
||||
max: new foundry.data.fields.NumberField({ initial: 0, integer: true })
|
||||
max: new foundry.data.fields.NumberField({ initial: 0, integer: true }),
|
||||
isReversed: new foundry.data.fields.BooleanField({ initial: true })
|
||||
});
|
||||
|
||||
export default class DhpAdversary extends BaseDataActor {
|
||||
|
|
@ -22,6 +23,7 @@ export default class DhpAdversary extends BaseDataActor {
|
|||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
...super.defineSchema(),
|
||||
tier: new fields.StringField({
|
||||
required: true,
|
||||
choices: CONFIG.DH.GENERAL.tiers,
|
||||
|
|
@ -32,7 +34,6 @@ export default class DhpAdversary extends BaseDataActor {
|
|||
choices: CONFIG.DH.ACTOR.adversaryTypes,
|
||||
initial: CONFIG.DH.ACTOR.adversaryTypes.standard.id
|
||||
}),
|
||||
description: new fields.StringField(),
|
||||
motivesAndTactics: new fields.StringField(),
|
||||
notes: new fields.HTMLField(),
|
||||
difficulty: new fields.NumberField({ required: true, initial: 1, integer: true }),
|
||||
|
|
@ -63,6 +64,7 @@ export default class DhpAdversary extends BaseDataActor {
|
|||
damage: {
|
||||
parts: [
|
||||
{
|
||||
type: ['physical'],
|
||||
value: {
|
||||
multiplier: 'flat'
|
||||
}
|
||||
|
|
@ -74,7 +76,7 @@ export default class DhpAdversary extends BaseDataActor {
|
|||
experiences: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField(),
|
||||
total: new fields.NumberField({ required: true, integer: true, initial: 1 })
|
||||
value: new fields.NumberField({ required: true, integer: true, initial: 1 })
|
||||
})
|
||||
),
|
||||
bonuses: new fields.SchemaField({
|
||||
|
|
|
|||
|
|
@ -1,5 +1,12 @@
|
|||
import DHBaseActorSettings from "../../applications/sheets/api/actor-setting.mjs";
|
||||
|
||||
const resistanceField = () =>
|
||||
new foundry.data.fields.SchemaField({
|
||||
resistance: new foundry.data.fields.BooleanField({ initial: false }),
|
||||
immunity: new foundry.data.fields.BooleanField({ initial: false }),
|
||||
reduction: new foundry.data.fields.NumberField({ integer: true, initial: 0 })
|
||||
});
|
||||
|
||||
/**
|
||||
* Describes metadata about the actor data model type
|
||||
* @typedef {Object} ActorDataModelMetadata
|
||||
|
|
@ -16,6 +23,7 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
|
|||
type: 'base',
|
||||
isNPC: true,
|
||||
settingSheet: null,
|
||||
hasResistances: true
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -24,6 +32,21 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
|
|||
return this.constructor.metadata;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
const schema = {};
|
||||
|
||||
if(this.metadata.isNPC)
|
||||
schema.description = new fields.HTMLField({ required: true, nullable: true });
|
||||
if(this.metadata.hasResistances)
|
||||
schema.resistance = new fields.SchemaField({
|
||||
physical: resistanceField(),
|
||||
magical: resistanceField()
|
||||
})
|
||||
return schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a data object used to evaluate any dice rolls associated with this Item Type
|
||||
* @param {object} [options] - Options which modify the getRollData method.
|
||||
|
|
|
|||
|
|
@ -5,16 +5,15 @@ import BaseDataActor from './base.mjs';
|
|||
|
||||
const attributeField = () =>
|
||||
new foundry.data.fields.SchemaField({
|
||||
value: new foundry.data.fields.NumberField({ initial: null, integer: true }),
|
||||
bonus: new foundry.data.fields.NumberField({ initial: 0, integer: true }),
|
||||
value: new foundry.data.fields.NumberField({ initial: 0, integer: true }),
|
||||
tierMarked: new foundry.data.fields.BooleanField({ initial: false })
|
||||
});
|
||||
|
||||
const resourceField = max =>
|
||||
const resourceField = (max, reverse = false) =>
|
||||
new foundry.data.fields.SchemaField({
|
||||
value: new foundry.data.fields.NumberField({ initial: 0, integer: true }),
|
||||
bonus: new foundry.data.fields.NumberField({ initial: 0, integer: true }),
|
||||
max: new foundry.data.fields.NumberField({ initial: max, integer: true })
|
||||
max: new foundry.data.fields.NumberField({ initial: max, integer: true }),
|
||||
isReversed: new foundry.data.fields.BooleanField({ initial: reverse })
|
||||
});
|
||||
|
||||
const stressDamageReductionRule = () =>
|
||||
|
|
@ -36,12 +35,10 @@ export default class DhCharacter extends BaseDataActor {
|
|||
const fields = foundry.data.fields;
|
||||
|
||||
return {
|
||||
...super.defineSchema(),
|
||||
resources: new fields.SchemaField({
|
||||
hitPoints: new fields.SchemaField({
|
||||
value: new foundry.data.fields.NumberField({ initial: 0, integer: true }),
|
||||
bonus: new foundry.data.fields.NumberField({ initial: 0, integer: true })
|
||||
}),
|
||||
stress: resourceField(6),
|
||||
hitPoints: resourceField(0, true),
|
||||
stress: resourceField(6, true),
|
||||
hope: resourceField(6),
|
||||
tokens: new fields.ObjectField(),
|
||||
dice: new fields.ObjectField()
|
||||
|
|
@ -54,18 +51,17 @@ export default class DhCharacter extends BaseDataActor {
|
|||
presence: attributeField(),
|
||||
knowledge: attributeField()
|
||||
}),
|
||||
proficiency: new fields.SchemaField({
|
||||
value: new fields.NumberField({ initial: 1, integer: true }),
|
||||
bonus: new fields.NumberField({ initial: 0, integer: true })
|
||||
}),
|
||||
evasion: new fields.SchemaField({
|
||||
bonus: new fields.NumberField({ initial: 0, integer: true })
|
||||
proficiency: new fields.NumberField({ initial: 1, integer: true }),
|
||||
evasion: new fields.NumberField({ initial: 0, integer: true }),
|
||||
armorScore: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
damageThresholds: new fields.SchemaField({
|
||||
severe: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
major: new fields.NumberField({ integer: true, initial: 0 })
|
||||
}),
|
||||
experiences: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField(),
|
||||
value: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
bonus: new fields.NumberField({ integer: true, initial: 0 })
|
||||
value: new fields.NumberField({ integer: true, initial: 0 })
|
||||
})
|
||||
),
|
||||
gold: new fields.SchemaField({
|
||||
|
|
@ -99,15 +95,6 @@ export default class DhCharacter extends BaseDataActor {
|
|||
}),
|
||||
levelData: new fields.EmbeddedDataField(DhLevelData),
|
||||
bonuses: new fields.SchemaField({
|
||||
armorScore: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
damageReduction: new fields.SchemaField({
|
||||
physical: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
magical: new fields.NumberField({ integer: true, initial: 0 })
|
||||
}),
|
||||
damageThresholds: new fields.SchemaField({
|
||||
severe: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
major: new fields.NumberField({ integer: true, initial: 0 })
|
||||
}),
|
||||
roll: new fields.SchemaField({
|
||||
attack: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
primaryWeapon: new fields.SchemaField({
|
||||
|
|
@ -306,7 +293,7 @@ export default class DhCharacter extends BaseDataActor {
|
|||
|
||||
get deathMoveViable() {
|
||||
return (
|
||||
this.resources.hitPoints.maxTotal > 0 && this.resources.hitPoints.value >= this.resources.hitPoints.maxTotal
|
||||
this.resources.hitPoints.max > 0 && this.resources.hitPoints.value >= this.resources.hitPoints.max
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -350,32 +337,32 @@ export default class DhCharacter extends BaseDataActor {
|
|||
for (let levelKey in this.levelData.levelups) {
|
||||
const level = this.levelData.levelups[levelKey];
|
||||
|
||||
this.proficiency.bonus += level.achievements.proficiency;
|
||||
this.proficiency += level.achievements.proficiency;
|
||||
|
||||
for (let selection of level.selections) {
|
||||
switch (selection.type) {
|
||||
case 'trait':
|
||||
selection.data.forEach(data => {
|
||||
this.traits[data].bonus += 1;
|
||||
this.traits[data].value += 1;
|
||||
this.traits[data].tierMarked = selection.tier === currentTier;
|
||||
});
|
||||
break;
|
||||
case 'hitPoint':
|
||||
this.resources.hitPoints.bonus += selection.value;
|
||||
this.resources.hitPoints.max += selection.value;
|
||||
break;
|
||||
case 'stress':
|
||||
this.resources.stress.bonus += selection.value;
|
||||
this.resources.stress.max += selection.value;
|
||||
break;
|
||||
case 'evasion':
|
||||
this.evasion.bonus += selection.value;
|
||||
this.evasion += selection.value;
|
||||
break;
|
||||
case 'proficiency':
|
||||
this.proficiency.bonus = selection.value;
|
||||
this.proficiency = selection.value;
|
||||
break;
|
||||
case 'experience':
|
||||
Object.keys(this.experiences).forEach(key => {
|
||||
const experience = this.experiences[key];
|
||||
experience.bonus += selection.value;
|
||||
experience.value += selection.value;
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
|
@ -383,7 +370,7 @@ export default class DhCharacter extends BaseDataActor {
|
|||
}
|
||||
|
||||
const armor = this.armor;
|
||||
this.armorScore = this.armor ? this.armor.system.baseScore + (this.bonuses.armorScore ?? 0) : 0; // Bonuses to armorScore won't have been applied yet. Need to solve in documentPreparation somehow
|
||||
this.armorScore = armor ? armor.system.baseScore : 0;
|
||||
this.damageThresholds = {
|
||||
major: armor
|
||||
? armor.system.baseThresholds.major + this.levelData.level.current
|
||||
|
|
@ -392,29 +379,12 @@ export default class DhCharacter extends BaseDataActor {
|
|||
? armor.system.baseThresholds.severe + this.levelData.level.current
|
||||
: this.levelData.level.current * 2
|
||||
};
|
||||
this.resources.hope.max -= Object.keys(this.scars).length;
|
||||
this.resources.hitPoints.max = this.class.value?.system?.hitPoints ?? 0;
|
||||
}
|
||||
|
||||
prepareDerivedData() {
|
||||
this.resources.hope.max -= Object.keys(this.scars).length;
|
||||
this.resources.hope.value = Math.min(this.resources.hope.value, this.resources.hope.max);
|
||||
|
||||
for (var traitKey in this.traits) {
|
||||
var trait = this.traits[traitKey];
|
||||
trait.total = (trait.value ?? 0) + trait.bonus;
|
||||
}
|
||||
|
||||
for (var experienceKey in this.experiences) {
|
||||
var experience = this.experiences[experienceKey];
|
||||
experience.total = experience.value + experience.bonus;
|
||||
}
|
||||
|
||||
this.rules.damageReduction.maxArmorMarked.total =
|
||||
this.rules.damageReduction.maxArmorMarked.value + this.rules.damageReduction.maxArmorMarked.bonus;
|
||||
|
||||
this.resources.hitPoints.maxTotal = (this.class.value?.system?.hitPoints ?? 0) + this.resources.hitPoints.bonus;
|
||||
this.resources.stress.maxTotal = this.resources.stress.max + this.resources.stress.bonus;
|
||||
this.evasion.total = (this.class?.evasion ?? 0) + this.evasion.bonus;
|
||||
this.proficiency.total = this.proficiency.value + this.proficiency.bonus;
|
||||
}
|
||||
|
||||
getRollData() {
|
||||
|
|
|
|||
|
|
@ -20,24 +20,21 @@ export default class DhCompanion extends BaseDataActor {
|
|||
const fields = foundry.data.fields;
|
||||
|
||||
return {
|
||||
...super.defineSchema(),
|
||||
partner: new ForeignDocumentUUIDField({ type: 'Actor' }),
|
||||
resources: new fields.SchemaField({
|
||||
stress: new fields.SchemaField({
|
||||
value: new fields.NumberField({ initial: 0, integer: true }),
|
||||
bonus: new fields.NumberField({ initial: 0, integer: true }),
|
||||
max: new fields.NumberField({ initial: 3, integer: true })
|
||||
max: new fields.NumberField({ initial: 3, integer: true }),
|
||||
isReversed: new foundry.data.fields.BooleanField({ initial: true })
|
||||
}),
|
||||
hope: new fields.NumberField({ initial: 0, integer: true })
|
||||
}),
|
||||
evasion: new fields.SchemaField({
|
||||
value: new fields.NumberField({ required: true, min: 1, initial: 10, integer: true }),
|
||||
bonus: new fields.NumberField({ initial: 0, integer: true })
|
||||
}),
|
||||
evasion: new fields.NumberField({ required: true, min: 1, initial: 10, integer: true }),
|
||||
experiences: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({}),
|
||||
value: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
bonus: new fields.NumberField({ integer: true, initial: 0 })
|
||||
value: new fields.NumberField({ integer: true, initial: 0 })
|
||||
}),
|
||||
{
|
||||
initial: {
|
||||
|
|
@ -66,10 +63,10 @@ export default class DhCompanion extends BaseDataActor {
|
|||
damage: {
|
||||
parts: [
|
||||
{
|
||||
multiplier: 'flat',
|
||||
type: ['physical'],
|
||||
value: {
|
||||
dice: 'd6',
|
||||
multiplier: 'flat'
|
||||
multiplier: 'prof'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -83,13 +80,17 @@ export default class DhCompanion extends BaseDataActor {
|
|||
|
||||
get traits() {
|
||||
return {
|
||||
instinct: { total: this.attack.roll.bonus }
|
||||
instinct: { value: this.attack.roll.bonus }
|
||||
};
|
||||
}
|
||||
|
||||
get proficiency() {
|
||||
return this.partner?.system?.proficiency ?? 1;
|
||||
}
|
||||
|
||||
prepareBaseData() {
|
||||
const partnerSpellcastingModifier = this.partner?.system?.spellcastingModifiers?.main;
|
||||
const spellcastingModifier = this.partner?.system?.traits?.[partnerSpellcastingModifier]?.total;
|
||||
const spellcastingModifier = this.partner?.system?.traits?.[partnerSpellcastingModifier]?.value;
|
||||
this.attack.roll.bonus = spellcastingModifier ?? 0; // Needs to expand on which modifier it is that should be used because of multiclassing;
|
||||
|
||||
for (let levelKey in this.levelData.levelups) {
|
||||
|
|
@ -107,15 +108,15 @@ export default class DhCompanion extends BaseDataActor {
|
|||
}
|
||||
break;
|
||||
case 'stress':
|
||||
this.resources.stress.bonus += selection.value;
|
||||
this.resources.stress.max += selection.value;
|
||||
break;
|
||||
case 'evasion':
|
||||
this.evasion.bonus += selection.value;
|
||||
this.evasion += selection.value;
|
||||
break;
|
||||
case 'experience':
|
||||
Object.keys(this.experiences).forEach(key => {
|
||||
const experience = this.experiences[key];
|
||||
experience.bonus += selection.value;
|
||||
experience.value += selection.value;
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
|
@ -124,17 +125,9 @@ export default class DhCompanion extends BaseDataActor {
|
|||
}
|
||||
|
||||
prepareDerivedData() {
|
||||
for (var experienceKey in this.experiences) {
|
||||
var experience = this.experiences[experienceKey];
|
||||
experience.total = experience.value + experience.bonus;
|
||||
}
|
||||
|
||||
if (this.partner) {
|
||||
this.partner.system.resources.hope.max += this.resources.hope;
|
||||
}
|
||||
|
||||
this.resources.stress.maxTotal = this.resources.stress.max + this.resources.stress.bonus;
|
||||
this.evasion.total = this.evasion.value + this.evasion.bonus;
|
||||
}
|
||||
|
||||
async _preDelete() {
|
||||
|
|
|
|||
|
|
@ -9,20 +9,21 @@ export default class DhEnvironment extends BaseDataActor {
|
|||
return foundry.utils.mergeObject(super.metadata, {
|
||||
label: 'TYPES.Actor.environment',
|
||||
type: 'environment',
|
||||
settingSheet: DHEnvironmentSettings
|
||||
settingSheet: DHEnvironmentSettings,
|
||||
hasResistances: false
|
||||
});
|
||||
}
|
||||
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
...super.defineSchema(),
|
||||
tier: new fields.StringField({
|
||||
required: true,
|
||||
choices: CONFIG.DH.GENERAL.tiers,
|
||||
initial: CONFIG.DH.GENERAL.tiers.tier1.id
|
||||
}),
|
||||
type: new fields.StringField({ choices: CONFIG.DH.ACTOR.environmentTypes }),
|
||||
description: new fields.StringField(),
|
||||
impulses: new fields.StringField(),
|
||||
difficulty: new fields.NumberField({ required: true, initial: 11, integer: true }),
|
||||
potentialAdversaries: new fields.TypedObjectField(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import DHAncestry from './ancestry.mjs';
|
||||
import DHArmor from './armor.mjs';
|
||||
import DHAttachableItem from './attachableItem.mjs';
|
||||
import DHClass from './class.mjs';
|
||||
import DHCommunity from './community.mjs';
|
||||
import DHConsumable from './consumable.mjs';
|
||||
|
|
@ -13,6 +14,7 @@ import DHBeastform from './beastform.mjs';
|
|||
export {
|
||||
DHAncestry,
|
||||
DHArmor,
|
||||
DHAttachableItem,
|
||||
DHClass,
|
||||
DHCommunity,
|
||||
DHConsumable,
|
||||
|
|
@ -27,6 +29,7 @@ export {
|
|||
export const config = {
|
||||
ancestry: DHAncestry,
|
||||
armor: DHArmor,
|
||||
attachableItem: DHAttachableItem,
|
||||
class: DHClass,
|
||||
community: DHCommunity,
|
||||
consumable: DHConsumable,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import BaseDataItem from './base.mjs';
|
||||
import AttachableItem from './attachableItem.mjs';
|
||||
import ActionField from '../fields/actionField.mjs';
|
||||
import { armorFeatures } from '../../config/itemConfig.mjs';
|
||||
import { actionsTypes } from '../action/_module.mjs';
|
||||
|
||||
export default class DHArmor extends BaseDataItem {
|
||||
export default class DHArmor extends AttachableItem {
|
||||
/** @inheritDoc */
|
||||
static get metadata() {
|
||||
return foundry.utils.mergeObject(super.metadata, {
|
||||
|
|
@ -44,6 +45,12 @@ export default class DHArmor extends BaseDataItem {
|
|||
};
|
||||
}
|
||||
|
||||
get customActions() {
|
||||
return this.actions.filter(
|
||||
action => !this.armorFeatures.some(feature => feature.actionIds.includes(action.id))
|
||||
);
|
||||
}
|
||||
|
||||
async _preUpdate(changes, options, user) {
|
||||
const allowed = await super._preUpdate(changes, options, user);
|
||||
if (allowed === false) return false;
|
||||
|
|
|
|||
152
module/data/item/attachableItem.mjs
Normal file
152
module/data/item/attachableItem.mjs
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
import BaseDataItem from './base.mjs';
|
||||
|
||||
export default class AttachableItem extends BaseDataItem {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
...super.defineSchema(),
|
||||
attached: new fields.ArrayField(new fields.DocumentUUIDField({ type: "Item", nullable: true }))
|
||||
};
|
||||
}
|
||||
|
||||
async _preUpdate(changes, options, user) {
|
||||
const allowed = await super._preUpdate(changes, options, user);
|
||||
if (allowed === false) return false;
|
||||
|
||||
// Handle equipped status changes for attachment effects
|
||||
if (changes.system?.equipped !== undefined && changes.system.equipped !== this.equipped) {
|
||||
await this.#handleAttachmentEffectsOnEquipChange(changes.system.equipped);
|
||||
}
|
||||
}
|
||||
|
||||
async #handleAttachmentEffectsOnEquipChange(newEquippedStatus) {
|
||||
const actor = this.parent.parent?.type === 'character' ? this.parent.parent : this.parent.parent?.parent;
|
||||
const parentType = this.parent.type;
|
||||
|
||||
if (!actor || !this.attached?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newEquippedStatus) {
|
||||
// Item is being equipped - add attachment effects
|
||||
for (const attachedUuid of this.attached) {
|
||||
const attachedItem = await fromUuid(attachedUuid);
|
||||
if (attachedItem && attachedItem.effects.size > 0) {
|
||||
await this.#copyAttachmentEffectsToActor({
|
||||
attachedItem,
|
||||
attachedUuid,
|
||||
parentType
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Item is being unequipped - remove attachment effects
|
||||
await this.#removeAllAttachmentEffects(parentType);
|
||||
}
|
||||
}
|
||||
|
||||
async #copyAttachmentEffectsToActor({ attachedItem, attachedUuid, parentType }) {
|
||||
const actor = this.parent.parent;
|
||||
if (!actor || !attachedItem.effects.size > 0 || !this.equipped) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const effectsToCreate = [];
|
||||
for (const effect of attachedItem.effects) {
|
||||
const effectData = effect.toObject();
|
||||
effectData.origin = `${this.parent.uuid}:${attachedUuid}`;
|
||||
|
||||
const attachmentSource = {
|
||||
itemUuid: attachedUuid,
|
||||
originalEffectId: effect.id
|
||||
};
|
||||
attachmentSource[`${parentType}Uuid`] = this.parent.uuid;
|
||||
|
||||
effectData.flags = {
|
||||
...effectData.flags,
|
||||
[CONFIG.DH.id]: {
|
||||
...effectData.flags?.[CONFIG.DH.id],
|
||||
[CONFIG.DH.FLAGS.itemAttachmentSource]: attachmentSource
|
||||
}
|
||||
};
|
||||
effectsToCreate.push(effectData);
|
||||
}
|
||||
|
||||
if (effectsToCreate.length > 0) {
|
||||
return await actor.createEmbeddedDocuments('ActiveEffect', effectsToCreate);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
async #removeAllAttachmentEffects(parentType) {
|
||||
const actor = this.parent.parent;
|
||||
if (!actor) return;
|
||||
|
||||
const parentUuidProperty = `${parentType}Uuid`;
|
||||
const effectsToRemove = actor.effects.filter(effect => {
|
||||
const attachmentSource = effect.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.itemAttachmentSource);
|
||||
return attachmentSource && attachmentSource[parentUuidProperty] === this.parent.uuid;
|
||||
});
|
||||
|
||||
if (effectsToRemove.length > 0) {
|
||||
await actor.deleteEmbeddedDocuments('ActiveEffect', effectsToRemove.map(e => e.id));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Public method for adding an attachment
|
||||
*/
|
||||
async addAttachment(droppedItem) {
|
||||
const newUUID = droppedItem.uuid;
|
||||
|
||||
if (this.attached.includes(newUUID)) {
|
||||
ui.notifications.warn(`${droppedItem.name} is already attached to this ${this.parent.type}.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedAttached = [...this.attached, newUUID];
|
||||
await this.parent.update({
|
||||
'system.attached': updatedAttached
|
||||
});
|
||||
|
||||
// Copy effects if equipped
|
||||
if (this.equipped && droppedItem.effects.size > 0) {
|
||||
await this.#copyAttachmentEffectsToActor({
|
||||
attachedItem: droppedItem,
|
||||
attachedUuid: newUUID,
|
||||
parentType: this.parent.type
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Public method for removing an attachment
|
||||
*/
|
||||
async removeAttachment(attachedUuid) {
|
||||
await this.parent.update({
|
||||
'system.attached': this.attached.filter(uuid => uuid !== attachedUuid)
|
||||
});
|
||||
|
||||
// Remove effects
|
||||
await this.#removeAttachmentEffects(attachedUuid);
|
||||
}
|
||||
|
||||
async #removeAttachmentEffects(attachedUuid) {
|
||||
const actor = this.parent.parent;
|
||||
if (!actor) return;
|
||||
|
||||
const parentType = this.parent.type;
|
||||
const parentUuidProperty = `${parentType}Uuid`;
|
||||
const effectsToRemove = actor.effects.filter(effect => {
|
||||
const attachmentSource = effect.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.itemAttachmentSource);
|
||||
return attachmentSource &&
|
||||
attachmentSource[parentUuidProperty] === this.parent.uuid &&
|
||||
attachmentSource.itemUuid === attachedUuid;
|
||||
});
|
||||
|
||||
if (effectsToRemove.length > 0) {
|
||||
await actor.deleteEmbeddedDocuments('ActiveEffect', effectsToRemove.map(e => e.id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -32,9 +32,9 @@ export default class DHSubclass extends BaseDataItem {
|
|||
|
||||
get features() {
|
||||
return [
|
||||
{ ...this.foundationFeature.toObject(), identifier: 'foundationFeature' },
|
||||
{ ...this.specializationFeature.toObject(), identifier: 'specializationFeature' },
|
||||
{ ...this.masteryFeature.toObject(), identifier: 'masteryFeature' }
|
||||
{ ...this.foundationFeature?.toObject(), identifier: 'foundationFeature' },
|
||||
{ ...this.specializationFeature?.toObject(), identifier: 'specializationFeature' },
|
||||
{ ...this.masteryFeature?.toObject(), identifier: 'masteryFeature' }
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import BaseDataItem from './base.mjs';
|
||||
import AttachableItem from './attachableItem.mjs';
|
||||
import { actionsTypes } from '../action/_module.mjs';
|
||||
import ActionField from '../fields/actionField.mjs';
|
||||
|
||||
export default class DHWeapon extends BaseDataItem {
|
||||
export default class DHWeapon extends AttachableItem {
|
||||
/** @inheritDoc */
|
||||
static get metadata() {
|
||||
return foundry.utils.mergeObject(super.metadata, {
|
||||
|
|
@ -37,7 +37,7 @@ export default class DHWeapon extends BaseDataItem {
|
|||
actionIds: new fields.ArrayField(new fields.StringField({ required: true }))
|
||||
})
|
||||
),
|
||||
attack: new ActionField({
|
||||
attack: new ActionField({
|
||||
initial: {
|
||||
name: 'Attack',
|
||||
img: 'icons/skills/melee/blood-slash-foam-red.webp',
|
||||
|
|
@ -56,6 +56,7 @@ export default class DHWeapon extends BaseDataItem {
|
|||
damage: {
|
||||
parts: [
|
||||
{
|
||||
type: ['physical'],
|
||||
value: {
|
||||
multiplier: 'prof',
|
||||
dice: 'd8'
|
||||
|
|
@ -73,6 +74,12 @@ export default class DHWeapon extends BaseDataItem {
|
|||
return [this.attack, ...this.actions];
|
||||
}
|
||||
|
||||
get customActions() {
|
||||
return this.actions.filter(
|
||||
action => !this.weaponFeatures.some(feature => feature.actionIds.includes(action.id))
|
||||
);
|
||||
}
|
||||
|
||||
async _preUpdate(changes, options, user) {
|
||||
const allowed = await super._preUpdate(changes, options, user);
|
||||
if (allowed === false) return false;
|
||||
|
|
|
|||
|
|
@ -40,6 +40,10 @@ export default class DhAppearance extends foundry.abstract.DataModel {
|
|||
outline: new fields.ColorField({ required: true, initial: '#ffffff' }),
|
||||
edge: new fields.ColorField({ required: true, initial: '#000000' })
|
||||
})
|
||||
}),
|
||||
showGenericStatusEffects: new fields.BooleanField({
|
||||
initial: true,
|
||||
label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.showGenericStatusEffects.label'
|
||||
})
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ export default class DhRangeMeasurement extends foundry.abstract.DataModel {
|
|||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
enabled: new fields.BooleanField({ required: true, initial: false, label: 'DAGGERHEART.GENERAL.enabled' }),
|
||||
enabled: new fields.BooleanField({ required: true, initial: true, label: 'DAGGERHEART.GENERAL.enabled' }),
|
||||
melee: new fields.NumberField({ required: true, initial: 5, label: 'DAGGERHEART.CONFIG.Range.melee.name' }),
|
||||
veryClose: new fields.NumberField({
|
||||
required: true,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue