forked from GitHub-Mirrors/foundry-sw5e
Potentially updated Migration
This commit is contained in:
parent
db5e90281c
commit
37a3e83f3a
3 changed files with 133 additions and 39 deletions
|
@ -1,4 +1,3 @@
|
||||||
// TODO: Update
|
|
||||||
/**
|
/**
|
||||||
* Perform a system migration for the entire World, applying migrations for Actors, Items, and Compendium packs
|
* Perform a system migration for the entire World, applying migrations for Actors, Items, and Compendium packs
|
||||||
* @return {Promise} A Promise which resolves once the migration is completed
|
* @return {Promise} A Promise which resolves once the migration is completed
|
||||||
|
@ -11,7 +10,7 @@ export const migrateWorld = async function() {
|
||||||
try {
|
try {
|
||||||
console.log(`Checking Actor entity ${a.name} for migration needs`);
|
console.log(`Checking Actor entity ${a.name} for migration needs`);
|
||||||
const updateData = await migrateActorData(a.data);
|
const updateData = await migrateActorData(a.data);
|
||||||
if ( !isObjectEmpty(updateData) ) {
|
if ( !foundry.utils.isObjectEmpty(updateData) ) {
|
||||||
console.log(`Migrating Actor entity ${a.name}`);
|
console.log(`Migrating Actor entity ${a.name}`);
|
||||||
await a.update(updateData, {enforceTypes: false});
|
await a.update(updateData, {enforceTypes: false});
|
||||||
}
|
}
|
||||||
|
@ -25,7 +24,7 @@ export const migrateWorld = async function() {
|
||||||
for ( let i of game.items.entities ) {
|
for ( let i of game.items.entities ) {
|
||||||
try {
|
try {
|
||||||
const updateData = migrateItemData(i.data);
|
const updateData = migrateItemData(i.data);
|
||||||
if ( !isObjectEmpty(updateData) ) {
|
if ( !foundry.utils.isObjectEmpty(updateData) ) {
|
||||||
console.log(`Migrating Item entity ${i.name}`);
|
console.log(`Migrating Item entity ${i.name}`);
|
||||||
await i.update(updateData, {enforceTypes: false});
|
await i.update(updateData, {enforceTypes: false});
|
||||||
}
|
}
|
||||||
|
@ -39,7 +38,7 @@ export const migrateWorld = async function() {
|
||||||
for ( let s of game.scenes.entities ) {
|
for ( let s of game.scenes.entities ) {
|
||||||
try {
|
try {
|
||||||
const updateData = await migrateSceneData(s.data);
|
const updateData = await migrateSceneData(s.data);
|
||||||
if ( !isObjectEmpty(updateData) ) {
|
if ( !foundry.utils.isObjectEmpty(updateData) ) {
|
||||||
console.log(`Migrating Scene entity ${s.name}`);
|
console.log(`Migrating Scene entity ${s.name}`);
|
||||||
await s.update(updateData, {enforceTypes: false});
|
await s.update(updateData, {enforceTypes: false});
|
||||||
}
|
}
|
||||||
|
@ -78,40 +77,39 @@ export const migrateCompendium = async function(pack) {
|
||||||
|
|
||||||
// Begin by requesting server-side data model migration and get the migrated content
|
// Begin by requesting server-side data model migration and get the migrated content
|
||||||
await pack.migrate();
|
await pack.migrate();
|
||||||
const content = await pack.getContent();
|
const documents = await pack.getDocuments();
|
||||||
|
|
||||||
// Iterate over compendium entries - applying fine-tuned migration functions
|
// Iterate over compendium entries - applying fine-tuned migration functions
|
||||||
for await ( let ent of content ) {
|
for await ( let doc of documents ) {
|
||||||
let updateData = {};
|
let updateData = {};
|
||||||
try {
|
try {
|
||||||
switch (entity) {
|
switch (entity) {
|
||||||
case "Actor":
|
case "Actor":
|
||||||
updateData = await migrateActorData(ent.data);
|
updateData = await migrateActorData(doc.data);
|
||||||
break;
|
break;
|
||||||
case "Item":
|
case "Item":
|
||||||
updateData = migrateItemData(ent.data);
|
updateData = migrateItemData(doc.data);
|
||||||
break;
|
break;
|
||||||
case "Scene":
|
case "Scene":
|
||||||
updateData = await migrateSceneData(ent.data);
|
updateData = await migrateSceneData(doc.data);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ( isObjectEmpty(updateData) ) continue;
|
if ( foundry.utils.isObjectEmpty(updateData) ) continue;
|
||||||
|
|
||||||
// Save the entry, if data was changed
|
// Save the entry, if data was changed
|
||||||
updateData["_id"] = ent._id;
|
await doc.update(updateData);
|
||||||
await pack.updateEntity(updateData);
|
console.log(`Migrated ${entity} entity ${doc.name} in Compendium ${pack.collection}`);
|
||||||
console.log(`Migrated ${entity} entity ${ent.name} in Compendium ${pack.collection}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle migration failures
|
// Handle migration failures
|
||||||
catch(err) {
|
catch(err) {
|
||||||
err.message = `Failed sw5e system migration for entity ${ent.name} in pack ${pack.collection}: ${err.message}`;
|
err.message = `Failed sw5e system migration for entity ${doc.name} in pack ${pack.collection}: ${err.message}`;
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply the original locked status for the pack
|
// Apply the original locked status for the pack
|
||||||
pack.configure({locked: wasLocked});
|
await pack.configure({locked: wasLocked});
|
||||||
console.log(`Migrated all ${entity} entities from Compendium ${pack.collection}`);
|
console.log(`Migrated all ${entity} entities from Compendium ${pack.collection}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -131,15 +129,15 @@ export const migrateActorData = async function(actor) {
|
||||||
// Actor Data Updates
|
// Actor Data Updates
|
||||||
_migrateActorMovement(actor, updateData);
|
_migrateActorMovement(actor, updateData);
|
||||||
_migrateActorSenses(actor, updateData);
|
_migrateActorSenses(actor, updateData);
|
||||||
|
_migrateActorType(actor, updateData);
|
||||||
|
|
||||||
// Migrate Owned Items
|
// Migrate Owned Items
|
||||||
if ( !!actor.items ) {
|
if ( !!actor.items ) {
|
||||||
let hasItemUpdates = false;
|
|
||||||
const items = await actor.items.reduce(async (memo, i) => {
|
const items = await actor.items.reduce(async (memo, i) => {
|
||||||
const results = await memo;
|
const results = await memo;
|
||||||
|
|
||||||
// Migrate the Owned Item
|
// Migrate the Owned Item
|
||||||
let itemUpdate = await migrateActorItemData(i, actor);
|
let itemUpdate = await migrateActorItemData(i.data, actor);
|
||||||
|
|
||||||
// Prepared, Equipped, and Proficient for NPC actors
|
// Prepared, Equipped, and Proficient for NPC actors
|
||||||
if ( actor.type === "npc" ) {
|
if ( actor.type === "npc" ) {
|
||||||
|
@ -150,13 +148,15 @@ export const migrateActorData = async function(actor) {
|
||||||
|
|
||||||
// Update the Owned Item
|
// Update the Owned Item
|
||||||
if ( !isObjectEmpty(itemUpdate) ) {
|
if ( !isObjectEmpty(itemUpdate) ) {
|
||||||
hasItemUpdates = true;
|
itemUpdate._id = i.id;
|
||||||
console.log(`Migrating Actor ${actor.name}'s ${i.name}`);
|
console.log(`Migrating Actor ${actor.name}'s ${i.name}`);
|
||||||
return [...results, mergeObject(i, itemUpdate, {enforceTypes: false, inplace: false})];
|
results.push(itemUpdate)
|
||||||
} else return [...results, i];
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if ( hasItemUpdates ) updateData.items = items;
|
if ( items.length > 0 ) updateData.items = items;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update NPC data with new datamodel information
|
// Update NPC data with new datamodel information
|
||||||
|
@ -235,24 +235,20 @@ export const migrateActorItemData = async function(item, actor) {
|
||||||
* @return {Object} The updateData to apply
|
* @return {Object} The updateData to apply
|
||||||
*/
|
*/
|
||||||
export const migrateSceneData = async function(scene) {
|
export const migrateSceneData = async function(scene) {
|
||||||
const tokens = duplicate(scene.tokens);
|
const tokens = await Promise.all(scene.tokens.map(async token => {
|
||||||
return {
|
const t = token.toJSON();
|
||||||
tokens: await Promise.all(tokens.map(async (t) => {
|
|
||||||
if (!t.actorId || t.actorLink || !t.actorData.data) {
|
if (!t.actorId || t.actorLink || !t.actorData.data) {
|
||||||
t.actorData = {};
|
t.actorData = {};
|
||||||
return t;
|
|
||||||
}
|
}
|
||||||
const token = new Token(t);
|
else if (!game.actors.has(t.actorId)) {
|
||||||
if ( !token.actor ) {
|
|
||||||
t.actorId = null;
|
t.actorId = null;
|
||||||
t.actorData = {};
|
t.actorData = {};
|
||||||
} else if ( !t.actorLink ) {
|
} else if ( !t.actorLink ) {
|
||||||
const updateData = await migrateActorData(token.data.actorData);
|
t.actorData = mergeObject(token.data.actorData, await migrateActorData(t.actorData));
|
||||||
t.actorData = mergeObject(token.data.actorData, updateData);
|
|
||||||
}
|
}
|
||||||
return t;
|
return t;
|
||||||
}))
|
}));
|
||||||
};
|
return {tokens};
|
||||||
};
|
};
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -448,6 +444,84 @@ function _migrateActorSenses(actor, updateData) {
|
||||||
return updateData;
|
return updateData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrate the actor details.type string to object
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function _migrateActorType(actor, updateData) {
|
||||||
|
const ad = actor.data;
|
||||||
|
const original = ad.details?.type;
|
||||||
|
if ( (original === undefined) || (foundry.utils.getType(original) === "Object") ) return;
|
||||||
|
|
||||||
|
// New default data structure
|
||||||
|
let data = {
|
||||||
|
"value": "",
|
||||||
|
"subtype": "",
|
||||||
|
"swarm": "",
|
||||||
|
"custom": ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specifics
|
||||||
|
// (Some of these have weird names, these need to be addressed individually)
|
||||||
|
if (original === "force entity") {
|
||||||
|
data.value = "force";
|
||||||
|
data.subtype = "storm";
|
||||||
|
} else if (original === "human") {
|
||||||
|
data.value = "humanoid";
|
||||||
|
data.subtype = "human";
|
||||||
|
} else if (["humanoid (any)", "humanoid (Villainous"].includes(original)) {
|
||||||
|
data.value = "humanoid";
|
||||||
|
} else if (original === "tree") {
|
||||||
|
data.value = "plant";
|
||||||
|
data.subtype = "tree";
|
||||||
|
} else if (original === "(humanoid) or Large (beast) force entity") {
|
||||||
|
data.value = "force";
|
||||||
|
} else if (original === "droid (appears human)") {
|
||||||
|
data.value = "droid";
|
||||||
|
} else {
|
||||||
|
// Match the existing string
|
||||||
|
const pattern = /^(?:swarm of (?<size>[\w\-]+) )?(?<type>[^(]+?)(?:\((?<subtype>[^)]+)\))?$/i;
|
||||||
|
const match = original.trim().match(pattern);
|
||||||
|
if (match) {
|
||||||
|
|
||||||
|
// Match a known creature type
|
||||||
|
const typeLc = match.groups.type.trim().toLowerCase();
|
||||||
|
const typeMatch = Object.entries(CONFIG.SW5E.creatureTypes).find(([k, v]) => {
|
||||||
|
return (typeLc === k) ||
|
||||||
|
(typeLc === game.i18n.localize(v).toLowerCase()) ||
|
||||||
|
(typeLc === game.i18n.localize(`${v}Pl`).toLowerCase());
|
||||||
|
});
|
||||||
|
if (typeMatch) data.value = typeMatch[0];
|
||||||
|
else {
|
||||||
|
data.value = "custom";
|
||||||
|
data.custom = match.groups.type.trim().titleCase();
|
||||||
|
}
|
||||||
|
data.subtype = match.groups.subtype?.trim().titleCase() || "";
|
||||||
|
|
||||||
|
// Match a swarm
|
||||||
|
const isNamedSwarm = actor.name.startsWith(game.i18n.localize("SW5E.CreatureSwarm"));
|
||||||
|
if (match.groups.size || isNamedSwarm) {
|
||||||
|
const sizeLc = match.groups.size ? match.groups.size.trim().toLowerCase() : "tiny";
|
||||||
|
const sizeMatch = Object.entries(CONFIG.SW5E.actorSizes).find(([k, v]) => {
|
||||||
|
return (sizeLc === k) || (sizeLc === game.i18n.localize(v).toLowerCase());
|
||||||
|
});
|
||||||
|
data.swarm = sizeMatch ? sizeMatch[0] : "tiny";
|
||||||
|
} else data.swarm = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// No match found
|
||||||
|
else {
|
||||||
|
data.value = "custom";
|
||||||
|
data.custom = original;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the actor data
|
||||||
|
updateData["data.details.type"] = data;
|
||||||
|
console.log(data);
|
||||||
|
return updateData;
|
||||||
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -457,19 +531,35 @@ function _migrateItemClassPowerCasting(item, updateData) {
|
||||||
if (item.type === "class"){
|
if (item.type === "class"){
|
||||||
switch (item.name){
|
switch (item.name){
|
||||||
case "Consular":
|
case "Consular":
|
||||||
updateData["data.powercasting"] = "consular";
|
updateData["data.powercasting"] = {
|
||||||
|
progression: "consular",
|
||||||
|
ability: ""
|
||||||
|
};
|
||||||
break;
|
break;
|
||||||
case "Engineer":
|
case "Engineer":
|
||||||
updateData["data.powercasting"] = "engineer";
|
|
||||||
|
updateData["data.powercasting"] = {
|
||||||
|
progression: "engineer",
|
||||||
|
ability: ""
|
||||||
|
};
|
||||||
break;
|
break;
|
||||||
case "Guardian":
|
case "Guardian":
|
||||||
updateData["data.powercasting"] = "guardian";
|
updateData["data.powercasting"] = {
|
||||||
|
progression: "guardian",
|
||||||
|
ability: ""
|
||||||
|
};
|
||||||
break;
|
break;
|
||||||
case "Scout":
|
case "Scout":
|
||||||
updateData["data.powercasting"] = "scout";
|
updateData["data.powercasting"] = {
|
||||||
|
progression: "scout",
|
||||||
|
ability: ""
|
||||||
|
};
|
||||||
break;
|
break;
|
||||||
case "Sentinel":
|
case "Sentinel":
|
||||||
updateData["data.powercasting"] = "sentinel";
|
updateData["data.powercasting"] = {
|
||||||
|
progression: "sentinel",
|
||||||
|
ability: ""
|
||||||
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -527,10 +617,14 @@ async function _migrateItemPower(item, actor, updateData) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete the old data.attuned boolean
|
* Delete the old data.attuned boolean
|
||||||
|
*
|
||||||
|
* @param {object} item Item data to migrate
|
||||||
|
* @param {object} updateData Existing update to expand upon
|
||||||
|
* @return {object} The updateData to apply
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
function _migrateItemAttunement(item, updateData) {
|
function _migrateItemAttunement(item, updateData) {
|
||||||
if ( item.data.attuned === undefined ) return;
|
if ( item.data.attuned === undefined ) return updateData;
|
||||||
updateData["data.attunement"] = CONFIG.SW5E.attunementTypes.NONE;
|
updateData["data.attunement"] = CONFIG.SW5E.attunementTypes.NONE;
|
||||||
updateData["data.-=attuned"] = null;
|
updateData["data.-=attuned"] = null;
|
||||||
return updateData;
|
return updateData;
|
||||||
|
|
2
sw5e.js
2
sw5e.js
|
@ -219,7 +219,7 @@ Hooks.once("ready", function() {
|
||||||
// Determine whether a system migration is required and feasible
|
// Determine whether a system migration is required and feasible
|
||||||
if ( !game.user.isGM ) return;
|
if ( !game.user.isGM ) return;
|
||||||
const currentVersion = game.settings.get("sw5e", "systemMigrationVersion");
|
const currentVersion = game.settings.get("sw5e", "systemMigrationVersion");
|
||||||
const NEEDS_MIGRATION_VERSION = "1.2.4.R1-A5";
|
const NEEDS_MIGRATION_VERSION = "1.3.2.R1-A5";
|
||||||
// Check for R1 SW5E versions
|
// Check for R1 SW5E versions
|
||||||
const SW5E_NEEDS_MIGRATION_VERSION = "R1-A5";
|
const SW5E_NEEDS_MIGRATION_VERSION = "R1-A5";
|
||||||
const COMPATIBLE_MIGRATION_VERSION = 0.80;
|
const COMPATIBLE_MIGRATION_VERSION = 0.80;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"name": "sw5e",
|
"name": "sw5e",
|
||||||
"title": "SW 5th Edition",
|
"title": "SW 5th Edition",
|
||||||
"description": "A comprehensive game system for running games of SW 5th Edition in the Foundry VTT environment.",
|
"description": "A comprehensive game system for running games of SW 5th Edition in the Foundry VTT environment.",
|
||||||
"version": "1.3.2",
|
"version": "1.3.2.R1-A6",
|
||||||
"author": "Dev Team",
|
"author": "Dev Team",
|
||||||
"scripts": [],
|
"scripts": [],
|
||||||
"esmodules": ["sw5e.js"],
|
"esmodules": ["sw5e.js"],
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue