Created
April 28, 2022 14:02
-
-
Save akrigline/170159bf376969b69c25a62f97398982 to your computer and use it in GitHub Desktop.
1.6.0 migration macros
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// the compendium id which should have the subclasses added to it | |
const PACK_NAME = 'world.custom-subclasses'; | |
// the core system includes subclasses without features defined, should these be created as subclasses? | |
const INCLUDE_EMPTY_SUBCLASSES = false; | |
/* Create Subclass Items that are missing (based on name matching) in the Compendium per legacy CONFIG.DND5E.classFeatures */ | |
/* v0.1 */ | |
const pack = game.packs.get(PACK_NAME); | |
if (!pack) { | |
ui.notifications.error('No pack by that name'); | |
return; | |
}; | |
const itemData = Object.entries(CONFIG.DND5E.classFeatures).map(([classIdentifier, { subclasses }]) => { | |
const subclassItemData = Object.entries(subclasses).map(([subclassIdentifier, { label, features, source }]) => { | |
if (!INCLUDE_EMPTY_SUBCLASSES && (!features || foundry.utils.isObjectEmpty(features))) return; | |
const advancements = (!features || foundry.utils.isObjectEmpty(features)) ? [] : Object.entries(features).map(([level, items]) => { | |
return { | |
classRestriction: "", | |
level, | |
icon: "", | |
type: 'ItemGrant', | |
title: "Features", | |
configuration: { items }, | |
value: {}, | |
_id: foundry.utils.randomID(), | |
} | |
}); | |
return { | |
data: { | |
advancement: advancements, | |
identifier: subclassIdentifier, | |
classIdentifier, | |
source, | |
}, | |
name: label, | |
type: 'subclass' | |
} | |
}); | |
return subclassItemData; | |
}).flat().filter(Boolean).filter(({name}) => !pack.index.getName(name)); | |
console.log("Creating Items:", itemData); | |
await Item.implementation.create(itemData, { pack: PACK_NAME }); | |
pack.render(true); | |
ui.notifications.notify('Complete'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const ACTOR_NAME = "Peter"; | |
/** | |
* Attempts to migrate the given actor to use the advancements on their classes as defined in the compendium source for those classes | |
* v0.2 | |
*/ | |
/** | |
* Gets all actor changes related to the given actor and class item | |
* @returns {Promise<object[]>} array of objects representing Item updates | |
*/ | |
const migrateClass = async (ogActor, ogClass) => { | |
if (!ogClass.getFlag('core', 'sourceId')) { | |
ui.notifications.warn(`${ogClass.name} does not appear to have a source item`); | |
return []; | |
} | |
const original = await fromUuid(ogClass.getFlag('core', 'sourceId')); | |
if (!original) { | |
ui.notifications.warn(`No original class found for ${ogClass.name}, was it deleted?`); | |
return []; | |
} | |
const advancements = original.toJSON().data.advancement | |
.filter(({type}) => type !== 'HitPoints'); | |
const actorLevels = ogClass.data.data.levels; | |
// output of this function: all items that should be changed to migrate this class | |
let changedItems = []; | |
const changedAdvancements = advancements.map((advancement) => { | |
// return unchanged if not an itemGrant advancement | |
if (advancement.type !== 'ItemGrant') return advancement; | |
if (advancement.level <= actorLevels) { | |
// allow skipping these because we have already leveled up this actor presumably | |
advancement.configuration.optional = true; | |
} | |
advancement.configuration.items.forEach((uuid) => { | |
const relevantIndex = game.dnd5e.utils.indexFromUuid(uuid); | |
// search the actor's items for one with the sourceId equal to the advancement itemgrant item's uuid | |
// OR fall back to name matching if the item searched has no sourceId flags (brittle) | |
const relevantActorItem = ogActor.items.find((item) => { | |
// prefer sourceId flags if those exist | |
if (item.getFlag('core', 'sourceId') || item.getFlag('dnd5e', 'sourceId')) { | |
return item.getFlag('core', 'sourceId') === uuid || item.getFlag('dnd5e', 'sourceId') === uuid; | |
} | |
// fall back to name matching (brittle) | |
return item.name === relevantIndex.name; | |
}); | |
// if one is found, add this item to `added` and add advancementOrigin flag to the item | |
if (!!relevantActorItem) { | |
if (!advancement.value.added) advancement.value.added = {}; | |
// add the appropriate flags to the existing item as well | |
changedItems.push({ | |
_id: relevantActorItem.id, | |
flags: { dnd5e: { | |
advancementOrigin: `${ranger.id}.${advancement.id}` | |
}} | |
}); | |
advancement.value.added[relevantActorItem.id] = uuid; | |
} | |
// if one is not found, simply skip it | |
}); | |
return advancement; | |
}); | |
const changedClass = { | |
_id: ogClass.id, | |
data: { | |
advancement: changedAdvancements | |
} | |
}; | |
changedItems.push(changedClass); | |
return changedItems; | |
} | |
const ogActor = game.actors.getName(ACTOR_NAME); | |
if (!ogActor) { | |
ui.notifications.error('No actor found with that name'); | |
return; | |
} | |
let allChangedItems = []; | |
for (actorClass of ogActor.itemTypes.class) { | |
const classChangedItems = await migrateClass(ogActor, actorClass); | |
allChangedItems.push(...classChangedItems); | |
} | |
console.log('completed', ogActor.items, allChangedItems); | |
await ogActor.updateEmbeddedDocuments('Item', allChangedItems); | |
ui.notifications.notify('Complete'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const PACK_NAME = 'world.blank-class-items'; | |
/* Update All Class Items in the Compendium per legacy CONFIG.DND5E.classFeatures */ | |
/* v0.3 */ | |
const pack = game.packs.get(PACK_NAME); | |
if (!pack) { | |
ui.notifications.error('No pack by that name'); | |
return; | |
}; | |
const docs = await pack.getDocuments(); | |
for (let item of docs) { | |
const className = item.name.slugify({strict: true}); | |
const clsConfig = CONFIG.DND5E.classFeatures[className]; | |
if (!clsConfig) continue; | |
const advancements = Object.entries(clsConfig.features).map(([level, items]) => { | |
return { | |
classRestriction: "", | |
level, | |
icon: "", | |
type: 'ItemGrant', | |
title: "Features", | |
configuration: { items }, | |
value: {}, | |
_id: foundry.utils.randomID(), | |
} | |
}); | |
// add hitpoints advancement because this is a class | |
advancements.push({ | |
classRestriction: "", | |
type: "HitPoints", | |
icon: "", | |
title: "", | |
configuration: {}, | |
value: {}, | |
_id: foundry.utils.randomID(), | |
}); | |
await item.update({ | |
'data.advancement': advancements | |
}); | |
} | |
pack.render(true); | |
ui.notifications.notify('Complete'); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment