mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-12 03:31:07 +01:00
Merge branch 'main' of https://github.com/Foundryborne/daggerheart
This commit is contained in:
commit
cedf07bc35
14 changed files with 122 additions and 81 deletions
|
|
@ -13,6 +13,7 @@ import { enrichedDualityRoll } from './module/enrichers/DualityRollEnricher.mjs'
|
||||||
import { registerCountdownHooks } from './module/data/countdowns.mjs';
|
import { registerCountdownHooks } from './module/data/countdowns.mjs';
|
||||||
import {
|
import {
|
||||||
handlebarsRegistration,
|
handlebarsRegistration,
|
||||||
|
runMigrations,
|
||||||
settingsRegistration,
|
settingsRegistration,
|
||||||
socketRegistration
|
socketRegistration
|
||||||
} from './module/systemRegistration/_module.mjs';
|
} from './module/systemRegistration/_module.mjs';
|
||||||
|
|
@ -168,6 +169,8 @@ Hooks.on('ready', async () => {
|
||||||
game.user.setFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.userFlags.welcomeMessage, true);
|
game.user.setFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.userFlags.welcomeMessage, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
runMigrations();
|
||||||
});
|
});
|
||||||
|
|
||||||
Hooks.once('dicesoniceready', () => {});
|
Hooks.once('dicesoniceready', () => {});
|
||||||
|
|
|
||||||
|
|
@ -193,7 +193,9 @@
|
||||||
"companionLevelup": {
|
"companionLevelup": {
|
||||||
"confirmTitle": "Companion Levelup",
|
"confirmTitle": "Companion Levelup",
|
||||||
"confirmText": "Would you like to level up your companion {name} by {levelChange} levels at this time? (You can do it manually later)"
|
"confirmText": "Would you like to level up your companion {name} by {levelChange} levels at this time? (You can do it manually later)"
|
||||||
}
|
},
|
||||||
|
"InvalidOldCharacterImportTitle": "Old Character Import",
|
||||||
|
"InvalidOldCharacterImportText": "Character data exported prior to system version 1.1 will not generate a complete character. Do you wish to continue?"
|
||||||
},
|
},
|
||||||
"Companion": {
|
"Companion": {
|
||||||
"FIELDS": {
|
"FIELDS": {
|
||||||
|
|
|
||||||
|
|
@ -591,7 +591,6 @@ export default function DHApplicationMixin(Base) {
|
||||||
if (featureOnCharacter) {
|
if (featureOnCharacter) {
|
||||||
systemData = {
|
systemData = {
|
||||||
originItemType: this.document.type,
|
originItemType: this.document.type,
|
||||||
originId: this.document.id,
|
|
||||||
identifier: this.document.system.isMulticlass ? 'multiclass' : null
|
identifier: this.document.system.isMulticlass ? 'multiclass' : null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -149,12 +149,12 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
||||||
const { type } = target.dataset;
|
const { type } = target.dataset;
|
||||||
const cls = foundry.documents.Item.implementation;
|
const cls = foundry.documents.Item.implementation;
|
||||||
|
|
||||||
|
const multiclass = this.document.system.isMulticlass ? 'multiclass' : null;
|
||||||
let systemData = {};
|
let systemData = {};
|
||||||
if (this.document.parent?.type === 'character') {
|
if (this.document.parent?.type === 'character') {
|
||||||
systemData = {
|
systemData = {
|
||||||
originItemType: this.document.type,
|
originItemType: this.document.type,
|
||||||
originId: this.document.id,
|
identifier: multiclass ?? type
|
||||||
identifier: this.document.system.isMulticlass ? 'multiclass' : null
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -275,14 +275,15 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
||||||
|
|
||||||
if (this.document.parent?.type === 'character') {
|
if (this.document.parent?.type === 'character') {
|
||||||
const itemData = item.toObject();
|
const itemData = item.toObject();
|
||||||
|
const multiclass = this.document.system.isMulticlass ? 'multiclass' : null;
|
||||||
item = await cls.create(
|
item = await cls.create(
|
||||||
{
|
{
|
||||||
...itemData,
|
...itemData,
|
||||||
|
_stats: { compendiumSource: this.document.uuid },
|
||||||
system: {
|
system: {
|
||||||
...itemData.system,
|
...itemData.system,
|
||||||
originItemType: this.document.type,
|
originItemType: this.document.type,
|
||||||
originId: this.document.id,
|
identifier: multiclass ?? target.dataset.type
|
||||||
identifier: this.document.system.isMulticlass ? 'multiclass' : null
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ parent: this.document.parent }
|
{ parent: this.document.parent }
|
||||||
|
|
|
||||||
|
|
@ -26,5 +26,6 @@ export const gameSettings = {
|
||||||
Fear: 'ResourcesFear'
|
Fear: 'ResourcesFear'
|
||||||
},
|
},
|
||||||
LevelTiers: 'LevelTiers',
|
LevelTiers: 'LevelTiers',
|
||||||
Countdowns: 'Countdowns'
|
Countdowns: 'Countdowns',
|
||||||
|
LastMigrationVersion: 'LastMigrationVersion'
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -444,16 +444,12 @@ export default class DhCharacter extends BaseDataActor {
|
||||||
} else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.subclass.id) {
|
} else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.subclass.id) {
|
||||||
if (this.class.subclass) {
|
if (this.class.subclass) {
|
||||||
const subclassState = this.class.subclass.system.featureState;
|
const subclassState = this.class.subclass.system.featureState;
|
||||||
const subclass =
|
|
||||||
item.system.identifier === 'multiclass' ? this.multiclass.subclass : this.class.subclass;
|
|
||||||
const featureType = subclass
|
|
||||||
? (subclass.system.features.find(x => x.item?.uuid === item.uuid)?.type ?? null)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
featureType === CONFIG.DH.ITEM.featureSubTypes.foundation ||
|
item.system.identifier === CONFIG.DH.ITEM.featureSubTypes.foundation ||
|
||||||
(featureType === CONFIG.DH.ITEM.featureSubTypes.specialization && subclassState >= 2) ||
|
(item.system.identifier === CONFIG.DH.ITEM.featureSubTypes.specialization &&
|
||||||
(featureType === CONFIG.DH.ITEM.featureSubTypes.mastery && subclassState >= 3)
|
subclassState >= 2) ||
|
||||||
|
(item.system.identifier === CONFIG.DH.ITEM.featureSubTypes.mastery && subclassState >= 3)
|
||||||
) {
|
) {
|
||||||
subclassFeatures.push(item);
|
subclassFeatures.push(item);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -144,50 +144,30 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.actor && this.actor.type === 'character' && this.features) {
|
if (this.actor && this.actor.type === 'character' && this.features) {
|
||||||
const featureUpdates = {};
|
const features = [];
|
||||||
for (let f of this.features) {
|
for (let f of this.features) {
|
||||||
const fBase = f.item ?? f;
|
const fBase = f.item ?? f;
|
||||||
const feature = fBase.system ? fBase : await foundry.utils.fromUuid(fBase.uuid);
|
const feature = fBase.system ? fBase : await foundry.utils.fromUuid(fBase.uuid);
|
||||||
const createData = foundry.utils.mergeObject(
|
const multiclass = this.isMulticlass ? 'multiclass' : null;
|
||||||
|
features.push(
|
||||||
|
foundry.utils.mergeObject(
|
||||||
feature.toObject(),
|
feature.toObject(),
|
||||||
{
|
{
|
||||||
|
_stats: { compendiumSource: fBase.uuid },
|
||||||
system: {
|
system: {
|
||||||
originItemType: this.parent.type,
|
originItemType: this.parent.type,
|
||||||
originId: data._id,
|
identifier: multiclass ?? (f.item ? f.type : null)
|
||||||
identifier: this.isMulticlass ? 'multiclass' : null
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ inplace: false }
|
{ inplace: false }
|
||||||
|
)
|
||||||
);
|
);
|
||||||
const [doc] = await this.actor.createEmbeddedDocuments('Item', [createData]);
|
|
||||||
|
|
||||||
if (!featureUpdates.features)
|
|
||||||
featureUpdates.features = this.features.map(x => (x.item ? { ...x, item: x.item.uuid } : x.uuid));
|
|
||||||
|
|
||||||
if (f.item) {
|
|
||||||
const existingFeature = featureUpdates.features.find(x => x.item === f.item.uuid);
|
|
||||||
existingFeature.item = doc.uuid;
|
|
||||||
} else {
|
|
||||||
const replaceIndex = featureUpdates.features.findIndex(x => x === f.uuid);
|
|
||||||
featureUpdates.features.splice(replaceIndex, 1, doc.uuid);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.updateSource(featureUpdates);
|
await this.actor.createEmbeddedDocuments('Item', features);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async _preDelete() {
|
|
||||||
if (!this.actor || this.actor.type !== 'character') return;
|
|
||||||
|
|
||||||
const items = this.actor.items.filter(item => item.system.originId === this.parent.id);
|
|
||||||
if (items.length > 0)
|
|
||||||
await this.actor.deleteEmbeddedDocuments(
|
|
||||||
'Item',
|
|
||||||
items.map(x => x.id)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async _preUpdate(changed, options, userId) {
|
async _preUpdate(changed, options, userId) {
|
||||||
const allowed = await super._preUpdate(changed, options, userId);
|
const allowed = await super._preUpdate(changed, options, userId);
|
||||||
if (allowed === false) return false;
|
if (allowed === false) return false;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import BaseDataItem from './base.mjs';
|
import BaseDataItem from './base.mjs';
|
||||||
import { ActionField, ActionsField } from '../fields/actionField.mjs';
|
|
||||||
|
|
||||||
export default class DHFeature extends BaseDataItem {
|
export default class DHFeature extends BaseDataItem {
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
|
|
@ -30,24 +29,7 @@ export default class DHFeature extends BaseDataItem {
|
||||||
nullable: true,
|
nullable: true,
|
||||||
initial: null
|
initial: null
|
||||||
}),
|
}),
|
||||||
originId: new fields.StringField({ nullable: true, initial: null }),
|
|
||||||
identifier: new fields.StringField()
|
identifier: new fields.StringField()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
get spellcastingModifier() {
|
|
||||||
let traitValue = 0;
|
|
||||||
if (this.actor && this.originId && ['class', 'subclass'].includes(this.originItemType)) {
|
|
||||||
if (this.originItemType === 'subclass') {
|
|
||||||
traitValue =
|
|
||||||
this.actor.system.traits[this.actor.items.get(this.originId).system.spellcastingTrait]?.value ?? 0;
|
|
||||||
} else {
|
|
||||||
const { value: multiclass, subclass } = this.actor.system.multiclass;
|
|
||||||
const selectedSubclass = multiclass?.id === this.originId ? subclass : this.actor.system.class.subclass;
|
|
||||||
traitValue = this.actor.system.traits[selectedSubclass.system.spellcastingTrait]?.value ?? 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return traitValue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { emitAsGM, GMUpdateEvent } from '../systemRegistration/socket.mjs';
|
import { emitAsGM, GMUpdateEvent } from '../systemRegistration/socket.mjs';
|
||||||
import { LevelOptionType } from '../data/levelTier.mjs';
|
import { LevelOptionType } from '../data/levelTier.mjs';
|
||||||
import DHFeature from '../data/item/feature.mjs';
|
import DHFeature from '../data/item/feature.mjs';
|
||||||
import { damageKeyToNumber } from '../helpers/utils.mjs';
|
import { damageKeyToNumber, versionCompare } from '../helpers/utils.mjs';
|
||||||
import DhCompanionLevelUp from '../applications/levelup/companionLevelup.mjs';
|
import DhCompanionLevelUp from '../applications/levelup/companionLevelup.mjs';
|
||||||
|
|
||||||
export default class DhpActor extends Actor {
|
export default class DhpActor extends Actor {
|
||||||
|
|
@ -27,7 +27,7 @@ export default class DhpActor extends Actor {
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
static migrateData(source) {
|
static migrateData(source) {
|
||||||
if(source.system?.attack && !source.system.attack.type) source.system.attack.type = "attack";
|
if (source.system?.attack && !source.system.attack.type) source.system.attack.type = 'attack';
|
||||||
return super.migrateData(source);
|
return super.migrateData(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -573,17 +573,13 @@ export default class DhpActor extends Actor {
|
||||||
updates.find(u => u.key === 'hitPoints').value = modifiedDamage;
|
updates.find(u => u.key === 'hitPoints').value = modifiedDamage;
|
||||||
if (armorSpent) {
|
if (armorSpent) {
|
||||||
const armorUpdate = updates.find(u => u.key === 'armor');
|
const armorUpdate = updates.find(u => u.key === 'armor');
|
||||||
if(armorUpdate)
|
if (armorUpdate) armorUpdate.value += armorSpent;
|
||||||
armorUpdate.value += armorSpent;
|
else updates.push({ value: armorSpent, key: 'armor' });
|
||||||
else
|
|
||||||
updates.push({ value: armorSpent, key: 'armor' });
|
|
||||||
}
|
}
|
||||||
if (stressSpent) {
|
if (stressSpent) {
|
||||||
const stressUpdate = updates.find(u => u.key === 'stress');
|
const stressUpdate = updates.find(u => u.key === 'stress');
|
||||||
if(stressUpdate)
|
if (stressUpdate) stressUpdate.value += stressSpent;
|
||||||
stressUpdate.value += stressSpent;
|
else updates.push({ value: stressSpent, key: 'stress' });
|
||||||
else
|
|
||||||
updates.push({ value: stressSpent, key: 'stress' });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -753,4 +749,26 @@ export default class DhpActor extends Actor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
async importFromJSON(json) {
|
||||||
|
if (!this.type === 'character') return await super.importFromJSON(json);
|
||||||
|
|
||||||
|
if (!CONST.WORLD_DOCUMENT_TYPES.includes(this.documentName)) {
|
||||||
|
throw new Error('Only world Documents may be imported');
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedJSON = JSON.parse(json);
|
||||||
|
if (versionCompare(parsedJSON._stats.systemVersion, '1.1.0')) {
|
||||||
|
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||||
|
window: {
|
||||||
|
title: game.i18n.localize('DAGGERHEART.ACTORS.Character.InvalidOldCharacterImportTitle')
|
||||||
|
},
|
||||||
|
content: game.i18n.localize('DAGGERHEART.ACTORS.Character.InvalidOldCharacterImportText')
|
||||||
|
});
|
||||||
|
if (!confirmed) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await super.importFromJSON(json);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -420,3 +420,14 @@ export async function createEmbeddedItemsWithEffects(actor, baseData) {
|
||||||
export const slugify = name => {
|
export const slugify = name => {
|
||||||
return name.toLowerCase().replaceAll(' ', '-').replaceAll('.', '');
|
return name.toLowerCase().replaceAll(' ', '-').replaceAll('.', '');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const versionCompare = (current, target) => {
|
||||||
|
const currentSplit = current.split('.').map(x => Number.parseInt(x));
|
||||||
|
const targetSplit = target.split('.').map(x => Number.parseInt(x));
|
||||||
|
for (var i = 0; i < currentSplit.length; i++) {
|
||||||
|
if (currentSplit[i] < targetSplit[i]) return true;
|
||||||
|
if (currentSplit[i] > targetSplit[i]) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
export { preloadHandlebarsTemplates as handlebarsRegistration } from './handlebars.mjs';
|
export { preloadHandlebarsTemplates as handlebarsRegistration } from './handlebars.mjs';
|
||||||
export * as settingsRegistration from './settings.mjs';
|
export * as settingsRegistration from './settings.mjs';
|
||||||
export * as socketRegistration from './socket.mjs';
|
export * as socketRegistration from './socket.mjs';
|
||||||
|
export { runMigrations } from './migrations.mjs';
|
||||||
|
|
|
||||||
41
module/systemRegistration/migrations.mjs
Normal file
41
module/systemRegistration/migrations.mjs
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { versionCompare } from '../helpers/utils.mjs';
|
||||||
|
|
||||||
|
export async function runMigrations() {
|
||||||
|
let lastMigrationVersion = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion);
|
||||||
|
if (!lastMigrationVersion) lastMigrationVersion = '1.0.6';
|
||||||
|
|
||||||
|
if (versionCompare(lastMigrationVersion, '1.1.0')) {
|
||||||
|
const compendiumActors = [];
|
||||||
|
for (let pack of game.packs) {
|
||||||
|
const documents = await pack.getDocuments();
|
||||||
|
compendiumActors.push(...documents.filter(x => x.type === 'character'));
|
||||||
|
}
|
||||||
|
|
||||||
|
[...compendiumActors, ...game.actors].forEach(actor => {
|
||||||
|
const items = actor.items.reduce((acc, item) => {
|
||||||
|
if (item.type === 'feature') {
|
||||||
|
const { originItemType, isMulticlass, identifier } = item.system;
|
||||||
|
const base = originItemType
|
||||||
|
? actor.items.find(
|
||||||
|
x => x.type === originItemType && Boolean(isMulticlass) === Boolean(x.system.isMulticlass)
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
if (base) {
|
||||||
|
const feature = base.system.features.find(x => x.item && x.item.uuid === item.uuid);
|
||||||
|
if (feature && identifier !== 'multiclass') {
|
||||||
|
acc.push({ _id: item.id, system: { identifier: feature.type } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
actor.updateEmbeddedDocuments('Item', items);
|
||||||
|
});
|
||||||
|
|
||||||
|
lastMigrationVersion = '1.1.0';
|
||||||
|
}
|
||||||
|
|
||||||
|
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion, lastMigrationVersion);
|
||||||
|
}
|
||||||
|
|
@ -91,6 +91,12 @@ const registerMenus = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const registerNonConfigSettings = () => {
|
const registerNonConfigSettings = () => {
|
||||||
|
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion, {
|
||||||
|
scope: 'world',
|
||||||
|
config: false,
|
||||||
|
type: String
|
||||||
|
});
|
||||||
|
|
||||||
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers, {
|
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers, {
|
||||||
scope: 'world',
|
scope: 'world',
|
||||||
config: false,
|
config: false,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "daggerheart",
|
"id": "daggerheart",
|
||||||
"title": "Daggerheart",
|
"title": "Daggerheart",
|
||||||
"description": "An unofficial implementation of the Daggerheart system",
|
"description": "An unofficial implementation of the Daggerheart system",
|
||||||
"version": "1.0.6",
|
"version": "1.1.0",
|
||||||
"compatibility": {
|
"compatibility": {
|
||||||
"minimum": "13",
|
"minimum": "13",
|
||||||
"verified": "13.347",
|
"verified": "13.347",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue