2020-06-24 14:23:55 -04:00
/ * *
* 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
* /
export const migrateWorld = async function ( ) {
2021-01-04 15:23:30 -05:00
ui . notifications . info ( ` Applying SW5e System Migration for version ${ game . system . data . version } . Please be patient and do not close your game or shut down your server. ` , { permanent : true } ) ;
2020-06-24 14:23:55 -04:00
// Migrate World Actors
2021-06-13 04:25:56 +01:00
for await ( let a of game . actors . contents ) {
2020-06-24 14:23:55 -04:00
try {
2021-03-12 16:47:24 -05:00
console . log ( ` Checking Actor entity ${ a . name } for migration needs ` ) ;
2021-03-10 13:50:17 -06:00
const updateData = await migrateActorData ( a . data ) ;
2021-06-09 02:29:03 +01:00
if ( ! foundry . utils . isObjectEmpty ( updateData ) ) {
2020-06-24 14:23:55 -04:00
console . log ( ` Migrating Actor entity ${ a . name } ` ) ;
await a . update ( updateData , { enforceTypes : false } ) ;
}
} catch ( err ) {
2020-11-12 17:30:07 -05:00
err . message = ` Failed sw5e system migration for Actor ${ a . name } : ${ err . message } ` ;
2020-06-24 14:23:55 -04:00
console . error ( err ) ;
}
}
// Migrate World Items
2021-06-13 04:25:56 +01:00
for ( let i of game . items . contents ) {
2020-06-24 14:23:55 -04:00
try {
2021-06-13 04:25:56 +01:00
const updateData = migrateItemData ( i . toObject ( ) ) ;
2021-06-09 02:29:03 +01:00
if ( ! foundry . utils . isObjectEmpty ( updateData ) ) {
2020-06-24 14:23:55 -04:00
console . log ( ` Migrating Item entity ${ i . name } ` ) ;
await i . update ( updateData , { enforceTypes : false } ) ;
}
} catch ( err ) {
2020-11-12 17:30:07 -05:00
err . message = ` Failed sw5e system migration for Item ${ i . name } : ${ err . message } ` ;
2020-06-24 14:23:55 -04:00
console . error ( err ) ;
}
}
// Migrate Actor Override Tokens
2021-06-13 04:25:56 +01:00
for ( let s of game . scenes . contents ) {
2020-06-24 14:23:55 -04:00
try {
2021-03-10 13:50:17 -06:00
const updateData = await migrateSceneData ( s . data ) ;
2021-06-09 02:29:03 +01:00
if ( ! foundry . utils . isObjectEmpty ( updateData ) ) {
2020-06-24 14:23:55 -04:00
console . log ( ` Migrating Scene entity ${ s . name } ` ) ;
await s . update ( updateData , { enforceTypes : false } ) ;
2021-06-13 04:25:56 +01:00
// If we do not do this, then synthetic token actors remain in cache
// with the un-updated actorData.
s . tokens . contents . forEach ( t => t . _actor = null ) ;
2020-06-24 14:23:55 -04:00
}
} catch ( err ) {
2020-11-12 17:30:07 -05:00
err . message = ` Failed sw5e system migration for Scene ${ s . name } : ${ err . message } ` ;
2020-06-24 14:23:55 -04:00
console . error ( err ) ;
}
}
// Migrate World Compendium Packs
2020-11-12 17:30:07 -05:00
for ( let p of game . packs ) {
if ( p . metadata . package !== "world" ) continue ;
if ( ! [ "Actor" , "Item" , "Scene" ] . includes ( p . metadata . entity ) ) continue ;
2020-06-24 14:23:55 -04:00
await migrateCompendium ( p ) ;
}
// Set the migration as complete
game . settings . set ( "sw5e" , "systemMigrationVersion" , game . system . data . version ) ;
2021-01-04 15:23:30 -05:00
ui . notifications . info ( ` SW5e System Migration to version ${ game . system . data . version } completed! ` , { permanent : true } ) ;
2020-06-24 14:23:55 -04:00
} ;
/* -------------------------------------------- */
/ * *
* Apply migration rules to all Entities within a single Compendium pack
* @ param pack
* @ return { Promise }
* /
export const migrateCompendium = async function ( pack ) {
const entity = pack . metadata . entity ;
if ( ! [ "Actor" , "Item" , "Scene" ] . includes ( entity ) ) return ;
2020-11-12 17:30:07 -05:00
// Unlock the pack for editing
const wasLocked = pack . locked ;
await pack . configure ( { locked : false } ) ;
2020-06-24 14:23:55 -04:00
// Begin by requesting server-side data model migration and get the migrated content
await pack . migrate ( ) ;
2021-06-09 02:29:03 +01:00
const documents = await pack . getDocuments ( ) ;
2020-06-24 14:23:55 -04:00
// Iterate over compendium entries - applying fine-tuned migration functions
2021-06-09 02:29:03 +01:00
for await ( let doc of documents ) {
2020-11-12 17:30:07 -05:00
let updateData = { } ;
2020-06-24 14:23:55 -04:00
try {
2020-11-12 17:30:07 -05:00
switch ( entity ) {
case "Actor" :
2021-06-09 02:29:03 +01:00
updateData = await migrateActorData ( doc . data ) ;
2020-11-12 17:30:07 -05:00
break ;
case "Item" :
2021-06-13 04:25:56 +01:00
updateData = migrateItemData ( doc . toObject ( ) ) ;
2020-11-12 17:30:07 -05:00
break ;
case "Scene" :
2021-06-09 02:29:03 +01:00
updateData = await migrateSceneData ( doc . data ) ;
2020-11-12 17:30:07 -05:00
break ;
2020-06-24 14:23:55 -04:00
}
2021-06-09 02:29:03 +01:00
if ( foundry . utils . isObjectEmpty ( updateData ) ) continue ;
2020-11-12 17:30:07 -05:00
// Save the entry, if data was changed
2021-06-09 02:29:03 +01:00
await doc . update ( updateData ) ;
console . log ( ` Migrated ${ entity } entity ${ doc . name } in Compendium ${ pack . collection } ` ) ;
2020-11-12 17:30:07 -05:00
}
// Handle migration failures
catch ( err ) {
2021-06-09 02:29:03 +01:00
err . message = ` Failed sw5e system migration for entity ${ doc . name } in pack ${ pack . collection } : ${ err . message } ` ;
2020-06-24 14:23:55 -04:00
console . error ( err ) ;
}
}
2020-11-12 17:30:07 -05:00
// Apply the original locked status for the pack
2021-06-09 02:29:03 +01:00
await pack . configure ( { locked : wasLocked } ) ;
2020-06-24 14:23:55 -04:00
console . log ( ` Migrated all ${ entity } entities from Compendium ${ pack . collection } ` ) ;
} ;
/* -------------------------------------------- */
/* Entity Type Migration Helpers */
/* -------------------------------------------- */
/ * *
* Migrate a single Actor entity to incorporate latest data model changes
* Return an Object of updateData to be applied
2021-01-21 21:46:42 -05:00
* @ param { object } actor The actor data object to update
* @ return { Object } The updateData to apply
2020-06-24 14:23:55 -04:00
* /
2021-03-10 13:50:17 -06:00
export const migrateActorData = async function ( actor ) {
2020-06-24 14:23:55 -04:00
const updateData = { } ;
// Actor Data Updates
2021-06-13 04:25:56 +01:00
if ( actor . data ) {
_migrateActorMovement ( actor , updateData ) ;
_migrateActorSenses ( actor , updateData ) ;
_migrateActorType ( actor , updateData ) ;
}
2020-06-24 14:23:55 -04:00
// Migrate Owned Items
2021-02-18 14:32:54 -05:00
if ( ! ! actor . items ) {
2021-03-12 16:47:24 -05:00
const items = await actor . items . reduce ( async ( memo , i ) => {
const results = await memo ;
2021-02-18 14:32:54 -05:00
// Migrate the Owned Item
2021-06-23 02:53:39 -04:00
const itemData = i instanceof CONFIG . Item . documentClass ? i . toObject ( ) : i ;
2021-06-13 04:25:56 +01:00
let itemUpdate = await migrateActorItemData ( itemData , actor ) ;
2021-02-18 14:32:54 -05:00
// Prepared, Equipped, and Proficient for NPC actors
if ( actor . type === "npc" ) {
2021-06-13 04:25:56 +01:00
if ( getProperty ( itemData . data , "preparation.prepared" ) === false ) itemUpdate [ "data.preparation.prepared" ] = true ;
if ( getProperty ( itemData . data , "equipped" ) === false ) itemUpdate [ "data.equipped" ] = true ;
if ( getProperty ( itemData . data , "proficient" ) === false ) itemUpdate [ "data.proficient" ] = true ;
2021-02-18 14:32:54 -05:00
}
2020-06-24 14:23:55 -04:00
2021-02-18 14:32:54 -05:00
// Update the Owned Item
if ( ! isObjectEmpty ( itemUpdate ) ) {
2021-06-23 02:53:39 -04:00
itemUpdate . _id = itemData . _id ;
2021-03-12 16:47:24 -05:00
console . log ( ` Migrating Actor ${ actor . name } 's ${ i . name } ` ) ;
2021-06-13 04:25:56 +01:00
results . push ( expandObject ( itemUpdate ) ) ;
2021-06-09 02:29:03 +01:00
}
return results ;
2021-03-12 16:47:24 -05:00
} , [ ] ) ;
2021-06-09 02:29:03 +01:00
if ( items . length > 0 ) updateData . items = items ;
2021-02-18 14:32:54 -05:00
}
2021-02-16 02:15:39 -05:00
2021-02-22 16:43:41 -05:00
// Update NPC data with new datamodel information
if ( actor . type === "npc" ) {
2021-02-23 01:17:30 -05:00
_updateNPCData ( actor ) ;
2021-02-22 16:43:41 -05:00
}
2021-02-16 02:15:39 -05:00
// migrate powers last since it relies on item classes being migrated first.
_migrateActorPowers ( actor , updateData ) ;
2020-06-24 14:23:55 -04:00
return updateData ;
} ;
2020-10-06 00:45:33 -04:00
/* -------------------------------------------- */
/ * *
* Scrub an Actor ' s system data , removing all keys which are not explicitly defined in the system template
* @ param { Object } actorData The data object for an Actor
* @ return { Object } The scrubbed Actor data
* /
function cleanActorData ( actorData ) {
// Scrub system data
const model = game . system . model . Actor [ actorData . type ] ;
actorData . data = filterObject ( actorData . data , model ) ;
// Scrub system flags
const allowedFlags = CONFIG . SW5E . allowedActorFlags . reduce ( ( obj , f ) => {
obj [ f ] = null ;
return obj ;
} , { } ) ;
if ( actorData . flags . sw5e ) {
actorData . flags . sw5e = filterObject ( actorData . flags . sw5e , allowedFlags ) ;
}
// Return the scrubbed data
return actorData ;
}
2020-06-24 14:23:55 -04:00
/* -------------------------------------------- */
/ * *
* Migrate a single Item entity to incorporate latest data model changes
2021-06-23 02:53:39 -04:00
*
* @ param { object } item Item data to migrate
* @ return { object } The updateData to apply
2020-06-24 14:23:55 -04:00
* /
2021-03-06 06:51:50 -05:00
export const migrateItemData = function ( item ) {
2020-06-24 14:23:55 -04:00
const updateData = { } ;
2021-03-03 00:58:56 -05:00
_migrateItemClassPowerCasting ( item , updateData ) ;
2021-01-04 15:23:30 -05:00
_migrateItemAttunement ( item , updateData ) ;
2021-06-23 02:53:39 -04:00
_migrateItemPowercasting ( item , updateData ) ;
2021-03-06 06:51:50 -05:00
return updateData ;
} ;
/* -------------------------------------------- */
/ * *
* Migrate a single owned actor Item entity to incorporate latest data model changes
* @ param item
* @ param actor
* /
2021-03-10 13:50:17 -06:00
export const migrateActorItemData = async function ( item , actor ) {
2021-03-06 06:51:50 -05:00
const updateData = { } ;
_migrateItemClassPowerCasting ( item , updateData ) ;
_migrateItemAttunement ( item , updateData ) ;
2021-06-23 02:53:39 -04:00
_migrateItemPowercasting ( item , updateData ) ;
2021-03-10 13:50:17 -06:00
await _migrateItemPower ( item , actor , updateData ) ;
2020-06-24 14:23:55 -04:00
return updateData ;
} ;
/* -------------------------------------------- */
/ * *
* Migrate a single Scene entity to incorporate changes to the data model of it ' s actor data overrides
* Return an Object of updateData to be applied
* @ param { Object } scene The Scene data to Update
* @ return { Object } The updateData to apply
* /
2021-03-12 16:47:24 -05:00
export const migrateSceneData = async function ( scene ) {
2021-06-09 02:29:03 +01:00
const tokens = await Promise . all ( scene . tokens . map ( async token => {
const t = token . toJSON ( ) ;
2021-06-13 04:25:56 +01:00
if ( ! t . actorId || t . actorLink ) {
2020-06-24 14:23:55 -04:00
t . actorData = { } ;
}
2021-06-09 02:29:03 +01:00
else if ( ! game . actors . has ( t . actorId ) ) {
2020-06-24 14:23:55 -04:00
t . actorId = null ;
t . actorData = { } ;
} else if ( ! t . actorLink ) {
2021-06-13 04:25:56 +01:00
const actorData = duplicate ( t . actorData ) ;
actorData . type = token . actor ? . type ;
const update = migrateActorData ( actorData ) ;
[ 'items' , 'effects' ] . forEach ( embeddedName => {
if ( ! update [ embeddedName ] ? . length ) return ;
const updates = new Map ( update [ embeddedName ] . map ( u => [ u . _id , u ] ) ) ;
t . actorData [ embeddedName ] . forEach ( original => {
const update = updates . get ( original . _id ) ;
if ( update ) mergeObject ( original , update ) ;
} ) ;
delete update [ embeddedName ] ;
} ) ;
mergeObject ( t . actorData , update ) ;
2020-06-24 14:23:55 -04:00
}
return t ;
2021-06-09 02:29:03 +01:00
} ) ) ;
return { tokens } ;
2020-06-24 14:23:55 -04:00
} ;
/* -------------------------------------------- */
/ * L o w l e v e l m i g r a t i o n u t i l i t i e s
/* -------------------------------------------- */
2021-02-22 16:43:41 -05:00
/* -------------------------------------------- */
/ * *
* Update an NPC Actor ' s data based on compendium
* @ param { Object } actor The data object for an Actor
* @ return { Object } The updated Actor
* /
function _updateNPCData ( actor ) {
2021-02-23 01:17:30 -05:00
let actorData = actor . data ;
2021-02-22 16:43:41 -05:00
const updateData = { } ;
2021-03-03 00:58:56 -05:00
// check for flag.core, if not there is no compendium monster so exit
2021-02-22 16:43:41 -05:00
const hasSource = actor ? . flags ? . core ? . sourceId !== undefined ;
if ( ! hasSource ) return actor ;
2021-03-11 01:30:03 -05:00
// shortcut out if dataVersion flag is set to 1.2.4 or higher
2021-03-09 23:37:20 -05:00
const hasDataVersion = actor ? . flags ? . sw5e ? . dataVersion !== undefined ;
2021-03-11 01:30:03 -05:00
if ( hasDataVersion && ( actor . flags . sw5e . dataVersion === "1.2.4" || isNewerVersion ( "1.2.4" , actor . flags . sw5e . dataVersion ) ) ) return actor ;
2021-03-03 00:58:56 -05:00
// Check to see what the source of NPC is
2021-02-23 01:17:30 -05:00
const sourceId = actor . flags . core . sourceId ;
const coreSource = sourceId . substr ( 0 , sourceId . length - 17 ) ;
const core _id = sourceId . substr ( sourceId . length - 16 , 16 ) ;
2021-02-22 16:43:41 -05:00
if ( coreSource === "Compendium.sw5e.monsters" ) {
2021-02-23 01:17:30 -05:00
game . packs . get ( "sw5e.monsters" ) . getEntity ( core _id ) . then ( monster => {
const monsterData = monster . data . data ;
// copy movement[], senses[], powercasting, force[], tech[], powerForceLevel, powerTechLevel
updateData [ "data.attributes.movement" ] = monsterData . attributes . movement ;
updateData [ "data.attributes.senses" ] = monsterData . attributes . senses ;
updateData [ "data.attributes.powercasting" ] = monsterData . attributes . powercasting ;
updateData [ "data.attributes.force" ] = monsterData . attributes . force ;
updateData [ "data.attributes.tech" ] = monsterData . attributes . tech ;
updateData [ "data.details.powerForceLevel" ] = monsterData . details . powerForceLevel ;
updateData [ "data.details.powerTechLevel" ] = monsterData . details . powerTechLevel ;
// push missing powers onto actor
2021-02-23 07:00:43 -05:00
let newPowers = [ ] ;
2021-02-23 01:17:30 -05:00
for ( let i of monster . items ) {
const itemData = i . data ;
if ( itemData . type === "power" ) {
2021-02-23 07:00:43 -05:00
const itemCompendium _id = itemData . flags ? . core ? . sourceId . split ( "." ) . slice ( - 1 ) [ 0 ] ;
let hasPower = ! ! actor . items . find ( item => item . flags ? . core ? . sourceId . split ( "." ) . slice ( - 1 ) [ 0 ] === itemCompendium _id ) ;
2021-02-23 01:17:30 -05:00
if ( ! hasPower ) {
2021-02-23 17:49:07 -06:00
// Clone power to new object. Don't know if it is technically needed, but seems to prevent some weirdness.
const newPower = JSON . parse ( JSON . stringify ( itemData ) ) ;
newPowers . push ( newPower ) ;
2021-02-23 01:17:30 -05:00
}
}
}
2021-02-23 17:49:07 -06:00
2021-03-11 01:30:03 -05:00
// get actor to create new powers
2021-02-23 17:49:07 -06:00
const liveActor = game . actors . get ( actor . _id ) ;
2021-03-11 01:30:03 -05:00
// create the powers on the actor
2021-02-23 17:49:07 -06:00
liveActor . createEmbeddedEntity ( "OwnedItem" , newPowers ) ;
2021-02-23 01:17:30 -05:00
// set flag to check to see if migration has been done so we don't do it again.
2021-03-10 13:50:17 -06:00
liveActor . setFlag ( "sw5e" , "dataVersion" , "1.2.4" ) ;
2021-02-23 01:17:30 -05:00
} )
2021-02-22 16:43:41 -05:00
}
//merge object
actorData = mergeObject ( actorData , updateData ) ;
// Return the scrubbed data
return actor ;
}
2020-06-24 14:23:55 -04:00
/ * *
2021-01-04 15:23:30 -05:00
* Migrate the actor speed string to movement object
2020-06-24 14:23:55 -04:00
* @ private
* /
2021-01-21 21:46:42 -05:00
function _migrateActorMovement ( actorData , updateData ) {
const ad = actorData . data ;
// Work is needed if old data is present
const old = actorData . type === 'vehicle' ? ad ? . attributes ? . speed : ad ? . attributes ? . speed ? . value ;
const hasOld = old !== undefined ;
if ( hasOld ) {
// If new data is not present, migrate the old data
const hasNew = ad ? . attributes ? . movement ? . walk !== undefined ;
if ( ! hasNew && ( typeof old === "string" ) ) {
const s = ( old || "" ) . split ( " " ) ;
if ( s . length > 0 ) updateData [ "data.attributes.movement.walk" ] = Number . isNumeric ( s [ 0 ] ) ? parseInt ( s [ 0 ] ) : null ;
}
// Remove the old attribute
updateData [ "data.attributes.-=speed" ] = null ;
}
2021-01-04 15:23:30 -05:00
return updateData
2020-11-12 17:30:07 -05:00
}
2020-10-08 02:20:12 -04:00
2021-01-04 15:23:30 -05:00
/* -------------------------------------------- */
2021-02-02 21:42:15 -05:00
/ * *
* Migrate the actor speed string to movement object
* @ private
* /
function _migrateActorPowers ( actorData , updateData ) {
const ad = actorData . data ;
// If new Force & Tech data is not present, create it
2021-02-11 07:04:13 -05:00
let hasNewAttrib = ad ? . attributes ? . force ? . level !== undefined ;
2021-02-02 21:42:15 -05:00
if ( ! hasNewAttrib ) {
updateData [ "data.attributes.force.known.value" ] = 0 ;
updateData [ "data.attributes.force.known.max" ] = 0 ;
updateData [ "data.attributes.force.points.value" ] = 0 ;
updateData [ "data.attributes.force.points.min" ] = 0 ;
updateData [ "data.attributes.force.points.max" ] = 0 ;
2021-03-16 12:24:29 -04:00
updateData [ "data.attributes.force.points.temp" ] = null ;
updateData [ "data.attributes.force.points.tempmax" ] = null ;
2021-02-02 21:42:15 -05:00
updateData [ "data.attributes.force.level" ] = 0 ;
updateData [ "data.attributes.tech.known.value" ] = 0 ;
updateData [ "data.attributes.tech.known.max" ] = 0 ;
updateData [ "data.attributes.tech.points.value" ] = 0 ;
updateData [ "data.attributes.tech.points.min" ] = 0 ;
updateData [ "data.attributes.tech.points.max" ] = 0 ;
2021-03-16 12:24:29 -04:00
updateData [ "data.attributes.tech.points.temp" ] = null ;
updateData [ "data.attributes.tech.points.tempmax" ] = null ;
2021-02-02 21:42:15 -05:00
updateData [ "data.attributes.tech.level" ] = 0 ;
}
2021-02-10 15:41:02 -05:00
// If new Power F/T split data is not present, create it
2021-02-16 02:15:39 -05:00
const hasNewLimit = ad ? . powers ? . power1 ? . foverride !== undefined ;
2021-02-11 09:47:46 -05:00
if ( ! hasNewLimit ) {
2021-02-10 15:41:02 -05:00
for ( let i = 1 ; i <= 9 ; i ++ ) {
2021-02-16 02:15:39 -05:00
// add new
2021-02-22 16:43:41 -05:00
updateData [ "data.powers.power" + i + ".fvalue" ] = getProperty ( ad . powers , "power" + i + ".value" ) ;
updateData [ "data.powers.power" + i + ".fmax" ] = getProperty ( ad . powers , "power" + i + ".max" ) ;
2021-02-10 15:41:02 -05:00
updateData [ "data.powers.power" + i + ".foverride" ] = null ;
2021-02-22 16:43:41 -05:00
updateData [ "data.powers.power" + i + ".tvalue" ] = getProperty ( ad . powers , "power" + i + ".value" ) ;
updateData [ "data.powers.power" + i + ".tmax" ] = getProperty ( ad . powers , "power" + i + ".max" ) ;
2021-02-10 15:41:02 -05:00
updateData [ "data.powers.power" + i + ".toverride" ] = null ;
//remove old
updateData [ "data.powers.power" + i + ".-=value" ] = null ;
updateData [ "data.powers.power" + i + ".-=override" ] = null ;
}
}
2021-02-02 21:42:15 -05:00
// If new Bonus Power DC data is not present, create it
const hasNewBonus = ad ? . bonuses ? . power ? . forceLightDC !== undefined ;
if ( ! hasNewBonus ) {
updateData [ "data.bonuses.power.forceLightDC" ] = "" ;
updateData [ "data.bonuses.power.forceDarkDC" ] = "" ;
updateData [ "data.bonuses.power.forceUnivDC" ] = "" ;
2021-02-09 23:47:32 -05:00
updateData [ "data.bonuses.power.techDC" ] = "" ;
2021-02-02 21:42:15 -05:00
}
// Remove the Power DC Bonus
updateData [ "data.bonuses.power.-=dc" ] = null ;
return updateData
}
/* -------------------------------------------- */
2021-01-04 15:23:30 -05:00
/ * *
* Migrate the actor traits . senses string to attributes . senses object
* @ private
* /
function _migrateActorSenses ( actor , updateData ) {
const ad = actor . data ;
if ( ad ? . traits ? . senses === undefined ) return ;
const original = ad . traits . senses || "" ;
2021-06-13 04:25:56 +01:00
if ( typeof original !== "string" ) return ;
2021-01-04 15:23:30 -05:00
// Try to match old senses with the format like "Darkvision 60 ft, Blindsight 30 ft"
2021-01-21 21:46:42 -05:00
const pattern = /([A-z]+)\s?([0-9]+)\s?([A-z]+)?/ ;
2021-01-04 15:23:30 -05:00
let wasMatched = false ;
// Match each comma-separated term
for ( let s of original . split ( "," ) ) {
s = s . trim ( ) ;
const match = s . match ( pattern ) ;
if ( ! match ) continue ;
const type = match [ 1 ] . toLowerCase ( ) ;
if ( type in CONFIG . SW5E . senses ) {
updateData [ ` data.attributes.senses. ${ type } ` ] = Number ( match [ 2 ] ) . toNearest ( 0.5 ) ;
wasMatched = true ;
}
}
// If nothing was matched, but there was an old string - put the whole thing in "special"
if ( ! wasMatched && ! ! original ) {
updateData [ "data.attributes.senses.special" ] = original ;
}
// Remove the old traits.senses string once the migration is complete
updateData [ "data.traits.-=senses" ] = null ;
return updateData ;
}
2020-10-08 02:20:12 -04:00
2021-06-23 02:53:39 -04:00
/* -------------------------------------------- */
2021-06-09 02:29:03 +01:00
/ * *
* Migrate the actor details . type string to object
* @ private
* /
function _migrateActorType ( actor , updateData ) {
const ad = actor . data ;
const original = ad . details ? . type ;
2021-06-13 04:25:56 +01:00
if ( typeof original !== "string" ) return ;
2021-06-09 02:29:03 +01:00
// 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 ;
return updateData ;
}
2021-02-09 02:14:10 -05:00
/* -------------------------------------------- */
/ * *
* @ private
* /
function _migrateItemClassPowerCasting ( item , updateData ) {
if ( item . type === "class" ) {
switch ( item . name ) {
case "Consular" :
2021-06-09 02:29:03 +01:00
updateData [ "data.powercasting" ] = {
progression : "consular" ,
ability : ""
} ;
2021-02-09 02:14:10 -05:00
break ;
case "Engineer" :
2021-06-09 02:29:03 +01:00
updateData [ "data.powercasting" ] = {
progression : "engineer" ,
ability : ""
} ;
2021-02-09 02:14:10 -05:00
break ;
case "Guardian" :
2021-06-09 02:29:03 +01:00
updateData [ "data.powercasting" ] = {
progression : "guardian" ,
ability : ""
} ;
2021-02-09 02:14:10 -05:00
break ;
case "Scout" :
2021-06-09 02:29:03 +01:00
updateData [ "data.powercasting" ] = {
progression : "scout" ,
ability : ""
} ;
2021-02-09 02:14:10 -05:00
break ;
case "Sentinel" :
2021-06-09 02:29:03 +01:00
updateData [ "data.powercasting" ] = {
progression : "sentinel" ,
ability : ""
} ;
2021-02-09 02:14:10 -05:00
break ;
}
}
return updateData ;
}
2021-03-03 00:58:56 -05:00
/* -------------------------------------------- */
/ * *
2021-03-09 23:37:20 -05:00
* Update an Power Item ' s data based on compendium
* @ param { Object } item The data object for an item
* @ param { Object } actor The data object for the actor owning the item
2021-03-03 00:58:56 -05:00
* @ private
* /
2021-03-10 13:50:17 -06:00
async function _migrateItemPower ( item , actor , updateData ) {
2021-03-09 23:37:20 -05:00
// if item is not a power shortcut out
if ( item . type !== "power" ) return updateData ;
2021-03-11 01:30:03 -05:00
2021-03-12 16:47:24 -05:00
console . log ( ` Checking Actor ${ actor . name } 's ${ item . name } for migration needs ` ) ;
2021-03-09 23:37:20 -05:00
// check for flag.core, if not there is no compendium power so exit
const hasSource = item ? . flags ? . core ? . sourceId !== undefined ;
if ( ! hasSource ) return updateData ;
2021-03-11 01:30:03 -05:00
// shortcut out if dataVersion flag is set to 1.2.4 or higher
2021-03-09 23:37:20 -05:00
const hasDataVersion = item ? . flags ? . sw5e ? . dataVersion !== undefined ;
2021-03-11 01:30:03 -05:00
if ( hasDataVersion && ( item . flags . sw5e . dataVersion === "1.2.4" || isNewerVersion ( "1.2.4" , item . flags . sw5e . dataVersion ) ) ) return updateData ;
2021-03-09 23:37:20 -05:00
// Check to see what the source of Power is
const sourceId = item . flags . core . sourceId ;
2021-03-10 13:50:17 -06:00
const coreSource = sourceId . substr ( 0 , sourceId . length - 17 ) ;
const core _id = sourceId . substr ( sourceId . length - 16 , 16 ) ;
2021-03-11 01:30:03 -05:00
//if power type is not force or tech exit out
let powerType = "none" ;
if ( coreSource === "Compendium.sw5e.forcepowers" ) powerType = "sw5e.forcepowers" ;
if ( coreSource === "Compendium.sw5e.techpowers" ) powerType = "sw5e.techpowers" ;
if ( powerType === "none" ) return updateData ;
2021-03-12 16:47:24 -05:00
const corePower = duplicate ( await game . packs . get ( powerType ) . getEntity ( core _id ) ) ;
console . log ( ` Updating Actor ${ actor . name } 's ${ item . name } from compendium ` ) ;
const corePowerData = corePower . data ;
2021-03-10 13:50:17 -06:00
// copy Core Power Data over original Power
2021-03-11 01:30:03 -05:00
updateData [ "data" ] = corePowerData ;
2021-03-10 13:50:17 -06:00
updateData [ "flags" ] = { "sw5e" : { "dataVersion" : "1.2.4" } } ;
return updateData ;
2021-03-12 16:47:24 -05:00
//game.packs.get(powerType).getEntity(core_id).then(corePower => {
//})
2021-03-09 23:37:20 -05:00
}
/* -------------------------------------------- */
2021-01-04 15:23:30 -05:00
/ * *
* Delete the old data . attuned boolean
2021-06-09 02:29:03 +01:00
*
* @ param { object } item Item data to migrate
* @ param { object } updateData Existing update to expand upon
* @ return { object } The updateData to apply
2021-01-04 15:23:30 -05:00
* @ private
* /
function _migrateItemAttunement ( item , updateData ) {
2021-06-23 02:53:39 -04:00
if ( item . data ? . attuned === undefined ) return updateData ;
2021-01-18 23:49:04 -05:00
updateData [ "data.attunement" ] = CONFIG . SW5E . attunementTypes . NONE ;
2021-01-04 15:23:30 -05:00
updateData [ "data.-=attuned" ] = null ;
return updateData ;
}
2021-06-23 02:53:39 -04:00
/* -------------------------------------------- */
/ * *
* Replace class powercasting string to object .
*
* @ param { object } item Item data to migrate
* @ param { object } updateData Existing update to expand upon
* @ return { object } The updateData to apply
* @ private
* /
function _migrateItemPowercasting ( item , updateData ) {
if ( item . type !== "class" || ( foundry . utils . getType ( item . data . powercasting ) === "Object" ) ) return updateData ;
updateData [ "data.powercasting" ] = {
progression : item . data . powercasting ,
ability : ""
} ;
return updateData ;
}
2021-01-04 15:23:30 -05:00
/* -------------------------------------------- */
2020-10-08 02:20:12 -04:00
/ * *
* A general tool to purge flags from all entities in a Compendium pack .
* @ param { Compendium } pack The compendium pack to clean
* @ private
* /
export async function purgeFlags ( pack ) {
const cleanFlags = ( flags ) => {
const flags5e = flags . sw5e || null ;
return flags5e ? { sw5e : flags5e } : { } ;
} ;
await pack . configure ( { locked : false } ) ;
const content = await pack . getContent ( ) ;
for ( let entity of content ) {
const update = { _id : entity . id , flags : cleanFlags ( entity . data . flags ) } ;
if ( pack . entity === "Actor" ) {
update . items = entity . data . items . map ( i => {
i . flags = cleanFlags ( i . flags ) ;
return i ;
} )
}
await pack . updateEntity ( update , { recursive : false } ) ;
console . log ( ` Purged flags from ${ entity . name } ` ) ;
}
await pack . configure ( { locked : true } ) ;
}
2020-11-12 17:30:07 -05:00
/* -------------------------------------------- */
/ * *
* Purge the data model of any inner objects which have been flagged as _deprecated .
* @ param { object } data The data to clean
* @ private
* /
export function removeDeprecatedObjects ( data ) {
for ( let [ k , v ] of Object . entries ( data ) ) {
if ( getType ( v ) === "Object" ) {
if ( v . _deprecated === true ) {
console . log ( ` Deleting deprecated object key ${ k } ` ) ;
delete data [ k ] ;
}
else removeDeprecatedObjects ( v ) ;
}
}
return data ;
}