diff --git a/src/module/migrations.ts b/src/module/migrations.ts index 0dafc3b5..e9a157c7 100644 --- a/src/module/migrations.ts +++ b/src/module/migrations.ts @@ -1,5 +1,7 @@ import { migrate as migrate001 } from "./migrations/001"; import { migrate as migrate002 } from "./migrations/002"; +import { migrate as migrate003 } from "./migrations/003"; + import notifications from "./ui/notifications"; async function migrate(): Promise { @@ -70,7 +72,7 @@ function getTargetMigrationVersion(): number { return migrations.length; } -const migrations: Array<() => Promise> = [migrate001, migrate002]; +const migrations: Array<() => Promise> = [migrate001, migrate002, migrate003]; function isFirstWorldStart(migrationVersion: number): boolean { return migrationVersion < 0; diff --git a/src/module/migrations/003.ts b/src/module/migrations/003.ts new file mode 100644 index 00000000..5801923e --- /dev/null +++ b/src/module/migrations/003.ts @@ -0,0 +1,141 @@ +export async function migrate(): Promise { + await migrateItems(); + await migrateActors(); + await migrateScenes(); + await migrateCompendiums(); +} + +async function migrateItems() { + for (const item of game.items?.entities ?? []) { + try { + const updateData = getItemUpdateData(item._data); + if (updateData) { + console.log(`Migrating Item entity ${item.name} (${item.id})`); + await item.update(updateData), { enforceTypes: false }; + } + } catch (err) { + err.message = `Error during migration of Item entity ${item.name} (${item.id}), continuing anyways.`; + console.error(err); + } + } +} + +function getItemUpdateData(itemData: DeepPartial) { + if (!["loot"].includes(itemData.type ?? "")) return undefined; + return { + data: { + "-=equipped": null, + }, + }; +} + +async function migrateActors() { + for (const actor of game.actors?.entities ?? []) { + try { + const updateData = getActorUpdateData(actor._data); + if (updateData) { + console.log(`Migrating Actor entity ${actor.name} (${actor.id})`); + await actor.update(updateData, { enforceTypes: false }); + } + } catch (err) { + err.message = `Error during migration of Actor entity ${actor.name} (${actor.id}), continuing anyways.`; + console.error(err); + } + } +} + +function getActorUpdateData(actorData: DeepPartial) { + let hasItemUpdates = false; + const items = actorData.items?.map((itemData) => { + const update = itemData ? getItemUpdateData(itemData) : undefined; + if (update) { + hasItemUpdates = true; + return mergeObject(itemData, update, { enforceTypes: false, inplace: false }); + } else { + return itemData; + } + }); + return hasItemUpdates ? { items } : undefined; +} + +async function migrateScenes() { + for (const scene of game.scenes?.entities ?? []) { + try { + const updateData = getSceneUpdateData(scene._data); + if (updateData) { + console.log(`Migrating Scene entity ${scene.name} (${scene.id})`); + await scene.update(updateData, { enforceTypes: false }); + } + } catch (err) { + err.message = `Error during migration of Scene entity ${scene.name} (${scene.id}), continuing anyways.`; + console.error(err); + } + } +} + +function getSceneUpdateData(sceneData: Scene.Data) { + let hasTokenUpdates = false; + const tokens = sceneData.tokens.map((tokenData) => { + if (!tokenData.actorId || tokenData.actorLink || tokenData.actorData.data) { + tokenData.actorData = {}; + hasTokenUpdates = true; + return tokenData; + } + const token = new Token(tokenData); + if (!token.actor) { + tokenData.actorId = (null as unknown) as string; + tokenData.actorData = {}; + hasTokenUpdates = true; + } else if (!tokenData.actorLink) { + const actorUpdateData = getActorUpdateData(token.data.actorData); + tokenData.actorData = mergeObject(token.data.actorData, actorUpdateData); + hasTokenUpdates = true; + } + return tokenData; + }); + if (!hasTokenUpdates) return undefined; + return hasTokenUpdates ? { tokens } : undefined; +} + +async function migrateCompendiums() { + for (const compendium of game.packs ?? []) { + if (compendium.metadata.package !== "world") continue; + if (!["Actor", "Item", "Scene"].includes(compendium.metadata.entity)) continue; + await migrateCompendium(compendium); + } +} + +async function migrateCompendium(compendium: Compendium) { + const entityName = compendium.metadata.entity; + if (!["Actor", "Item", "Scene"].includes(entityName)) return; + const wasLocked = compendium.locked; + await compendium.configure({ locked: false }); + + const content = await compendium.getContent(); + + for (const entity of content) { + try { + const getUpdateData = (entity: Entity) => { + switch (entityName) { + case "Item": + return getItemUpdateData(entity._data); + case "Actor": + return getActorUpdateData(entity._data); + case "Scene": + return getSceneUpdateData(entity._data as Scene.Data); + } + }; + const updateData = getUpdateData(entity); + if (updateData) { + console.log(`Migrating entity ${entity.name} (${entity.id}) in compendium ${compendium.collection}`); + await compendium.updateEntity({ ...updateData, _id: entity._id }); + } + } catch (err) { + err.message = `Error during migration of entity ${entity.name} (${entity.id}) in compendium ${compendium.collection}, continuing anyways.`; + console.error(err); + } + } + + await compendium.migrate({}); + await compendium.configure({ locked: wasLocked }); +} diff --git a/src/templates/actor/partials/items-overview.hbs b/src/templates/actor/partials/items-overview.hbs index c003fb8d..e0dc4e20 100644 --- a/src/templates/actor/partials/items-overview.hbs +++ b/src/templates/actor/partials/items-overview.hbs @@ -55,7 +55,7 @@ {{!-- !-- Render a list row from a given item. !-- It is a flexbox with a child for each item value of interest. -!-- An equipped checkbox is rendered if item.data.data.equipped is defined. +!-- An equipped checkbox is rendered except for the case item.data.type==='loot'. !-- The partial assumes a variable item to be given in the context. !-- If the partial is called with a partial block, the partial block !-- content is inserted before the description. @@ -66,7 +66,7 @@ {{#*inline "itemListEntry"}}
  • {{!-- equipped --}} - {{#if (ne item.data.data.equipped undefined)}} + {{#if (ne item.data.type 'loot')}} {{/if}}