mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-18 16:09:03 +01:00
Fix conflict
This commit is contained in:
commit
3d1be5fa22
487 changed files with 16301 additions and 1974 deletions
|
|
@ -1,5 +1,6 @@
|
|||
import { abilities } from '../../config/actorConfig.mjs';
|
||||
import { burden } from '../../config/generalConfig.mjs';
|
||||
import { createEmbeddedItemWithEffects } from '../../helpers/utils.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
|
|
@ -550,34 +551,46 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
|
|||
}
|
||||
};
|
||||
|
||||
await this.character.createEmbeddedDocuments('Item', [ancestry]);
|
||||
await this.character.createEmbeddedDocuments('Item', [this.setup.community]);
|
||||
await this.character.createEmbeddedDocuments('Item', [this.setup.class]);
|
||||
await this.character.createEmbeddedDocuments('Item', [this.setup.subclass]);
|
||||
await this.character.createEmbeddedDocuments('Item', Object.values(this.setup.domainCards));
|
||||
|
||||
if (this.equipment.armor.uuid)
|
||||
await this.character.createEmbeddedDocuments('Item', [
|
||||
{ ...this.equipment.armor, system: { ...this.equipment.armor.system, equipped: true } }
|
||||
]);
|
||||
if (this.equipment.primaryWeapon.uuid)
|
||||
await this.character.createEmbeddedDocuments('Item', [
|
||||
{ ...this.equipment.primaryWeapon, system: { ...this.equipment.primaryWeapon.system, equipped: true } }
|
||||
]);
|
||||
if (this.equipment.secondaryWeapon.uuid)
|
||||
await this.character.createEmbeddedDocuments('Item', [
|
||||
{
|
||||
...this.equipment.secondaryWeapon,
|
||||
system: { ...this.equipment.secondaryWeapon.system, equipped: true }
|
||||
}
|
||||
]);
|
||||
if (this.equipment.inventory.choiceA.uuid)
|
||||
await this.character.createEmbeddedDocuments('Item', [this.equipment.inventory.choiceA]);
|
||||
if (this.equipment.inventory.choiceB.uuid)
|
||||
await this.character.createEmbeddedDocuments('Item', [this.equipment.inventory.choiceB]);
|
||||
await createEmbeddedItemWithEffects(this.character, ancestry);
|
||||
await createEmbeddedItemWithEffects(this.character, this.setup.community);
|
||||
await createEmbeddedItemWithEffects(this.character, this.setup.class);
|
||||
await createEmbeddedItemWithEffects(this.character, this.setup.subclass);
|
||||
await this.character.createEmbeddedDocuments(
|
||||
'Item',
|
||||
this.setup.class.system.inventory.take.filter(x => x)
|
||||
Object.values(this.setup.domainCards).map(x => ({
|
||||
...x,
|
||||
effects: x.effects?.map(effect => effect.toObject())
|
||||
}))
|
||||
);
|
||||
|
||||
if (this.equipment.armor.uuid)
|
||||
await createEmbeddedItemWithEffects(this.character, this.equipment.armor, {
|
||||
...this.equipment.armor,
|
||||
system: { ...this.equipment.armor.system, equipped: true }
|
||||
});
|
||||
if (this.equipment.primaryWeapon.uuid)
|
||||
await createEmbeddedItemWithEffects(this.character, this.equipment.primaryWeapon, {
|
||||
...this.equipment.primaryWeapon,
|
||||
system: { ...this.equipment.primaryWeapon.system, equipped: true }
|
||||
});
|
||||
if (this.equipment.secondaryWeapon.uuid)
|
||||
await createEmbeddedItemWithEffects(this.character, this.equipment.secondaryWeapon, {
|
||||
...this.equipment.secondaryWeapon,
|
||||
system: { ...this.equipment.secondaryWeapon.system, equipped: true }
|
||||
});
|
||||
if (this.equipment.inventory.choiceA.uuid)
|
||||
await createEmbeddedItemWithEffects(this.character, this.equipment.inventory.choiceA);
|
||||
if (this.equipment.inventory.choiceB.uuid)
|
||||
await createEmbeddedItemWithEffects(this.character, this.equipment.inventory.choiceB);
|
||||
|
||||
await this.character.createEmbeddedDocuments(
|
||||
'Item',
|
||||
this.setup.class.system.inventory.take
|
||||
.filter(x => x)
|
||||
.map(x => ({
|
||||
...x,
|
||||
effects: x.effects?.map(effect => effect.toObject())
|
||||
}))
|
||||
);
|
||||
|
||||
await this.character.update({
|
||||
|
|
|
|||
|
|
@ -10,14 +10,18 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
|
|||
this.reject = reject;
|
||||
this.actor = actor;
|
||||
this.damage = damage;
|
||||
this.rulesDefault = game.settings.get(
|
||||
CONFIG.DH.id,
|
||||
CONFIG.DH.SETTINGS.gameSettings.Automation
|
||||
).damageReductionRulesDefault;
|
||||
|
||||
this.rulesOn = [CONFIG.DH.GENERAL.ruleChoice.on.id, CONFIG.DH.GENERAL.ruleChoice.onWithToggle.id].includes(
|
||||
this.rulesDefault
|
||||
);
|
||||
|
||||
const canApplyArmor = damageType.every(t => actor.system.armorApplicableDamageTypes[t] === true);
|
||||
const maxArmorMarks = canApplyArmor
|
||||
? Math.min(
|
||||
actor.system.armorScore - actor.system.armor.system.marks.value,
|
||||
actor.system.rules.damageReduction.maxArmorMarked.value
|
||||
)
|
||||
: 0;
|
||||
const availableArmor = actor.system.armorScore - actor.system.armor.system.marks.value;
|
||||
const maxArmorMarks = canApplyArmor ? availableArmor : 0;
|
||||
|
||||
const armor = [...Array(maxArmorMarks).keys()].reduce((acc, _) => {
|
||||
acc[foundry.utils.randomID()] = { selected: false };
|
||||
|
|
@ -42,6 +46,7 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
|
|||
acc[damage] = {
|
||||
cost: dr.cost,
|
||||
selected: false,
|
||||
any: key === 'any',
|
||||
from: getDamageLabel(damage),
|
||||
to: getDamageLabel(damage - 1)
|
||||
};
|
||||
|
|
@ -51,16 +56,28 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
|
|||
},
|
||||
null
|
||||
);
|
||||
|
||||
this.thresholdImmunities = Object.keys(actor.system.rules.damageReduction.thresholdImmunities).reduce(
|
||||
(acc, key) => {
|
||||
if (actor.system.rules.damageReduction.thresholdImmunities[key])
|
||||
acc[damageKeyToNumber(key)] = game.i18n.format(`DAGGERHEART.GENERAL.DamageThresholds.with`, {
|
||||
threshold: game.i18n.localize(`DAGGERHEART.GENERAL.DamageThresholds.${key}`)
|
||||
});
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
classes: ['daggerheart', 'views', 'damage-reduction'],
|
||||
position: {
|
||||
width: 240,
|
||||
width: 280,
|
||||
height: 'auto'
|
||||
},
|
||||
actions: {
|
||||
toggleRules: this.toggleRules,
|
||||
setMarks: this.setMarks,
|
||||
useStressReduction: this.useStressReduction,
|
||||
takeDamage: this.takeDamage
|
||||
|
|
@ -89,6 +106,12 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
|
|||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.rulesOn = this.rulesOn;
|
||||
context.rulesToggleable = [
|
||||
CONFIG.DH.GENERAL.ruleChoice.onWithToggle.id,
|
||||
CONFIG.DH.GENERAL.ruleChoice.offWithToggle.id
|
||||
].includes(this.rulesDefault);
|
||||
context.thresholdImmunities = this.thresholdImmunities;
|
||||
|
||||
const { selectedArmorMarks, selectedStressMarks, stressReductions, currentMarks, currentDamage } =
|
||||
this.getDamageInfo();
|
||||
|
|
@ -110,12 +133,22 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
|
|||
}
|
||||
: null;
|
||||
|
||||
context.marks = this.marks;
|
||||
const maxArmor = this.actor.system.rules.damageReduction.maxArmorMarked.value;
|
||||
context.marks = {
|
||||
armor: Object.keys(this.marks.armor).reduce((acc, key, index) => {
|
||||
const mark = this.marks.armor[key];
|
||||
if (!this.rulesOn || index + 1 <= maxArmor) acc[key] = mark;
|
||||
|
||||
return acc;
|
||||
}, {}),
|
||||
stress: this.marks.stress
|
||||
};
|
||||
context.availableStressReductions = this.availableStressReductions;
|
||||
|
||||
context.damage = getDamageLabel(this.damage);
|
||||
context.reducedDamage = currentDamage !== this.damage ? getDamageLabel(currentDamage) : null;
|
||||
context.currentDamage = context.reducedDamage ?? context.damage;
|
||||
context.currentDamageNr = currentDamage;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
|
@ -136,22 +169,48 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
|
|||
|
||||
const armorMarkReduction =
|
||||
selectedArmorMarks.length * this.actor.system.rules.damageReduction.increasePerArmorMark;
|
||||
const currentDamage = this.damage - armorMarkReduction - selectedStressMarks.length - stressReductions.length;
|
||||
let currentDamage = Math.max(
|
||||
this.damage - armorMarkReduction - selectedStressMarks.length - stressReductions.length,
|
||||
0
|
||||
);
|
||||
|
||||
if (this.thresholdImmunities[currentDamage]) currentDamage = 0;
|
||||
|
||||
return { selectedArmorMarks, selectedStressMarks, stressReductions, currentMarks, currentDamage };
|
||||
};
|
||||
|
||||
static toggleRules() {
|
||||
this.rulesOn = !this.rulesOn;
|
||||
|
||||
const maxArmor = this.actor.system.rules.damageReduction.maxArmorMarked.value;
|
||||
this.marks = {
|
||||
armor: Object.keys(this.marks.armor).reduce((acc, key, index) => {
|
||||
const mark = this.marks.armor[key];
|
||||
const keepSelectValue = !this.rulesOn || index + 1 <= maxArmor;
|
||||
acc[key] = { ...mark, selected: keepSelectValue ? mark.selected : false };
|
||||
|
||||
return acc;
|
||||
}, {}),
|
||||
stress: this.marks.stress
|
||||
};
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static setMarks(_, target) {
|
||||
const currentMark = this.marks[target.dataset.type][target.dataset.key];
|
||||
const { selectedStressMarks, stressReductions, currentMarks, currentDamage } = this.getDamageInfo();
|
||||
|
||||
if (!currentMark.selected && currentDamage === 0) {
|
||||
ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.damageAlreadyNone'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!currentMark.selected && currentMarks === this.actor.system.armorScore) {
|
||||
ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noAvailableArmorMarks'));
|
||||
return;
|
||||
if (this.rulesOn) {
|
||||
if (!currentMark.selected && currentMarks === this.actor.system.armorScore) {
|
||||
ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noAvailableArmorMarks'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentMark.selected) {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
|||
actor.system.bonuses.rest[`${shortrest ? 'short' : 'long'}Rest`].longMoves
|
||||
}
|
||||
};
|
||||
|
||||
this.refreshables = this.getRefreshables();
|
||||
}
|
||||
|
||||
get title() {
|
||||
|
|
@ -81,11 +83,56 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
|||
context.shortRestMoves = this.nrChoices.shortRest.max > 0 ? this.moveData.shortRest : null;
|
||||
context.longRestMoves = this.nrChoices.longRest.max > 0 ? this.moveData.longRest : null;
|
||||
|
||||
context.refreshables = this.refreshables;
|
||||
|
||||
context.disabledDowntime = shortRestMovesSelected === 0 && longRestMovesSelected === 0;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
getRefreshables() {
|
||||
const actionItems = this.actor.items.reduce((acc, x) => {
|
||||
if (x.system.actions) {
|
||||
const recoverable = x.system.actions.reduce((acc, action) => {
|
||||
if (action.uses.recovery && (action.uses.recovery === 'shortRest') === this.shortrest) {
|
||||
acc.push({
|
||||
title: x.name,
|
||||
name: action.name,
|
||||
uuid: action.uuid
|
||||
});
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
if (recoverable) {
|
||||
acc.push(...recoverable);
|
||||
}
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
const resourceItems = this.actor.items.reduce((acc, x) => {
|
||||
if (
|
||||
x.system.resource &&
|
||||
x.system.resource.type &&
|
||||
(x.system.resource.recovery === 'shortRest') === this.shortrest
|
||||
) {
|
||||
acc.push({
|
||||
title: game.i18n.localize(`TYPES.Item.${x.type}`),
|
||||
name: x.name,
|
||||
uuid: x.uuid
|
||||
});
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
return {
|
||||
actionItems,
|
||||
resourceItems
|
||||
};
|
||||
}
|
||||
|
||||
static selectMove(_, target) {
|
||||
const { category, move } = target.dataset;
|
||||
|
||||
|
|
@ -172,11 +219,24 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
|||
}
|
||||
}
|
||||
|
||||
// We can close the window when all moves are taken
|
||||
// We can close the window and refresh resources when all moves are taken
|
||||
if (
|
||||
this.nrChoices.shortRest.taken >= this.nrChoices.shortRest.max &&
|
||||
this.nrChoices.longRest.taken >= this.nrChoices.longRest.max
|
||||
) {
|
||||
for (var data of this.refreshables.actionItems) {
|
||||
const action = await foundry.utils.fromUuid(data.uuid);
|
||||
await action.parent.parent.update({ [`system.actions.${action.id}.uses.value`]: action.uses.max ?? 1 });
|
||||
}
|
||||
|
||||
for (var data of this.refreshables.resourceItems) {
|
||||
const feature = await foundry.utils.fromUuid(data.uuid);
|
||||
const increasing =
|
||||
feature.system.resource.progression === CONFIG.DH.ITEM.itemResourceProgression.increasing.id;
|
||||
const resetValue = increasing ? 0 : (feature.system.resource.max ?? 0);
|
||||
await feature.update({ 'system.resource.value': resetValue });
|
||||
}
|
||||
|
||||
this.close();
|
||||
} else {
|
||||
this.render();
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ export default function DHApplicationMixin(Base) {
|
|||
docs.push(doc);
|
||||
}
|
||||
|
||||
docs.filter(doc => doc).map(doc => (doc.apps[this.id] = this));
|
||||
docs.filter(doc => doc).forEach(doc => (doc.apps[this.id] = this));
|
||||
|
||||
if (!!this.options.contextMenus.length) this._createContextMenus();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,10 +80,17 @@ export default class ClassSheet extends DHBaseItemSheet {
|
|||
'inventory.choiceB'
|
||||
];
|
||||
|
||||
paths.forEach(path => {
|
||||
const docs = [].concat(foundry.utils.getProperty(this.document, `system.${path}`) ?? []);
|
||||
docs.forEach(doc => (doc.apps[this.id] = this));
|
||||
});
|
||||
for (let path of paths) {
|
||||
const docDatas = [].concat(foundry.utils.getProperty(this.document, `system.${path}`) ?? []);
|
||||
|
||||
const docs = [];
|
||||
for (var docData of docDatas) {
|
||||
const doc = await foundry.utils.fromUuid(docData.uuid);
|
||||
docs.push(doc);
|
||||
}
|
||||
|
||||
docs.filter(doc => doc).forEach(doc => (doc.apps[this.id] = this));
|
||||
}
|
||||
}
|
||||
|
||||
/**@inheritdoc */
|
||||
|
|
|
|||
|
|
@ -55,6 +55,24 @@ export const abilities = {
|
|||
}
|
||||
};
|
||||
|
||||
export const scrollingTextResource = {
|
||||
hitPoints: {
|
||||
label: 'DAGGERHEART.GENERAL.HitPoints.plural',
|
||||
reversed: true
|
||||
},
|
||||
stress: {
|
||||
label: 'DAGGERHEART.GENERAL.stress',
|
||||
reversed: true
|
||||
},
|
||||
hope: {
|
||||
label: 'DAGGERHEART.GENERAL.hope'
|
||||
},
|
||||
armor: {
|
||||
label: 'DAGGERHEART.GENERAL.armor',
|
||||
reversed: true
|
||||
}
|
||||
};
|
||||
|
||||
export const featureProperties = {
|
||||
agility: {
|
||||
name: 'DAGGERHEART.CONFIG.Traits.agility.name',
|
||||
|
|
|
|||
|
|
@ -2,6 +2,25 @@ export const compendiumJournals = {
|
|||
welcome: 'Compendium.daggerheart.journals.JournalEntry.g7NhKvwltwafmMyR'
|
||||
};
|
||||
|
||||
export const ruleChoice = {
|
||||
on: {
|
||||
id: 'on',
|
||||
label: 'DAGGERHEART.CONFIG.RuleChoice.on'
|
||||
},
|
||||
of: {
|
||||
id: 'off',
|
||||
label: 'DAGGERHEART.CONFIG.RuleChoice.off'
|
||||
},
|
||||
onWithToggle: {
|
||||
id: 'onWithToggle',
|
||||
label: 'DAGGERHEART.CONFIG.RuleChoice.onWithToggle'
|
||||
},
|
||||
offWithToggle: {
|
||||
id: 'offWithToggle',
|
||||
label: 'DAGGERHEART.CONFIG.RuleChoice.offWithToggle'
|
||||
}
|
||||
};
|
||||
|
||||
export const range = {
|
||||
self: {
|
||||
id: 'self',
|
||||
|
|
@ -129,10 +148,10 @@ export const healingTypes = {
|
|||
label: 'DAGGERHEART.CONFIG.HealingType.hope.name',
|
||||
abbreviation: 'DAGGERHEART.CONFIG.HealingType.hope.abbreviation'
|
||||
},
|
||||
armorSlot: {
|
||||
id: 'armorSlot',
|
||||
label: 'DAGGERHEART.CONFIG.HealingType.armorSlot.name',
|
||||
abbreviation: 'DAGGERHEART.CONFIG.HealingType.armorSlot.abbreviation'
|
||||
armor: {
|
||||
id: 'armor',
|
||||
label: 'DAGGERHEART.CONFIG.HealingType.armor.name',
|
||||
abbreviation: 'DAGGERHEART.CONFIG.HealingType.armor.abbreviation'
|
||||
},
|
||||
fear: {
|
||||
id: 'fear',
|
||||
|
|
@ -190,14 +209,21 @@ export const defaultRestOptions = {
|
|||
img: 'icons/magic/life/cross-worn-green.webp',
|
||||
actionType: 'action',
|
||||
chatDisplay: false,
|
||||
healing: {
|
||||
applyTo: healingTypes.hitPoints.id,
|
||||
value: {
|
||||
custom: {
|
||||
enabled: true,
|
||||
formula: '1d4 + @tier'
|
||||
target: {
|
||||
type: 'self'
|
||||
},
|
||||
damage: {
|
||||
parts: [
|
||||
{
|
||||
applyTo: healingTypes.hitPoints.id,
|
||||
value: {
|
||||
custom: {
|
||||
enabled: true,
|
||||
formula: '1d4 + @tier'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -216,14 +242,21 @@ export const defaultRestOptions = {
|
|||
img: 'icons/magic/perception/eye-ringed-green.webp',
|
||||
actionType: 'action',
|
||||
chatDisplay: false,
|
||||
healing: {
|
||||
applyTo: healingTypes.stress.id,
|
||||
value: {
|
||||
custom: {
|
||||
enabled: true,
|
||||
formula: '1d4 + @tier'
|
||||
target: {
|
||||
type: 'self'
|
||||
},
|
||||
damage: {
|
||||
parts: [
|
||||
{
|
||||
applyTo: healingTypes.stress.id,
|
||||
value: {
|
||||
custom: {
|
||||
enabled: true,
|
||||
formula: '1d4 + @tier'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -242,14 +275,21 @@ export const defaultRestOptions = {
|
|||
img: 'icons/skills/trades/smithing-anvil-silver-red.webp',
|
||||
actionType: 'action',
|
||||
chatDisplay: false,
|
||||
healing: {
|
||||
applyTo: healingTypes.armorSlot.id,
|
||||
value: {
|
||||
custom: {
|
||||
enabled: true,
|
||||
formula: '1d4 + @tier'
|
||||
target: {
|
||||
type: 'self'
|
||||
},
|
||||
damage: {
|
||||
parts: [
|
||||
{
|
||||
applyTo: healingTypes.armor.id,
|
||||
value: {
|
||||
custom: {
|
||||
enabled: true,
|
||||
formula: '1d4 + @tier'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -278,14 +318,21 @@ export const defaultRestOptions = {
|
|||
img: 'icons/magic/life/cross-worn-green.webp',
|
||||
actionType: 'action',
|
||||
chatDisplay: false,
|
||||
healing: {
|
||||
applyTo: healingTypes.hitPoints.id,
|
||||
value: {
|
||||
custom: {
|
||||
enabled: true,
|
||||
formula: '@system.resources.hitPoints.max'
|
||||
target: {
|
||||
type: 'self'
|
||||
},
|
||||
damage: {
|
||||
parts: [
|
||||
{
|
||||
applyTo: healingTypes.hitPoints.id,
|
||||
value: {
|
||||
custom: {
|
||||
enabled: true,
|
||||
formula: '@system.resources.hitPoints.max'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -304,14 +351,21 @@ export const defaultRestOptions = {
|
|||
img: 'icons/magic/perception/eye-ringed-green.webp',
|
||||
actionType: 'action',
|
||||
chatDisplay: false,
|
||||
healing: {
|
||||
applyTo: healingTypes.stress.id,
|
||||
value: {
|
||||
custom: {
|
||||
enabled: true,
|
||||
formula: '@system.resources.stress.max'
|
||||
target: {
|
||||
type: 'self'
|
||||
},
|
||||
damage: {
|
||||
parts: [
|
||||
{
|
||||
applyTo: healingTypes.stress.id,
|
||||
value: {
|
||||
custom: {
|
||||
enabled: true,
|
||||
formula: '@system.resources.stress.max'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -330,14 +384,21 @@ export const defaultRestOptions = {
|
|||
img: 'icons/skills/trades/smithing-anvil-silver-red.webp',
|
||||
actionType: 'action',
|
||||
chatDisplay: false,
|
||||
healing: {
|
||||
applyTo: healingTypes.armorSlot.id,
|
||||
value: {
|
||||
custom: {
|
||||
enabled: true,
|
||||
formula: '@system.armorScore'
|
||||
target: {
|
||||
type: 'self'
|
||||
},
|
||||
damage: {
|
||||
parts: [
|
||||
{
|
||||
applyTo: healingTypes.armor.id,
|
||||
value: {
|
||||
custom: {
|
||||
enabled: true,
|
||||
formula: '@system.armorScore'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -432,8 +493,20 @@ export const getDiceSoNicePresets = async (hopeFaces, fearFaces, advantageFaces
|
|||
const { diceSoNice } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance);
|
||||
const getPreset = async (type, faces) => {
|
||||
const system = game.dice3d.DiceFactory.systems.get(type.system).dice.get(faces);
|
||||
if (!system.modelLoaded) {
|
||||
if (!system) {
|
||||
ui.notifications.error(
|
||||
game.i18n.format('DAGGERHEART.UI.Notifications.noDiceSystem', {
|
||||
system: game.dice3d.DiceFactory.systems.get(type.system).name,
|
||||
faces: faces
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (system.modelFile && !system.modelLoaded) {
|
||||
await system.loadModel(game.dice3d.DiceFactory.loaderGLTF);
|
||||
} else {
|
||||
await system.loadTextures();
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -675,7 +675,7 @@ export const weaponFeatures = {
|
|||
},
|
||||
cost: [
|
||||
{
|
||||
type: 'armorSlot',
|
||||
type: 'armor',
|
||||
value: 1
|
||||
}
|
||||
],
|
||||
|
|
@ -886,14 +886,21 @@ export const weaponFeatures = {
|
|||
name: 'DAGGERHEART.CONFIG.WeaponFeature.healing.actions.heal.name',
|
||||
description: 'DAGGERHEART.CONFIG.WeaponFeature.healing.actions.heal.description',
|
||||
img: 'icons/magic/life/cross-beam-green.webp',
|
||||
healing: {
|
||||
type: 'health',
|
||||
value: {
|
||||
custom: {
|
||||
enabled: true,
|
||||
formula: '1'
|
||||
target: {
|
||||
type: 'self'
|
||||
},
|
||||
damage: {
|
||||
parts: [
|
||||
{
|
||||
applyTo: 'hitPoints',
|
||||
value: {
|
||||
custom: {
|
||||
enabled: true,
|
||||
formula: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -1473,6 +1480,17 @@ export const itemResourceTypes = {
|
|||
}
|
||||
};
|
||||
|
||||
export const itemResourceProgression = {
|
||||
increasing: {
|
||||
id: 'increasing',
|
||||
label: 'DAGGERHEART.CONFIG.ItemResourceProgression.increasing'
|
||||
},
|
||||
decreasing: {
|
||||
id: 'decreasing',
|
||||
label: 'DAGGERHEART.CONFIG.ItemResourceProgression.decreasing'
|
||||
}
|
||||
};
|
||||
|
||||
export const beastformTypes = {
|
||||
normal: {
|
||||
id: 'normal',
|
||||
|
|
|
|||
|
|
@ -53,8 +53,8 @@ export default class DhpAdversary extends BaseDataActor {
|
|||
})
|
||||
}),
|
||||
resources: new fields.SchemaField({
|
||||
hitPoints: resourceField(0, 'DAGGERHEART.GENERAL.HitPoints.plural', true),
|
||||
stress: resourceField(0, 'DAGGERHEART.GENERAL.stress', true)
|
||||
hitPoints: resourceField(0, 0, 'DAGGERHEART.GENERAL.HitPoints.plural', true),
|
||||
stress: resourceField(0, 0, 'DAGGERHEART.GENERAL.stress', true)
|
||||
}),
|
||||
attack: new ActionField({
|
||||
initial: {
|
||||
|
|
@ -105,6 +105,13 @@ export default class DhpAdversary extends BaseDataActor {
|
|||
};
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**@inheritdoc */
|
||||
static DEFAULT_ICON = 'systems/daggerheart/assets/icons/documents/actors/dragon-head.svg';
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
get attackBonus() {
|
||||
return this.attack.roll.bonus;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import DHBaseActorSettings from '../../applications/sheets/api/actor-setting.mjs';
|
||||
import { createScrollText, getScrollTextData } from '../../helpers/utils.mjs';
|
||||
|
||||
const resistanceField = (resistanceLabel, immunityLabel, reductionLabel) =>
|
||||
new foundry.data.fields.SchemaField({
|
||||
|
|
@ -69,6 +70,16 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
|
|||
return schema;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* The default icon used for newly created Actors documents
|
||||
* @type {string}
|
||||
*/
|
||||
static DEFAULT_ICON = null;
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Obtain a data object used to evaluate any dice rolls associated with this Item Type
|
||||
* @param {object} [options] - Options which modify the getRollData method.
|
||||
|
|
@ -78,4 +89,28 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
|
|||
const data = { ...this };
|
||||
return data;
|
||||
}
|
||||
|
||||
async _preUpdate(changes, options, userId) {
|
||||
const allowed = await super._preUpdate(changes, options, userId);
|
||||
if (allowed === false) return;
|
||||
|
||||
const autoSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation);
|
||||
if (changes.system?.resources && autoSettings.resourceScrollTexts) {
|
||||
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));
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
options.scrollingTextData = textData;
|
||||
}
|
||||
}
|
||||
|
||||
_onUpdate(changes, options, userId) {
|
||||
super._onUpdate(changes, options, userId);
|
||||
|
||||
createScrollText(this.parent, options.scrollingTextData);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,8 +7,10 @@ import { ActionField } from '../fields/actionField.mjs';
|
|||
import DHCharacterSettings from '../../applications/sheets-configs/character-settings.mjs';
|
||||
|
||||
export default class DhCharacter extends BaseDataActor {
|
||||
/**@override */
|
||||
static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Character'];
|
||||
|
||||
/**@inheritdoc */
|
||||
static get metadata() {
|
||||
return foundry.utils.mergeObject(super.metadata, {
|
||||
label: 'TYPES.Actor.character',
|
||||
|
|
@ -18,6 +20,7 @@ export default class DhCharacter extends BaseDataActor {
|
|||
});
|
||||
}
|
||||
|
||||
/**@inheritdoc */
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
|
|
@ -25,13 +28,14 @@ export default class DhCharacter extends BaseDataActor {
|
|||
...super.defineSchema(),
|
||||
resources: new fields.SchemaField({
|
||||
hitPoints: resourceField(
|
||||
0,
|
||||
0,
|
||||
'DAGGERHEART.GENERAL.HitPoints.plural',
|
||||
true,
|
||||
'DAGGERHEART.ACTORS.Character.maxHPBonus'
|
||||
),
|
||||
stress: resourceField(6, 'DAGGERHEART.GENERAL.stress', true),
|
||||
hope: resourceField(6, 'DAGGERHEART.GENERAL.hope')
|
||||
stress: resourceField(6, 0, 'DAGGERHEART.GENERAL.stress', true),
|
||||
hope: resourceField(6, 2, 'DAGGERHEART.GENERAL.hope')
|
||||
}),
|
||||
traits: new fields.SchemaField({
|
||||
agility: attributeField('DAGGERHEART.CONFIG.Traits.agility.name'),
|
||||
|
|
@ -239,7 +243,8 @@ export default class DhCharacter extends BaseDataActor {
|
|||
stressDamageReduction: new fields.SchemaField({
|
||||
severe: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.severe'),
|
||||
major: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.major'),
|
||||
minor: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.minor')
|
||||
minor: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.minor'),
|
||||
any: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.any')
|
||||
}),
|
||||
increasePerArmorMark: new fields.NumberField({
|
||||
integer: true,
|
||||
|
|
@ -248,7 +253,11 @@ export default class DhCharacter extends BaseDataActor {
|
|||
hint: 'DAGGERHEART.GENERAL.Rules.damageReduction.increasePerArmorMark.hint'
|
||||
}),
|
||||
magical: new fields.BooleanField({ initial: false }),
|
||||
physical: new fields.BooleanField({ initial: false })
|
||||
physical: new fields.BooleanField({ initial: false }),
|
||||
thresholdImmunities: new fields.SchemaField({
|
||||
minor: new fields.BooleanField({ initial: false })
|
||||
}),
|
||||
disabledArmor: new fields.BooleanField({ intial: false })
|
||||
}),
|
||||
attack: new fields.SchemaField({
|
||||
damage: new fields.SchemaField({
|
||||
|
|
@ -289,11 +298,16 @@ export default class DhCharacter extends BaseDataActor {
|
|||
*/
|
||||
flipMinDiceValue: new fields.BooleanField({ intial: false })
|
||||
}),
|
||||
runeWard: new fields.BooleanField({ initial: false })
|
||||
runeWard: new fields.BooleanField({ initial: false }),
|
||||
burden: new fields.SchemaField({
|
||||
ignore: new fields.BooleanField()
|
||||
})
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
get tier() {
|
||||
const currentLevel = this.levelData.level.current;
|
||||
return currentLevel === 1
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import { resourceField, bonusField } from '../fields/actorField.mjs';
|
|||
export default class DhCompanion extends BaseDataActor {
|
||||
static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Companion'];
|
||||
|
||||
/**@inheritdoc */
|
||||
static get metadata() {
|
||||
return foundry.utils.mergeObject(super.metadata, {
|
||||
label: 'TYPES.Actor.companion',
|
||||
|
|
@ -18,6 +19,7 @@ export default class DhCompanion extends BaseDataActor {
|
|||
});
|
||||
}
|
||||
|
||||
/**@inheritdoc */
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
|
|
@ -25,7 +27,7 @@ export default class DhCompanion extends BaseDataActor {
|
|||
...super.defineSchema(),
|
||||
partner: new ForeignDocumentUUIDField({ type: 'Actor' }),
|
||||
resources: new fields.SchemaField({
|
||||
stress: resourceField(3, 'DAGGERHEART.GENERAL.stress', true),
|
||||
stress: resourceField(3, 0, 'DAGGERHEART.GENERAL.stress', true),
|
||||
hope: new fields.NumberField({ initial: 0, integer: true, label: 'DAGGERHEART.GENERAL.hope' })
|
||||
}),
|
||||
evasion: new fields.NumberField({
|
||||
|
|
@ -87,6 +89,13 @@ export default class DhCompanion extends BaseDataActor {
|
|||
};
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**@inheritdoc */
|
||||
static DEFAULT_ICON = 'systems/daggerheart/assets/icons/documents/actors/capybara.svg';
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
get proficiency() {
|
||||
return this.partner?.system?.proficiency ?? 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@ import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayFie
|
|||
import DHEnvironmentSettings from '../../applications/sheets-configs/environment-settings.mjs';
|
||||
|
||||
export default class DhEnvironment extends BaseDataActor {
|
||||
/**@override */
|
||||
static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Environment'];
|
||||
|
||||
/**@inheritdoc */
|
||||
static get metadata() {
|
||||
return foundry.utils.mergeObject(super.metadata, {
|
||||
label: 'TYPES.Actor.environment',
|
||||
|
|
@ -14,6 +16,7 @@ export default class DhEnvironment extends BaseDataActor {
|
|||
});
|
||||
}
|
||||
|
||||
/**@inheritdoc */
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
|
|
@ -37,6 +40,13 @@ export default class DhEnvironment extends BaseDataActor {
|
|||
};
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**@inheritdoc */
|
||||
static DEFAULT_ICON = 'systems/daggerheart/assets/icons/documents/actors/forest.svg';
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
get features() {
|
||||
return this.parent.items.filter(x => x.type === 'feature');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ class DhCountdown extends foundry.abstract.DataModel {
|
|||
required: true,
|
||||
choices: CONFIG.DH.GENERAL.countdownTypes,
|
||||
initial: CONFIG.DH.GENERAL.countdownTypes.custom.id,
|
||||
label: 'DAGGERHEART.APPLICATIONS.Countdown.FIELDS.countdowns.element.progress.type.value.label'
|
||||
label: 'DAGGERHEART.GENERAL.type'
|
||||
}),
|
||||
label: new fields.StringField({
|
||||
label: 'DAGGERHEART.APPLICATIONS.Countdown.FIELDS.countdowns.element.progress.type.label.label'
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ const attributeField = label =>
|
|||
tierMarked: new fields.BooleanField({ initial: false })
|
||||
});
|
||||
|
||||
const resourceField = (max = 0, label, reverse = false, maxLabel) =>
|
||||
const resourceField = (max = 0, initial = 0, label, reverse = false, maxLabel) =>
|
||||
new fields.SchemaField({
|
||||
value: new fields.NumberField({ initial: 0, min: 0, integer: true, label }),
|
||||
value: new fields.NumberField({ initial: initial, min: 0, integer: true, label }),
|
||||
max: new fields.NumberField({
|
||||
initial: max,
|
||||
integer: true,
|
||||
|
|
|
|||
|
|
@ -19,10 +19,26 @@ export default class DHAncestry extends BaseDataItem {
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**@override */
|
||||
static DEFAULT_ICON = 'systems/daggerheart/assets/icons/documents/items/family-tree.svg';
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Gets the primary feature.
|
||||
* @type {foundry.documents.Item|null} Returns the item of the first feature with type "primary" or null if none is found.
|
||||
*/
|
||||
get primaryFeature() {
|
||||
return this.features.find(x => x.type === CONFIG.DH.ITEM.featureSubTypes.primary)?.item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the secondary feature.
|
||||
* @type {foundry.documents.Item|null} Returns the item of the first feature with type "secondary" or null if none is found.
|
||||
*/
|
||||
get secondaryFeature() {
|
||||
return this.features.find(x => x.type === CONFIG.DH.ITEM.featureSubTypes.secondary)?.item;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,12 +42,20 @@ export default class DHArmor extends AttachableItem {
|
|||
};
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**@override */
|
||||
static DEFAULT_ICON = 'systems/daggerheart/assets/icons/documents/items/chest-armor.svg';
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
get customActions() {
|
||||
return this.actions.filter(
|
||||
action => !this.armorFeatures.some(feature => feature.actionIds.includes(action.id))
|
||||
);
|
||||
}
|
||||
|
||||
/**@inheritdoc */
|
||||
async _preUpdate(changes, options, user) {
|
||||
const allowed = await super._preUpdate(changes, options, user);
|
||||
if (allowed === false) return false;
|
||||
|
|
@ -68,7 +76,7 @@ export default class DHArmor extends AttachableItem {
|
|||
return acc;
|
||||
}, {});
|
||||
|
||||
for (var feature of added) {
|
||||
for (const feature of added) {
|
||||
const featureData = armorFeatures[feature.value];
|
||||
if (featureData.effects?.length > 0) {
|
||||
const embeddedItems = await this.parent.createEmbeddedDocuments(
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
* @property {boolean} isInventoryItem- Indicates whether items of this type is a Inventory Item
|
||||
*/
|
||||
|
||||
import { addLinkedItemsDiff, updateLinkedItemApps } from '../../helpers/utils.mjs';
|
||||
import { addLinkedItemsDiff, createScrollText, getScrollTextData, updateLinkedItemApps } from '../../helpers/utils.mjs';
|
||||
import { ActionsField } from '../fields/actionField.mjs';
|
||||
import FormulaField from '../fields/formulaField.mjs';
|
||||
|
||||
|
|
@ -56,6 +56,11 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
|||
initial: null,
|
||||
nullable: true
|
||||
}),
|
||||
progression: new fields.StringField({
|
||||
required: true,
|
||||
choices: CONFIG.DH.ITEM.itemResourceProgression,
|
||||
initial: CONFIG.DH.ITEM.itemResourceProgression.increasing.id
|
||||
}),
|
||||
diceStates: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
value: new fields.NumberField({ integer: true, initial: 1, min: 1 }),
|
||||
|
|
@ -79,6 +84,16 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
|||
return schema;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* The default icon used for newly created Item documents
|
||||
* @type {string}
|
||||
*/
|
||||
static DEFAULT_ICON = null;
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Convenient access to the item's actor, if it exists.
|
||||
* @returns {foundry.documents.Actor | null}
|
||||
|
|
@ -178,11 +193,20 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
|||
if (allowed === false) return false;
|
||||
|
||||
addLinkedItemsDiff(changed.system?.features, this.features, options);
|
||||
|
||||
const autoSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation);
|
||||
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');
|
||||
options.scrollingTextData = [armorData];
|
||||
}
|
||||
}
|
||||
|
||||
_onUpdate(changed, options, userId) {
|
||||
super._onUpdate(changed, options, userId);
|
||||
|
||||
updateLinkedItemApps(options, this.parent.sheet);
|
||||
createScrollText(this.parent?.parent, options.scrollingTextData);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,6 +81,13 @@ export default class DHBeastform extends BaseDataItem {
|
|||
};
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**@override */
|
||||
static DEFAULT_ICON = 'systems/daggerheart/assets/icons/documents/items/wolf-head.svg';
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
async _preCreate() {
|
||||
if (!this.actor) return;
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,13 @@ export default class DHClass extends BaseDataItem {
|
|||
};
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**@override */
|
||||
static DEFAULT_ICON = 'systems/daggerheart/assets/icons/documents/items/laurel-crown.svg';
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
get hopeFeatures() {
|
||||
return this.features.filter(x => x.type === CONFIG.DH.ITEM.featureSubTypes.hope).map(x => x.item);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,10 +13,15 @@ export default class DHCommunity extends BaseDataItem {
|
|||
|
||||
/** @inheritDoc */
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
...super.defineSchema(),
|
||||
features: new ForeignDocumentUUIDArrayField({ type: 'Item' })
|
||||
};
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**@override */
|
||||
static DEFAULT_ICON = 'systems/daggerheart/assets/icons/documents/items/village.svg';
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,4 +22,10 @@ export default class DHConsumable extends BaseDataItem {
|
|||
consumeOnUse: new fields.BooleanField({ initial: false })
|
||||
};
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**@override */
|
||||
static DEFAULT_ICON = 'systems/daggerheart/assets/icons/documents/items/round-potion.svg';
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,18 +33,26 @@ export default class DHDomainCard extends BaseDataItem {
|
|||
};
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**@override */
|
||||
static DEFAULT_ICON = 'systems/daggerheart/assets/icons/documents/items/card-play.svg';
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**@inheritdoc */
|
||||
async _preCreate(data, options, user) {
|
||||
const allowed = await super._preCreate(data, options, user);
|
||||
if (allowed === false) return;
|
||||
|
||||
if (this.actor?.type === 'character') {
|
||||
if (!this.actor.system.class.value) {
|
||||
const actorClasses = this.actor.items.filter(x => x.type === 'class');
|
||||
if (!actorClasses.length) {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.noClassSelected'));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.actor.system.domains.find(x => x === this.domain)) {
|
||||
if (!actorClasses.some(c => c.system.domains.find(x => x === this.domain))) {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.lacksDomain'));
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,13 @@ export default class DHFeature extends BaseDataItem {
|
|||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**@override */
|
||||
static DEFAULT_ICON = 'systems/daggerheart/assets/icons/documents/items/stars-stack.svg';
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @inheritDoc */
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
|
|
|
|||
|
|
@ -19,4 +19,11 @@ export default class DHLoot extends BaseDataItem {
|
|||
...super.defineSchema()
|
||||
};
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**@override */
|
||||
static DEFAULT_ICON = 'systems/daggerheart/assets/icons/documents/items/open-treasure-chest.svg';
|
||||
|
||||
/* -------------------------------------------- */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,13 @@ export default class DHSubclass extends BaseDataItem {
|
|||
};
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**@override */
|
||||
static DEFAULT_ICON = 'systems/daggerheart/assets/icons/documents/items/laurels.svg';
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
get foundationFeatures() {
|
||||
return this.features.filter(x => x.type === CONFIG.DH.ITEM.featureSubTypes.foundation).map(x => x.item);
|
||||
}
|
||||
|
|
@ -43,6 +50,7 @@ export default class DHSubclass extends BaseDataItem {
|
|||
|
||||
async _preCreate(data, options, user) {
|
||||
if (this.actor?.type === 'character') {
|
||||
const dataUuid = data.uuid ?? data._stats?.compendiumSource ?? `Item.${data._id}`;
|
||||
if (this.actor.system.class.subclass) {
|
||||
if (this.actor.system.multiclass.subclass) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.subclassesAlreadyPresent'));
|
||||
|
|
@ -54,7 +62,7 @@ export default class DHSubclass extends BaseDataItem {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (multiclass.system.subclasses.every(x => x.uuid !== (data.uuid ?? `Item.${data._id}`))) {
|
||||
if (multiclass.system.subclasses.every(x => x.uuid !== dataUuid)) {
|
||||
ui.notifications.error(
|
||||
game.i18n.localize('DAGGERHEART.UI.Notifications.subclassNotInMulticlass')
|
||||
);
|
||||
|
|
@ -69,7 +77,7 @@ export default class DHSubclass extends BaseDataItem {
|
|||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.missingClass'));
|
||||
return false;
|
||||
}
|
||||
if (actorClass.system.subclasses.every(x => x.uuid !== (data.uuid ?? `Item.${data._id}`))) {
|
||||
if (actorClass.system.subclasses.every(x => x.uuid !== dataUuid)) {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.subclassNotInClass'));
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,6 +80,13 @@ export default class DHWeapon extends AttachableItem {
|
|||
};
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**@override */
|
||||
static DEFAULT_ICON = 'systems/daggerheart/assets/icons/documents/items/battered-axe.svg';
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
get actionsList() {
|
||||
return [this.attack, ...this.actions];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,17 @@ export default class DhAutomation extends foundry.abstract.DataModel {
|
|||
initial: true,
|
||||
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.effects.rangeDependent.label'
|
||||
})
|
||||
}),
|
||||
damageReductionRulesDefault: new fields.StringField({
|
||||
required: true,
|
||||
choices: CONFIG.DH.GENERAL.ruleChoice,
|
||||
initial: CONFIG.DH.GENERAL.ruleChoice.onWithToggle.id,
|
||||
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.damageReductionRulesDefault.label'
|
||||
}),
|
||||
resourceScrollTexts: new fields.BooleanField({
|
||||
required: true,
|
||||
initial: true,
|
||||
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.resourceScrollTexts.label'
|
||||
})
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,3 +5,4 @@ export { default as DhActiveEffect } from './activeEffect.mjs';
|
|||
export { default as DhChatMessage } from './chatMessage.mjs';
|
||||
export { default as DhToken } from './token.mjs';
|
||||
export { default as DhTooltipManager } from './tooltipManager.mjs';
|
||||
export { default as DhTemplateManager } from './templateManager.mjs';
|
||||
|
|
|
|||
|
|
@ -147,4 +147,32 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
|
|||
|
||||
cls.create(msg);
|
||||
}
|
||||
|
||||
prepareDerivedData() {
|
||||
/* Preventing subclass features from transferring to actor if they do not have the right subclass advancement */
|
||||
if (this.parent?.type === 'feature') {
|
||||
const origSubclassParent = this.parent.system.originItemType === 'subclass';
|
||||
if (origSubclassParent) {
|
||||
const subclass = this.parent.parent.items.find(
|
||||
x =>
|
||||
x.type === 'subclass' &&
|
||||
x.system.isMulticlass === (this.parent.system.identifier === 'multiclass')
|
||||
);
|
||||
|
||||
if (subclass) {
|
||||
const featureState = subclass.system.featureState;
|
||||
const featureType = subclass
|
||||
? (subclass.system.features.find(x => x.item?.uuid === this.parent.uuid)?.type ?? null)
|
||||
: null;
|
||||
|
||||
if (
|
||||
(featureType === CONFIG.DH.ITEM.featureSubTypes.specialization && featureState < 2) ||
|
||||
(featureType === CONFIG.DH.ITEM.featureSubTypes.mastery && featureState < 3)
|
||||
) {
|
||||
this.transfer = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,23 @@ export default class DhpActor extends Actor {
|
|||
return this.system.metadata.isNPC;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**@inheritdoc */
|
||||
static getDefaultArtwork(actorData) {
|
||||
const { type } = actorData;
|
||||
const Model = CONFIG.Actor.dataModels[type];
|
||||
const img = Model.DEFAULT_ICON ?? this.DEFAULT_ICON;
|
||||
return {
|
||||
img,
|
||||
texture: {
|
||||
src: img
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @inheritDoc */
|
||||
getEmbeddedDocument(embeddedName, id, options) {
|
||||
let doc;
|
||||
|
|
@ -39,6 +56,7 @@ export default class DhpActor extends Actor {
|
|||
return doc;
|
||||
}
|
||||
|
||||
/**@inheritdoc */
|
||||
async _preCreate(data, options, user) {
|
||||
if ((await super._preCreate(data, options, user)) === false) return false;
|
||||
|
||||
|
|
@ -455,6 +473,7 @@ export default class DhpActor extends Actor {
|
|||
return ActiveEffect.implementation.create(effect, { parent: this, keepId: true });
|
||||
}
|
||||
|
||||
/**@inheritdoc */
|
||||
getRollData() {
|
||||
const rollData = super.getRollData();
|
||||
rollData.system = this.system.getRollData();
|
||||
|
|
@ -464,14 +483,17 @@ export default class DhpActor extends Actor {
|
|||
}
|
||||
|
||||
#canReduceDamage(hpDamage, type) {
|
||||
const { stressDamageReduction, disabledArmor } = this.system.rules.damageReduction;
|
||||
if (disabledArmor) return false;
|
||||
|
||||
const availableStress = this.system.resources.stress.max - this.system.resources.stress.value;
|
||||
|
||||
const canUseArmor =
|
||||
this.system.armor &&
|
||||
this.system.armor.system.marks.value < this.system.armorScore &&
|
||||
type.every(t => this.system.armorApplicableDamageTypes[t] === true);
|
||||
const canUseStress = Object.keys(this.system.rules.damageReduction.stressDamageReduction).reduce((acc, x) => {
|
||||
const rule = this.system.rules.damageReduction.stressDamageReduction[x];
|
||||
const canUseStress = Object.keys(stressDamageReduction).reduce((acc, x) => {
|
||||
const rule = stressDamageReduction[x];
|
||||
if (damageKeyToNumber(x) <= hpDamage) return acc || (rule.enabled && availableStress >= rule.cost);
|
||||
return acc;
|
||||
}, false);
|
||||
|
|
@ -537,8 +559,8 @@ export default class DhpActor extends Actor {
|
|||
|
||||
updates.forEach(
|
||||
u =>
|
||||
(u.value =
|
||||
u.key === 'fear' || this.system?.resources?.[u.key]?.isReversed === false ? u.value * -1 : u.value)
|
||||
(u.value =
|
||||
u.key === 'fear' || this.system?.resources?.[u.key]?.isReversed === false ? u.value * -1 : u.value)
|
||||
);
|
||||
|
||||
await this.modifyResource(updates);
|
||||
|
|
@ -584,9 +606,9 @@ export default class DhpActor extends Actor {
|
|||
|
||||
updates.forEach(
|
||||
u =>
|
||||
(u.value = !(u.key === 'fear' || this.system?.resources?.[u.key]?.isReversed === false)
|
||||
? u.value * -1
|
||||
: u.value)
|
||||
(u.value = !(u.key === 'fear' || this.system?.resources?.[u.key]?.isReversed === false)
|
||||
? u.value * -1
|
||||
: u.value)
|
||||
);
|
||||
|
||||
await this.modifyResource(updates);
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
|||
elements.forEach(e => {
|
||||
const uuid = e.dataset.permId,
|
||||
document = fromUuidSync(uuid);
|
||||
if (!document) return;
|
||||
|
||||
e.setAttribute('data-view-perm', document.testUserPermission(game.user, 'OBSERVER'));
|
||||
e.setAttribute('data-use-perm', document.testUserPermission(game.user, 'OWNER'));
|
||||
});
|
||||
|
|
@ -68,7 +70,7 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
|||
|
||||
getTargetList() {
|
||||
const targets = this.system.hitTargets;
|
||||
return targets.map(target => game.canvas.tokens.documentCollection.find(t => t.actor.uuid === target.actorId));
|
||||
return targets.map(target => game.canvas.tokens.documentCollection.find(t => t.actor?.uuid === target.actorId));
|
||||
}
|
||||
|
||||
async onDamage(event) {
|
||||
|
|
@ -88,7 +90,7 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
|||
|
||||
if (targets.length === 0)
|
||||
return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelected'));
|
||||
|
||||
|
||||
for (let target of targets) {
|
||||
let damages = foundry.utils.deepClone(this.system.damage);
|
||||
if (
|
||||
|
|
@ -139,9 +141,9 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
|||
}
|
||||
|
||||
consumeOnSuccess() {
|
||||
if(!this.system.successConsumed && !this.system.targetSelection) {
|
||||
if (!this.system.successConsumed && !this.system.targetSelection) {
|
||||
const action = this.system.action;
|
||||
if(action) action.consume(this.system, true);
|
||||
if (action) action.consume(this.system, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,8 +74,8 @@ export default class DHItem extends foundry.documents.Item {
|
|||
isInventoryItem === true
|
||||
? 'Inventory Items' //TODO localize
|
||||
: isInventoryItem === false
|
||||
? 'Character Items' //TODO localize
|
||||
: 'Other'; //TODO localize
|
||||
? 'Character Items' //TODO localize
|
||||
: 'Other'; //TODO localize
|
||||
|
||||
return { value: type, label, group };
|
||||
}
|
||||
|
|
@ -112,12 +112,25 @@ export default class DHItem extends foundry.documents.Item {
|
|||
* Generate a localized label array for this item.
|
||||
* @returns {(string | { value: string, icons: string[] })[]} An array of localized strings and damage label objects.
|
||||
*/
|
||||
getLabels() {
|
||||
_getLabels() {
|
||||
const labels = [];
|
||||
if (this.system.getLabels) labels.push(...this.system.getLabels());
|
||||
if (this.system._getLabels) labels.push(...this.system._getLabels());
|
||||
return labels;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**@inheritdoc */
|
||||
static getDefaultArtwork(itemData) {
|
||||
const { type } = itemData;
|
||||
const Model = CONFIG.Item.dataModels[type];
|
||||
const img = Model.DEFAULT_ICON ?? this.DEFAULT_ICON;
|
||||
return { img };
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
|
||||
async use(event) {
|
||||
const actions = new Set(this.system.actionsList);
|
||||
if (actions?.size) {
|
||||
|
|
@ -139,10 +152,10 @@ export default class DHItem extends foundry.documents.Item {
|
|||
this.type === 'ancestry'
|
||||
? game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.ancestryTitle')
|
||||
: this.type === 'community'
|
||||
? game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.communityTitle')
|
||||
: this.type === 'feature'
|
||||
? game.i18n.localize('TYPES.Item.feature')
|
||||
: game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.subclassFeatureTitle'),
|
||||
? game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.communityTitle')
|
||||
: this.type === 'feature'
|
||||
? game.i18n.localize('TYPES.Item.feature')
|
||||
: game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.subclassFeatureTitle'),
|
||||
origin: origin,
|
||||
img: this.img,
|
||||
item: {
|
||||
|
|
|
|||
99
module/documents/templateManager.mjs
Normal file
99
module/documents/templateManager.mjs
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/**
|
||||
* A singleton class that handles preview templates.
|
||||
*/
|
||||
|
||||
export default class DhTemplateManager {
|
||||
#activePreview;
|
||||
|
||||
/**
|
||||
* Create a template preview, deactivating any existing ones.
|
||||
* @param {object} data
|
||||
*/
|
||||
async createPreview(data) {
|
||||
const template = await canvas.templates._createPreview(data, { renderSheet: false });
|
||||
|
||||
this.#activePreview = {
|
||||
document: template.document,
|
||||
object: template,
|
||||
origin: { x: template.document.x, y: template.document.y }
|
||||
};
|
||||
|
||||
this.#activePreview.events = {
|
||||
contextmenu: this.#cancelTemplate.bind(this),
|
||||
mousedown: this.#confirmTemplate.bind(this),
|
||||
mousemove: this.#onDragMouseMove.bind(this),
|
||||
wheel: this.#onMouseWheel.bind(this)
|
||||
};
|
||||
canvas.stage.on('mousemove', this.#activePreview.events.mousemove);
|
||||
canvas.stage.on('mousedown', this.#activePreview.events.mousedown);
|
||||
|
||||
canvas.app.view.addEventListener('wheel', this.#activePreview.events.wheel, true);
|
||||
canvas.app.view.addEventListener('contextmenu', this.#activePreview.events.contextmenu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the movement of the temlate preview on mousedrag.
|
||||
* @param {mousemove Event} event
|
||||
*/
|
||||
#onDragMouseMove(event) {
|
||||
event.stopPropagation();
|
||||
const { moveTime, object } = this.#activePreview;
|
||||
const update = {};
|
||||
|
||||
const now = Date.now();
|
||||
if (now - (moveTime || 0) <= 16) return;
|
||||
this.#activePreview.moveTime = now;
|
||||
|
||||
let cursor = event.getLocalPosition(canvas.templates);
|
||||
|
||||
Object.assign(update, canvas.grid.getCenterPoint(cursor));
|
||||
|
||||
object.document.updateSource(update);
|
||||
object.renderFlags.set({ refresh: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the rotation of the preview template on scrolling.
|
||||
* @param {wheel Event} event
|
||||
*/
|
||||
#onMouseWheel(event) {
|
||||
if (!event.shiftKey) return;
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
const { moveTime, object } = this.#activePreview;
|
||||
|
||||
const now = Date.now();
|
||||
if (now - (moveTime || 0) <= 16) return;
|
||||
this.#activePreview.moveTime = now;
|
||||
|
||||
object.document.updateSource({
|
||||
direction: object.document.direction + event.deltaY * 0.2
|
||||
});
|
||||
object.renderFlags.set({ refresh: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the preview template on right-click.
|
||||
* @param {contextmenu Event} event
|
||||
*/
|
||||
#cancelTemplate(event) {
|
||||
const { mousemove, mousedown, contextmenu } = this.#activePreview.events;
|
||||
canvas.templates._onDragLeftCancel(event);
|
||||
|
||||
canvas.stage.off('mousemove', mousemove);
|
||||
canvas.stage.off('mousedown', mousedown);
|
||||
canvas.app.view.removeEventListener('contextmenu', contextmenu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a real MeasuredTemplate at the preview location and cancels the preview.
|
||||
* @param {click Event} event
|
||||
*/
|
||||
#confirmTemplate(event) {
|
||||
event.stopPropagation();
|
||||
|
||||
canvas.scene.createEmbeddedDocuments('MeasuredTemplate', [this.#activePreview.document.toObject()]);
|
||||
|
||||
this.#cancelTemplate(event);
|
||||
}
|
||||
}
|
||||
|
|
@ -59,14 +59,14 @@ export const renderMeasuredTemplate = async event => {
|
|||
const distance = type === CONFIG.DH.GENERAL.templateTypes.EMANATION ? baseDistance + 2.5 : baseDistance;
|
||||
|
||||
const { width, height } = game.canvas.scene.dimensions;
|
||||
canvas.scene.createEmbeddedDocuments('MeasuredTemplate', [
|
||||
{
|
||||
x: width / 2,
|
||||
y: height / 2,
|
||||
t: usedType,
|
||||
distance: distance,
|
||||
width: type === CONST.MEASURED_TEMPLATE_TYPES.RAY ? 5 : undefined,
|
||||
angle: angle
|
||||
}
|
||||
]);
|
||||
const data = {
|
||||
x: width / 2,
|
||||
y: height / 2,
|
||||
t: usedType,
|
||||
distance: distance,
|
||||
width: type === CONST.MEASURED_TEMPLATE_TYPES.RAY ? 5 : undefined,
|
||||
angle: angle
|
||||
};
|
||||
|
||||
CONFIG.ux.TemplateManager.createPreview(data);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -324,3 +324,48 @@ export const arraysEqual = (a, b) =>
|
|||
[...new Set([...a, ...b])].every(v => a.filter(e => e === v).length === b.filter(e => e === v).length);
|
||||
|
||||
export const setsEqual = (a, b) => a.size === b.size && [...a].every(value => b.has(value));
|
||||
|
||||
export function getScrollTextData(resources, resource, key) {
|
||||
const { reversed, label } = CONFIG.DH.ACTOR.scrollingTextResource[key];
|
||||
const { BOTTOM, TOP } = CONST.TEXT_ANCHOR_POINTS;
|
||||
const increased = resources[key].value < resource.value;
|
||||
const value = -1 * (resources[key].value - resource.value);
|
||||
|
||||
const text = `${game.i18n.localize(label)} ${value.signedString()}`;
|
||||
|
||||
const stroke = increased ? (reversed ? 0xffffff : 0x000000) : reversed ? 0x000000 : 0xffffff;
|
||||
const fill = increased ? (reversed ? 0x0032b1 : 0xffe760) : reversed ? 0xffe760 : 0x0032b1;
|
||||
const direction = increased ? (reversed ? BOTTOM : TOP) : reversed ? TOP : BOTTOM;
|
||||
|
||||
return { text, stroke, fill, direction };
|
||||
}
|
||||
|
||||
export function createScrollText(actor, optionsData) {
|
||||
if (actor && optionsData?.length) {
|
||||
actor.getDependentTokens().forEach(token => {
|
||||
optionsData.forEach(data => {
|
||||
const { text, ...options } = data;
|
||||
canvas.interface.createScrollingText(token.getCenterPoint(), data.text, {
|
||||
duration: 2000,
|
||||
distance: token.h,
|
||||
jitter: 0,
|
||||
...options
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function createEmbeddedItemWithEffects(actor, baseData, update) {
|
||||
const data = baseData.uuid.startsWith('Compendium') ? await foundry.utils.fromUuid(baseData.uuid) : baseData;
|
||||
const [doc] = await actor.createEmbeddedDocuments('Item', [
|
||||
{
|
||||
...(update ?? data),
|
||||
id: data.id,
|
||||
uuid: data.uuid,
|
||||
effects: data.effects?.map(effect => effect.toObject())
|
||||
}
|
||||
]);
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue