Feature/132 weapon armor features (#136)

* Added effects for Weapon and Armor traits

* Fixed so @ strings can be parsed as active effect values

* Added actions
This commit is contained in:
WBHarry 2025-06-15 13:18:36 +02:00 committed by GitHub
parent 6adbb4e49b
commit 5b94675db1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 598 additions and 48 deletions

View file

@ -264,6 +264,7 @@
} }
}, },
"Tiers": { "Tiers": {
"singular": "Tier",
"tier1": "Tier 1", "tier1": "Tier 1",
"tier2": "Tier 2", "tier2": "Tier 2",
"tier3": "Tier 3", "tier3": "Tier 3",
@ -622,7 +623,7 @@
"WeaponFeature": { "WeaponFeature": {
"Barrier": { "Barrier": {
"Name": "Barrier", "Name": "Barrier",
"Description": "+{armorBonus} to Armor Score; -1 to Evasion" "Description": "+{armorScore} to Armor Score; -1 to Evasion"
}, },
"Bonded": { "Bonded": {
"Name": "Bonded", "Name": "Bonded",
@ -692,6 +693,10 @@
"Name": "Greedy", "Name": "Greedy",
"Description": "Spend a handful of gold to gain a +1 bonus to your Proficiency on a damage roll." "Description": "Spend a handful of gold to gain a +1 bonus to your Proficiency on a damage roll."
}, },
"Healing": {
"Name": "Healing",
"Description": "During downtime, automatically clear a Hit Point."
},
"Heavy": { "Heavy": {
"Name": "Heavy", "Name": "Heavy",
"Description": "-1 to Evasion" "Description": "-1 to Evasion"
@ -716,6 +721,10 @@
"Name": "Locked On", "Name": "Locked On",
"Description": "On a successful attack, your next attack against the same target with your primary weapon automatically succeeds." "Description": "On a successful attack, your next attack against the same target with your primary weapon automatically succeeds."
}, },
"Lucky": {
"Name": "Lucky",
"Description": "On a failed attack, you can mark a Stress to reroll your attack."
},
"Long": { "Long": {
"Name": "Long", "Name": "Long",
"Description": "This weapon's attack targets all adversaries in a line within range." "Description": "This weapon's attack targets all adversaries in a line within range."
@ -750,7 +759,7 @@
}, },
"Protective": { "Protective": {
"Name": "Protective", "Name": "Protective",
"Description": "+{armorBonus} to Armor Score" "Description": "+{tier} to Armor Score"
}, },
"Quick": { "Quick": {
"Name": "Quick", "Name": "Quick",

View file

@ -1,3 +1,5 @@
import { armorFeatures } from '../../../config/itemConfig.mjs';
import { tagifyElement } from '../../../helpers/utils.mjs';
import DHItemSheetV2 from '../item.mjs'; import DHItemSheetV2 from '../item.mjs';
const { ItemSheetV2 } = foundry.applications.sheets; const { ItemSheetV2 } = foundry.applications.sheets;
@ -20,4 +22,28 @@ export default class ArmorSheet extends DHItemSheetV2(ItemSheetV2) {
scrollable: ['.settings'] scrollable: ['.settings']
} }
}; };
async _preparePartContext(partId, context) {
super._preparePartContext(partId, context);
switch (partId) {
case 'settings':
context.features = this.document.system.features.map(x => x.value);
break;
}
return context;
}
_attachPartListeners(partId, htmlElement, options) {
super._attachPartListeners(partId, htmlElement, options);
const featureInput = htmlElement.querySelector('.features-input');
tagifyElement(featureInput, armorFeatures, this.onFeatureSelect.bind(this));
}
async onFeatureSelect(features) {
await this.document.update({ 'system.features': features.map(x => ({ value: x.value })) });
this.render(true);
}
} }

View file

@ -1,3 +1,5 @@
import { weaponFeatures } from '../../../config/itemConfig.mjs';
import { tagifyElement } from '../../../helpers/utils.mjs';
import DHItemSheetV2 from '../item.mjs'; import DHItemSheetV2 from '../item.mjs';
const { ItemSheetV2 } = foundry.applications.sheets; const { ItemSheetV2 } = foundry.applications.sheets;
@ -19,4 +21,28 @@ export default class WeaponSheet extends DHItemSheetV2(ItemSheetV2) {
scrollable: ['.settings'] scrollable: ['.settings']
} }
}; };
async _preparePartContext(partId, context) {
super._preparePartContext(partId, context);
switch (partId) {
case 'settings':
context.features = this.document.system.features.map(x => x.value);
break;
}
return context;
}
_attachPartListeners(partId, htmlElement, options) {
super._attachPartListeners(partId, htmlElement, options);
const featureInput = htmlElement.querySelector('.features-input');
tagifyElement(featureInput, weaponFeatures, this.onFeatureSelect.bind(this));
}
async onFeatureSelect(features) {
await this.document.update({ 'system.features': features.map(x => ({ value: x.value })) });
this.render(true);
}
} }

View file

@ -5,15 +5,78 @@ export const armorFeatures = {
}, },
channeling: { channeling: {
label: 'DAGGERHEART.ArmorFeature.Channeling.Name', label: 'DAGGERHEART.ArmorFeature.Channeling.Name',
description: 'DAGGERHEART.ArmorFeature.Channeling.Description' description: 'DAGGERHEART.ArmorFeature.Channeling.Description',
effects: [
{
changes: [
{
key: 'system.bonuses.spellcast',
mode: 2,
value: '1'
}
]
}
]
}, },
difficult: { difficult: {
label: 'DAGGERHEART.ArmorFeature.Difficult.Name', label: 'DAGGERHEART.ArmorFeature.Difficult.Name',
description: 'DAGGERHEART.ArmorFeature.Difficult.Description' description: 'DAGGERHEART.ArmorFeature.Difficult.Description',
effects: [
{
changes: [
{
key: 'system.traits.agility.bonus',
mode: 2,
value: '-1'
},
{
key: 'system.traits.strength.bonus',
mode: 2,
value: '-1'
},
{
key: 'system.traits.finesse.bonus',
mode: 2,
value: '-1'
},
{
key: 'system.traits.instinct.bonus',
mode: 2,
value: '-1'
},
{
key: 'system.traits.presence.bonus',
mode: 2,
value: '-1'
},
{
key: 'system.traits.knowledge.bonus',
mode: 2,
value: '-1'
},
{
key: 'system.evasion.bonus',
mode: 2,
value: '-1'
}
]
}
]
}, },
flexible: { flexible: {
label: 'DAGGERHEART.ArmorFeature.Flexible.Name', label: 'DAGGERHEART.ArmorFeature.Flexible.Name',
description: 'DAGGERHEART.ArmorFeature.Flexible.Description' description: 'DAGGERHEART.ArmorFeature.Flexible.Description',
effects: [
{
changes: [
{
key: 'system.evasion.bonus',
mode: 2,
value: '1'
}
]
}
]
}, },
fortified: { fortified: {
label: 'DAGGERHEART.ArmorFeature.Fortified.Name', label: 'DAGGERHEART.ArmorFeature.Fortified.Name',
@ -21,11 +84,33 @@ export const armorFeatures = {
}, },
gilded: { gilded: {
label: 'DAGGERHEART.ArmorFeature.Gilded.Name', label: 'DAGGERHEART.ArmorFeature.Gilded.Name',
description: 'DAGGERHEART.ArmorFeature.Gilded.Description' description: 'DAGGERHEART.ArmorFeature.Gilded.Description',
effects: [
{
changes: [
{
key: 'system.traits.presence.bonus',
mode: 2,
value: '1'
}
]
}
]
}, },
heavy: { heavy: {
label: 'DAGGERHEART.ArmorFeature.Heavy.Name', label: 'DAGGERHEART.ArmorFeature.Heavy.Name',
description: 'DAGGERHEART.ArmorFeature.Heavy.Description' description: 'DAGGERHEART.ArmorFeature.Heavy.Description',
effects: [
{
changes: [
{
key: 'system.evasion.bonus',
mode: 2,
value: '-1'
}
]
}
]
}, },
hopeful: { hopeful: {
label: 'DAGGERHEART.ArmorFeature.Hopeful.Name', label: 'DAGGERHEART.ArmorFeature.Hopeful.Name',
@ -77,7 +162,23 @@ export const armorFeatures = {
}, },
veryheavy: { veryheavy: {
label: 'DAGGERHEART.ArmorFeature.VeryHeavy.Name', label: 'DAGGERHEART.ArmorFeature.VeryHeavy.Name',
description: 'DAGGERHEART.ArmorFeature.VeryHeavy.Description' description: 'DAGGERHEART.ArmorFeature.VeryHeavy.Description',
effects: [
{
changes: [
{
key: 'system.evasion.bonus',
mode: 2,
value: '-2'
},
{
key: 'system.traits.agility.bonus',
mode: 2,
value: '-1'
}
]
}
]
}, },
warded: { warded: {
label: 'DAGGERHEART.ArmorFeature.Warded.Name', label: 'DAGGERHEART.ArmorFeature.Warded.Name',
@ -89,13 +190,41 @@ export const weaponFeatures = {
barrier: { barrier: {
label: 'DAGGERHEART.WeaponFeature.Barrier.Name', label: 'DAGGERHEART.WeaponFeature.Barrier.Name',
description: 'DAGGERHEART.WeaponFeature.Barrier.Description', description: 'DAGGERHEART.WeaponFeature.Barrier.Description',
override: { effects: [
armorBonus: 1 {
} changes: [
{
key: 'system.bonuses.armorScore',
mode: 2,
value: '@system.tier + 1'
}
]
},
{
changes: [
{
key: 'system.evasion.bonus',
mode: 2,
value: '-1'
}
]
}
]
}, },
bonded: { bonded: {
label: 'DAGGERHEART.WeaponFeature.Bonded.Name', label: 'DAGGERHEART.WeaponFeature.Bonded.Name',
description: 'DAGGERHEART.WeaponFeature.Bonded.Description' description: 'DAGGERHEART.WeaponFeature.Bonded.Description',
effects: [
{
changes: [
{
key: 'system.bonuses.damage',
mode: 2,
value: 'system.levelData.levels.current'
}
]
}
]
}, },
bouncing: { bouncing: {
label: 'DAGGERHEART.WeaponFeature.Bouncing.Name', label: 'DAGGERHEART.WeaponFeature.Bouncing.Name',
@ -103,7 +232,27 @@ export const weaponFeatures = {
}, },
brave: { brave: {
label: 'DAGGERHEART.WeaponFeature.Brave.Name', label: 'DAGGERHEART.WeaponFeature.Brave.Name',
description: 'DAGGERHEART.WeaponFeature.Brave.Description' description: 'DAGGERHEART.WeaponFeature.Brave.Description',
effects: [
{
changes: [
{
key: 'system.evasion.bonus',
mode: 2,
value: '-1'
}
]
},
{
changes: [
{
key: 'system.damageThresholds.severe',
mode: 2,
value: '3'
}
]
}
]
}, },
brutal: { brutal: {
label: 'DAGGERHEART.WeaponFeature.Brutal.Name', label: 'DAGGERHEART.WeaponFeature.Brutal.Name',
@ -111,15 +260,55 @@ export const weaponFeatures = {
}, },
charged: { charged: {
label: 'DAGGERHEART.WeaponFeature.Charged.Name', label: 'DAGGERHEART.WeaponFeature.Charged.Name',
description: 'DAGGERHEART.WeaponFeature.Charged.Description' description: 'DAGGERHEART.WeaponFeature.Charged.Description',
actions: [
{
type: 'effect',
name: 'DAGGERHEART.WeaponFeature.Concussive.Name',
img: 'icons/skills/melee/shield-damaged-broken-brown.webp',
actionType: 'action',
cost: [
{
type: 'stress',
value: 1
}
]
// Should add an effect with path system.proficiency.bonus +1
}
]
}, },
concussive: { concussive: {
label: 'DAGGERHEART.WeaponFeature.Concussive.Name', label: 'DAGGERHEART.WeaponFeature.Concussive.Name',
description: 'DAGGERHEART.WeaponFeature.Concussive.Description' description: 'DAGGERHEART.WeaponFeature.Concussive.Description',
actions: [
{
type: 'resource',
name: 'DAGGERHEART.WeaponFeature.Concussive.Name',
img: 'icons/skills/melee/shield-damaged-broken-brown.webp',
actionType: 'action',
cost: [
{
type: 'hope',
value: 1
}
]
}
]
}, },
cumbersome: { cumbersome: {
label: 'DAGGERHEART.WeaponFeature.Cumbersome.Name', label: 'DAGGERHEART.WeaponFeature.Cumbersome.Name',
description: 'DAGGERHEART.WeaponFeature.Cumbersome.Description' description: 'DAGGERHEART.WeaponFeature.Cumbersome.Description',
effects: [
{
changes: [
{
key: 'system.traits.finesse.bonus',
mode: 2,
value: '-1'
}
]
}
]
}, },
deadly: { deadly: {
label: 'DAGGERHEART.WeaponFeature.Deadly.Name', label: 'DAGGERHEART.WeaponFeature.Deadly.Name',
@ -128,18 +317,64 @@ export const weaponFeatures = {
deflecting: { deflecting: {
label: 'DAGGERHEART.WeaponFeature.Deflecting.Name', label: 'DAGGERHEART.WeaponFeature.Deflecting.Name',
description: 'DAGGERHEART.WeaponFeature.Deflecting.Description' description: 'DAGGERHEART.WeaponFeature.Deflecting.Description'
// actions: [{
// type: 'effect',
// name: 'DAGGERHEART.WeaponFeature.Deflecting.Name',
// img: 'icons/skills/melee/strike-flail-destructive-yellow.webp',
// actionType: 'reaction',
// cost: [{
// type: 'armorSlot', // Needs armorSlot as type
// value: 1
// }],
// }],
}, },
destructive: { destructive: {
label: 'DAGGERHEART.WeaponFeature.Destructive.Name', label: 'DAGGERHEART.WeaponFeature.Destructive.Name',
description: 'DAGGERHEART.WeaponFeature.Destructive.Description' description: 'DAGGERHEART.WeaponFeature.Destructive.Description',
effects: [
{
changes: [
{
key: 'system.traits.agility.bonus',
mode: 2,
value: '-1'
}
]
}
]
}, },
devastating: { devastating: {
label: 'DAGGERHEART.WeaponFeature.Devastating.Name', label: 'DAGGERHEART.WeaponFeature.Devastating.Name',
description: 'DAGGERHEART.WeaponFeature.Devastating.Description' description: 'DAGGERHEART.WeaponFeature.Devastating.Description',
actions: [
{
type: 'resource',
name: 'DAGGERHEART.WeaponFeature.Devastating.Name',
img: 'icons/skills/melee/strike-flail-destructive-yellow.webp',
actionType: 'action',
cost: [
{
type: 'stress',
value: 1
}
]
}
]
}, },
doubleduty: { doubleduty: {
label: 'DAGGERHEART.WeaponFeature.DoubleDuty.Name', label: 'DAGGERHEART.WeaponFeature.DoubleDuty.Name',
description: 'DAGGERHEART.WeaponFeature.DoubleDuty.Description' description: 'DAGGERHEART.WeaponFeature.DoubleDuty.Description',
effects: [
{
changes: [
{
key: 'system.bonuses.armorScore',
mode: 2,
value: '1'
}
]
}
]
}, },
doubledup: { doubledup: {
label: 'DAGGERHEART.WeaponFeature.DoubledUp.Name', label: 'DAGGERHEART.WeaponFeature.DoubledUp.Name',
@ -155,15 +390,61 @@ export const weaponFeatures = {
}, },
grappling: { grappling: {
label: 'DAGGERHEART.WeaponFeature.Grappling.Name', label: 'DAGGERHEART.WeaponFeature.Grappling.Name',
description: 'DAGGERHEART.WeaponFeature.Grappling.Description' description: 'DAGGERHEART.WeaponFeature.Grappling.Description',
actions: [
{
type: 'resource',
name: 'DAGGERHEART.WeaponFeature.Grappling.Name',
img: 'icons/magic/control/debuff-chains-ropes-net-white.webp',
actionType: 'action',
cost: [
{
type: 'stress',
value: 1
}
]
}
]
}, },
greedy: { greedy: {
label: 'DAGGERHEART.WeaponFeature.Greedy.Name', label: 'DAGGERHEART.WeaponFeature.Greedy.Name',
description: 'DAGGERHEART.WeaponFeature.Greedy.Description' description: 'DAGGERHEART.WeaponFeature.Greedy.Description'
}, },
healing: {
label: 'DAGGERHEART.WeaponFeature.Healing.Name',
description: 'DAGGERHEART.WeaponFeature.Healing.Description',
actions: [
{
type: 'healing',
name: 'DAGGERHEART.WeaponFeature.Healing.Name',
img: 'icons/magic/life/cross-beam-green.webp',
actionType: 'action',
healing: {
type: 'health',
value: {
custom: {
enabled: true,
formula: '1'
}
}
}
}
]
},
heavy: { heavy: {
label: 'DAGGERHEART.WeaponFeature.Heavy.Name', label: 'DAGGERHEART.WeaponFeature.Heavy.Name',
description: 'DAGGERHEART.WeaponFeature.Heavy.Description' description: 'DAGGERHEART.WeaponFeature.Heavy.Description',
effects: [
{
changes: [
{
key: 'system.evasion.bonus',
mode: 2,
value: '-1'
}
]
}
]
}, },
hooked: { hooked: {
label: 'DAGGERHEART.WeaponFeature.Hooked.Name', label: 'DAGGERHEART.WeaponFeature.Hooked.Name',
@ -189,13 +470,56 @@ export const weaponFeatures = {
label: 'DAGGERHEART.WeaponFeature.Long.Name', label: 'DAGGERHEART.WeaponFeature.Long.Name',
description: 'DAGGERHEART.WeaponFeature.Long.Description' description: 'DAGGERHEART.WeaponFeature.Long.Description'
}, },
lucky: {
label: 'DAGGERHEART.WeaponFeature.Lucky.Name',
description: 'DAGGERHEART.WeaponFeature.Lucky.Description',
actions: [
{
type: 'resource',
name: 'DAGGERHEART.WeaponFeature.Lucky.Name',
img: 'icons/magic/control/buff-luck-fortune-green.webp',
actionType: 'action',
cost: [
{
type: 'stress',
value: 1
}
]
}
]
},
massive: { massive: {
label: 'DAGGERHEART.WeaponFeature.Massive.Name', label: 'DAGGERHEART.WeaponFeature.Massive.Name',
description: 'DAGGERHEART.WeaponFeature.Massive.Description' description: 'DAGGERHEART.WeaponFeature.Massive.Description',
effects: [
{
changes: [
{
key: 'system.evasion.bonus',
mode: 2,
value: '-1'
}
]
}
]
}, },
painful: { painful: {
label: 'DAGGERHEART.WeaponFeature.Painful.Name', label: 'DAGGERHEART.WeaponFeature.Painful.Name',
description: 'DAGGERHEART.WeaponFeature.Painful.Description' description: 'DAGGERHEART.WeaponFeature.Painful.Description',
actions: [
{
type: 'resource',
name: 'DAGGERHEART.WeaponFeature.Painful.Name',
img: 'icons/skills/wounds/injury-face-impact-orange.webp',
actionType: 'action',
cost: [
{
type: 'stress',
value: 1
}
]
}
]
}, },
paired: { paired: {
label: 'DAGGERHEART.WeaponFeature.Paired.Name', label: 'DAGGERHEART.WeaponFeature.Paired.Name',
@ -223,17 +547,50 @@ export const weaponFeatures = {
protective: { protective: {
label: 'DAGGERHEART.WeaponFeature.Protective.Name', label: 'DAGGERHEART.WeaponFeature.Protective.Name',
description: 'DAGGERHEART.WeaponFeature.Protective.Description', description: 'DAGGERHEART.WeaponFeature.Protective.Description',
override: { effects: [
armorBonus: 1 {
} changes: [
{
key: 'system.bonuses.armorScore',
mode: 2,
value: '@system.tier'
}
]
}
]
}, },
quick: { quick: {
label: 'DAGGERHEART.WeaponFeature.Quick.Name', label: 'DAGGERHEART.WeaponFeature.Quick.Name',
description: 'DAGGERHEART.WeaponFeature.Quick.Description' description: 'DAGGERHEART.WeaponFeature.Quick.Description',
actions: [
{
type: 'resource',
name: 'DAGGERHEART.WeaponFeature.Quick.Name',
img: 'icons/skills/movement/arrow-upward-yellow.webp',
actionType: 'action',
cost: [
{
type: 'stress',
value: 1
}
]
}
]
}, },
reliable: { reliable: {
label: 'DAGGERHEART.WeaponFeature.Reliable.Name', label: 'DAGGERHEART.WeaponFeature.Reliable.Name',
description: 'DAGGERHEART.WeaponFeature.Reliable.Description' description: 'DAGGERHEART.WeaponFeature.Reliable.Description',
effects: [
{
changes: [
{
key: 'system.bonuses.attack',
mode: 2,
value: 1
}
]
}
]
}, },
reloading: { reloading: {
label: 'DAGGERHEART.WeaponFeature.Reloading.Name', label: 'DAGGERHEART.WeaponFeature.Reloading.Name',
@ -265,7 +622,21 @@ export const weaponFeatures = {
}, },
startling: { startling: {
label: 'DAGGERHEART.WeaponFeature.Startling.Name', label: 'DAGGERHEART.WeaponFeature.Startling.Name',
description: 'DAGGERHEART.WeaponFeature.Startling.Description' description: 'DAGGERHEART.WeaponFeature.Startling.Description',
actions: [
{
type: 'resource',
name: 'DAGGERHEART.WeaponFeature.Startling.Name',
img: 'icons/magic/control/fear-fright-mask-orange.webp',
actionType: 'action',
cost: [
{
type: 'stress',
value: 1
}
]
}
]
}, },
timebending: { timebending: {
label: 'DAGGERHEART.WeaponFeature.Timebending.Name', label: 'DAGGERHEART.WeaponFeature.Timebending.Name',

View file

@ -132,7 +132,8 @@ export class DHBaseAction extends foundry.abstract.DataModel {
async use(event) { async use(event) {
if (this.roll.type && this.roll.trait) { if (this.roll.type && this.roll.trait) {
const modifierValue = this.actor.system.traits[this.roll.trait].value; const modifierValue =
this.actor.system.traits[this.roll.trait].value + (this.actor.system.bonuses.attack ?? 0);
const config = { const config = {
event: event, event: event,
title: this.chatTitle, title: this.chatTitle,

View file

@ -23,7 +23,7 @@ export class DHActionDiceData extends foundry.abstract.DataModel {
getFormula(actor) { getFormula(actor) {
return this.custom.enabled return this.custom.enabled
? this.custom.formula ? this.custom.formula
: `${actor.system[this.multiplier] ?? 1}${this.dice}${this.bonus ? (this.bonus < 0 ? ` - ${Math.abs(this.bonus)}` : ` + ${this.bonus}`) : ''}`; : `${actor.system[this.multiplier]?.total ?? 1}${this.dice}${this.bonus ? (this.bonus < 0 ? ` - ${Math.abs(this.bonus)}` : ` + ${this.bonus}`) : ''}`;
} }
} }

View file

@ -84,7 +84,12 @@ export default class DhCharacter extends BaseDataActor {
value: new ForeignDocumentUUIDField({ type: 'Item', nullable: true }), value: new ForeignDocumentUUIDField({ type: 'Item', nullable: true }),
subclass: new ForeignDocumentUUIDField({ type: 'Item', nullable: true }) subclass: new ForeignDocumentUUIDField({ type: 'Item', nullable: true })
}), }),
levelData: new fields.EmbeddedDataField(DhPCLevelData) levelData: new fields.EmbeddedDataField(DhPCLevelData),
bonuses: new fields.SchemaField({
attack: new fields.NumberField({ integer: true, initial: 0 }),
spellcast: new fields.NumberField({ integer: true, initial: 0 }),
armorScore: new fields.NumberField({ integer: true, initial: 0 })
})
}; };
} }

View file

@ -1,14 +1,15 @@
import BaseDataItem from './base.mjs'; import BaseDataItem from './base.mjs';
import ActionField from '../fields/actionField.mjs'; import ActionField from '../fields/actionField.mjs';
import { armorFeatures } from '../../config/itemConfig.mjs';
export default class DHArmor extends BaseDataItem { export default class DHArmor extends BaseDataItem {
/** @inheritDoc */ /** @inheritDoc */
static get metadata() { static get metadata() {
return foundry.utils.mergeObject(super.metadata, { return foundry.utils.mergeObject(super.metadata, {
label: "TYPES.Item.armor", label: 'TYPES.Item.armor',
type: "armor", type: 'armor',
hasDescription: true, hasDescription: true,
isQuantifiable: true, isQuantifiable: true
}); });
} }
@ -17,9 +18,16 @@ export default class DHArmor extends BaseDataItem {
const fields = foundry.data.fields; const fields = foundry.data.fields;
return { return {
...super.defineSchema(), ...super.defineSchema(),
tier: new fields.NumberField({ required: true, integer: true, initial: 1, min: 1 }),
equipped: new fields.BooleanField({ initial: false }), equipped: new fields.BooleanField({ initial: false }),
baseScore: new fields.NumberField({ integer: true, initial: 0 }), baseScore: new fields.NumberField({ integer: true, initial: 0 }),
feature: new fields.StringField({ choices: SYSTEM.ITEM.armorFeatures, blank: true }), features: new fields.ArrayField(
new fields.SchemaField({
value: new fields.StringField({ required: true, choices: SYSTEM.ITEM.armorFeatures, blank: true }),
effectIds: new fields.ArrayField(new fields.StringField({ required: true })),
actionIds: new fields.ArrayField(new fields.StringField({ required: true }))
})
),
marks: new fields.SchemaField({ marks: new fields.SchemaField({
max: new fields.NumberField({ initial: 6, integer: true }), max: new fields.NumberField({ initial: 6, integer: true }),
value: new fields.NumberField({ initial: 0, integer: true }) value: new fields.NumberField({ initial: 0, integer: true })
@ -35,4 +43,47 @@ export default class DHArmor extends BaseDataItem {
get featureInfo() { get featureInfo() {
return this.feature ? CONFIG.daggerheart.ITEM.armorFeatures[this.feature] : null; return this.feature ? CONFIG.daggerheart.ITEM.armorFeatures[this.feature] : null;
} }
async _preUpdate(changes, options, user) {
const allowed = await super._preUpdate(changes, options, user);
if (allowed === false) return false;
if (changes.system.features) {
const removed = this.features.filter(x => !changes.system.features.includes(x));
const added = changes.system.features.filter(x => !this.features.includes(x));
for (var feature of removed) {
for (var effectId of feature.effectIds) {
await this.parent.effects.get(effectId).delete();
}
changes.system.actions = this.actions.filter(x => !feature.actionIds.includes(x._id));
}
for (var feature of added) {
const featureData = armorFeatures[feature.value];
if (featureData.effects?.length > 0) {
const embeddedItems = await this.parent.createEmbeddedDocuments('ActiveEffect', [
{
name: game.i18n.localize(featureData.label),
description: game.i18n.localize(featureData.description),
changes: featureData.effects.flatMap(x => x.changes)
}
]);
feature.effectIds = embeddedItems.map(x => x.id);
}
if (featureData.actions?.length > 0) {
const newActions = featureData.actions.map(action => {
const cls = actionsTypes[action.type];
return new cls(
{ ...action, _id: foundry.utils.randomID(), name: game.i18n.localize(action.name) },
{ parent: this }
);
});
changes.system.actions = [...this.actions, ...newActions];
feature.actionIds = newActions.map(x => x._id);
}
}
}
}
} }

View file

@ -1,8 +1,8 @@
import BaseDataItem from './base.mjs'; import BaseDataItem from './base.mjs';
import FormulaField from '../fields/formulaField.mjs'; import FormulaField from '../fields/formulaField.mjs';
import PseudoDocumentsField from '../fields/pseudoDocumentsField.mjs';
import BaseFeatureData from '../pseudo-documents/feature/baseFeatureData.mjs';
import ActionField from '../fields/actionField.mjs'; import ActionField from '../fields/actionField.mjs';
import { weaponFeatures } from '../../config/itemConfig.mjs';
import { actionsTypes } from '../../data/_module.mjs';
export default class DHWeapon extends BaseDataItem { export default class DHWeapon extends BaseDataItem {
/** @inheritDoc */ /** @inheritDoc */
@ -23,6 +23,7 @@ export default class DHWeapon extends BaseDataItem {
const fields = foundry.data.fields; const fields = foundry.data.fields;
return { return {
...super.defineSchema(), ...super.defineSchema(),
tier: new fields.NumberField({ required: true, integer: true, initial: 1, min: 1 }),
equipped: new fields.BooleanField({ initial: false }), equipped: new fields.BooleanField({ initial: false }),
//SETTINGS //SETTINGS
@ -39,14 +40,57 @@ export default class DHWeapon extends BaseDataItem {
initial: 'physical' initial: 'physical'
}) })
}), }),
feature: new fields.StringField({ choices: SYSTEM.ITEM.weaponFeatures, blank: true }), features: new fields.ArrayField(
featureTest: new PseudoDocumentsField(BaseFeatureData, { new fields.SchemaField({
required: true, value: new fields.StringField({ required: true, choices: SYSTEM.ITEM.weaponFeatures, blank: true }),
nullable: true, effectIds: new fields.ArrayField(new fields.StringField({ required: true })),
max: 1, actionIds: new fields.ArrayField(new fields.StringField({ required: true }))
validTypes: ['weapon'] })
}), ),
actions: new fields.ArrayField(new ActionField()) actions: new fields.ArrayField(new ActionField())
}; };
} }
async _preUpdate(changes, options, user) {
const allowed = await super._preUpdate(changes, options, user);
if (allowed === false) return false;
if (changes.system.features) {
const removed = this.features.filter(x => !changes.system.features.includes(x));
const added = changes.system.features.filter(x => !this.features.includes(x));
for (var feature of removed) {
for (var effectId of feature.effectIds) {
await this.parent.effects.get(effectId).delete();
}
changes.system.actions = this.actions.filter(x => !feature.actionIds.includes(x._id));
}
for (var feature of added) {
const featureData = weaponFeatures[feature.value];
if (featureData.effects?.length > 0) {
const embeddedItems = await this.parent.createEmbeddedDocuments('ActiveEffect', [
{
name: game.i18n.localize(featureData.label),
description: game.i18n.localize(featureData.description),
changes: featureData.effects.flatMap(x => x.changes)
}
]);
feature.effectIds = embeddedItems.map(x => x.id);
}
if (featureData.actions?.length > 0) {
const newActions = featureData.actions.map(action => {
const cls = actionsTypes[action.type];
return new cls(
{ ...action, _id: foundry.utils.randomID(), name: game.i18n.localize(action.name) },
{ parent: this }
);
});
changes.system.actions = [...this.actions, ...newActions];
feature.actionIds = newActions.map(x => x._id);
}
}
}
}
} }

View file

@ -1,4 +1,12 @@
export default class DhActiveEffect extends ActiveEffect { export default class DhActiveEffect extends ActiveEffect {
get isSuppressed() {
if (['weapon', 'armor'].includes(this.parent.type)) {
return !this.parent.system.equipped;
}
return super.isSuppressed;
}
async _preCreate(data, options, user) { async _preCreate(data, options, user) {
const update = {}; const update = {};
if (!data.img) { if (!data.img) {
@ -11,4 +19,9 @@ export default class DhActiveEffect extends ActiveEffect {
await super._preCreate(data, options, user); await super._preCreate(data, options, user);
} }
static applyField(model, change, field) {
change.value = Roll.safeEval(Roll.replaceFormulaData(change.value, change.effect.parent));
super.applyField(model, change, field);
}
} }

View file

@ -13,7 +13,7 @@
<li class="item inventory-item"> <li class="item inventory-item">
<div class="inventory-row" data-item-id="{{item.uuid}}"> <div class="inventory-row" data-item-id="{{item.uuid}}">
<div class="inventory-item-title-container"> <div class="inventory-item-title-container">
<div data-action="useItem" data-value="{{item.uuid}}" class="inventory-item-title"> <div data-action="viewObject" data-value="{{item.uuid}}" class="inventory-item-title">
<img src="{{item.img}}" /> <img src="{{item.img}}" />
{{item.name}} {{item.name}}
</div> </div>

View file

@ -6,11 +6,13 @@
<fieldset class="two-columns"> <fieldset class="two-columns">
<legend>{{localize tabs.settings.label}}</legend> <legend>{{localize tabs.settings.label}}</legend>
<span>{{localize "DAGGERHEART.Tiers.singular"}}</span>
{{formField systemFields.tier value=source.system.tier}}
<span>{{localize "DAGGERHEART.Sheets.Armor.baseScore"}}</span> <span>{{localize "DAGGERHEART.Sheets.Armor.baseScore"}}</span>
{{formField systemFields.baseScore value=source.system.baseScore}} {{formField systemFields.baseScore value=source.system.baseScore}}
<span>{{localize "DAGGERHEART.Sheets.Armor.feature"}}</span> <span>{{localize "DAGGERHEART.Sheets.Armor.feature"}}</span>
{{formField systemFields.feature value=source.system.feature localize=true blank=""}} <input type="text" class="features-input" value="{{features}}" />
<span>{{localize "DAGGERHEART.Sheets.Armor.baseThresholds.base"}}</span> <span>{{localize "DAGGERHEART.Sheets.Armor.baseThresholds.base"}}</span>
<div class="nest-inputs"> <div class="nest-inputs">

View file

@ -5,6 +5,8 @@
> >
<fieldset class="two-columns"> <fieldset class="two-columns">
<legend>{{localize tabs.settings.label}}</legend> <legend>{{localize tabs.settings.label}}</legend>
<span>{{localize "DAGGERHEART.Tiers.singular"}}</span>
{{formField systemFields.tier value=source.system.tier}}
<span>{{localize "DAGGERHEART.Sheets.Weapon.SecondaryWeapon"}}</span> <span>{{localize "DAGGERHEART.Sheets.Weapon.SecondaryWeapon"}}</span>
{{formField systemFields.secondary value=source.system.secondary}} {{formField systemFields.secondary value=source.system.secondary}}
<span>{{localize "DAGGERHEART.Sheets.Weapon.Trait"}}</span> <span>{{localize "DAGGERHEART.Sheets.Weapon.Trait"}}</span>
@ -25,6 +27,6 @@
<fieldset class="two-columns"> <fieldset class="two-columns">
<legend>{{localize "DAGGERHEART.Sheets.Weapon.Feature"}}</legend> <legend>{{localize "DAGGERHEART.Sheets.Weapon.Feature"}}</legend>
<span>{{localize "DAGGERHEART.Sheets.Weapon.Feature"}}</span> <span>{{localize "DAGGERHEART.Sheets.Weapon.Feature"}}</span>
{{formField systemFields.feature value=source.system.feature localize=true}} <input type="text" class="features-input" value="{{features}}" />
</fieldset> </fieldset>
</section> </section>