item to actors script

I have this macro for updating actors in a compendium with spells from a compendium of spells (i.e. replacing by name any spells on the actors, so I can push my improvements to spell automation onto the monsters in my compendium). It works, but I'm wondering if I can make it more efficient - currently, it's doing an individual update per actor, and that feels like it should be possible to batch into a single update (which would also allow me to construct the update list by getting an extended index with the items for the monsters, and not have to getDocument on each monster). There's definitely a bunch of smaller improvements that can be made as well - this is slightly sketchy code and I'm out of practice.
let pack = game.packs.get("DDBImports.spells")
let index = pack.index
let monsterpack = game.packs.get("DDBImports.monsters")
//await monsterpack.getIndex({fields:["items"]})
let monsterindex = Array.from(monsterpack.index)
for (let monster of monsterindex){
console.log(monster.name)
let act = await monsterpack.getDocument(monster._id)
let items = Array.from(act.items)
let updates = []
for (let item of items){
if(item.type !== "spell") continue //only replacing spells
let newitem = await pack.getDocument(index.find(i => i.name === item.name)?._id) //find a spell with the same name
if(!newitem) continue //if none, then skip to next
let update = duplicate(newitem.data)
update.data.preparation = item.data.data.preparation;
update.data.uses = item.data.data.uses
update.data.consume = item.data.data.consume
update._id = item.data._id
updates.push(update)
}
if(!updates) continue
console.log(updates)
await act.updateEmbeddedDocuments("Item", updates)
}
let pack = game.packs.get("DDBImports.spells")
let index = pack.index
let monsterpack = game.packs.get("DDBImports.monsters")
//await monsterpack.getIndex({fields:["items"]})
let monsterindex = Array.from(monsterpack.index)
for (let monster of monsterindex){
console.log(monster.name)
let act = await monsterpack.getDocument(monster._id)
let items = Array.from(act.items)
let updates = []
for (let item of items){
if(item.type !== "spell") continue //only replacing spells
let newitem = await pack.getDocument(index.find(i => i.name === item.name)?._id) //find a spell with the same name
if(!newitem) continue //if none, then skip to next
let update = duplicate(newitem.data)
update.data.preparation = item.data.data.preparation;
update.data.uses = item.data.data.uses
update.data.consume = item.data.data.consume
update._id = item.data._id
updates.push(update)
}
if(!updates) continue
console.log(updates)
await act.updateEmbeddedDocuments("Item", updates)
}
C
Calego•924d ago
this is an interesting problem if you have the inclination, playing around and trying to get updateAll to work would be a solution. https://foundryvtt.com/api/CompendiumCollection.html#updateAll But I suspect it's an "accidentally" inherited method that won't work with compendiums at all. I think the getIndex might be the best answer to this problem. but I do not know how well (if at all) one can index items on actors in a compendium like ideally you'd be able to index in such a way that only figures out if the one item needed exists Hmmmm updating all spells in the compendium at once prevents that too getDocuments with an nedb query feels like the best solution here, but it's going to be a gnarly query you'll need to construct based on the spell pack index
const docsToUpdate = await game.packs.get("DDBImports.monsters").getDocuments({
items: {$elemMatch: { type: 'spell' }}
});

const documentUpdates = docsToUpdate.map((actor) => {
// some Promise.all shennanegins will be needed in here somewhere
const itemsToUpdate = actor.items
.filter(({type}) => type === 'spell')
.map((item) => {
// how to get the compendium items somewhat performantly? probably getDocument is fine as it caches individual documents as needed
const newItemId = game.packs.get("DDBImports.spells").index.find(({name}) => name === item.name)?._id;
const newItemData = await game.packs.get("DDBImports.spells").getDocument(newItemId).toJSON();
return {
...newItemData,
_id: item._id,
}
});

return {
_id: actor._id,
items: itemsToUpdate
}
});

Actor.updateDocuments(documentUpdates, {pack: 'DDBImports.monsters'});
const docsToUpdate = await game.packs.get("DDBImports.monsters").getDocuments({
items: {$elemMatch: { type: 'spell' }}
});

const documentUpdates = docsToUpdate.map((actor) => {
// some Promise.all shennanegins will be needed in here somewhere
const itemsToUpdate = actor.items
.filter(({type}) => type === 'spell')
.map((item) => {
// how to get the compendium items somewhat performantly? probably getDocument is fine as it caches individual documents as needed
const newItemId = game.packs.get("DDBImports.spells").index.find(({name}) => name === item.name)?._id;
const newItemData = await game.packs.get("DDBImports.spells").getDocument(newItemId).toJSON();
return {
...newItemData,
_id: item._id,
}
});

return {
_id: actor._id,
items: itemsToUpdate
}
});

Actor.updateDocuments(documentUpdates, {pack: 'DDBImports.monsters'});
B
BadIdeasBureau•924d ago
There's actually a way of doing updateAll on a compendium via Document.updateDocuments with an appropriate context modifier, e.g.:
let updated = await Actor.updateDocuments(updates, {pack: packname})
let updated = await Actor.updateDocuments(updates, {pack: packname})
It's more just how to construct the updates object for that
C
Calego•924d ago
the promise of updateAll was being able to use a function instead of an object for the update operation
B
BadIdeasBureau•924d ago
aaaah, right, yeah that makes sense Though I think doing getIndex({fields:[items]}) would give you enough to manipulate to construct the update object without grabbing the full Documents (since all you'd need is {_id, items}.... Which is actually the answer here, isn't it? Grab the expanded index, copy each entry with spells into updates = [{_id, items},...], futz with the items objects to swap out the spells, Actor.updateDocuments(updates, pack). I think that should work, unless there's some additional stuff updateEmbeddedDocuments does which would be bad to skip over... (Also if it helps with the query you can assume that any monster with an item of type "spell" needs an update - the spells compendium is a complete import from D&DBeyond, as is the monsters compendium) Hmmm.... I should probably prep for the session tonight before poking this more (first session of Witchlight, so the players would have to make some very poor life choices to end up in a combat where this matters, and I need to be ready to speak a whole lot in rhyme...)
C
Calego•924d ago
Alright @badideasbureau that's as good as I got. I know there's some Promise shennanegins that'll need to be sorted out in there for sure but the concept seems sound Oh shit you can do .find with index? that's dope does Foundry add .find to the Map prototype?? no, not on all the mindblown
B
BadIdeasBureau•924d ago
My general assumption with Foundry at this point is that if I want to treat it like an Array it's probably worth a shot 😛
C
Calego•924d ago
Ahh it's a Collection under the hood, neat I hope it goes without saying that you should try this with something tame in a test world first 😛
Want results from more Discord servers?
Add your server
More Posts
Primary and Embedded UpdatesTIL how to update embedded documents at the same time as parent documents. ``` game.actors.getName(compendium errors upgrading to v9hmm im not entirely sure how this will work though.. because I did just a quick smell test to my modBlind Chat MessagesAnyone have good examples of creating a chat message that should only be visible to a connected GM, compendium shennaneginsTIL about `CompendiumContent.getDocuments` taking nedb queries. ``` game.packs.get('dnd5e.heroes').gwith just a parent class and the class name, is there a way to instantiate a new class?JamesB and @veterini have a question for y'all in #coc7-dev https://discord.com/channels/7323252527More Hooks 5ehttps://github.com/ElfFriend-DnD/foundryvtt-more-hooks-5e I'm going to try to use this library to hacreateEmbeddedDocuments issueI'm going to throw my code in a thread so I don't clutter this channel up with a wall of textBase Item@otigon it occurs to me that the new `baseItem` property on weapons and such is probably of interestCompendium loadinghmm ghost, I added some console.time tags here and there to measure the difference between loading oSkill and Ability bonusesA massive enhancement of Skills and Ability Checks/Saves allows them to be individually affected by 150 Sheet Changes@sdenec @lordzeel Sheet Changes required: - New Cog-menu for Ability Scores and Skills to support neDocumentData shennaneginsTIL: It is possible to `update` an Actor or Item's `data.data` with arbitrary information that isn'tflag shenanneginsTIL you can set the `flags` key on a document to whatever you want to (e.g. a string). This is a surraaeAfter some discussion yesterday in #active-effects , I decided to try to create a system-agnostic modevMode json changed warningI checked the module repo and I honestly can't figure out where I would plug in my code. I'd be guesS3 File Picker SettingsCan someone with an S3 configuration give me a test of the FilePicker settings api and tell me if a item preCreate@sol.folango @mrprimate (pinging you two in particular because you do import stuff involving existinItem Macro Compendium WorkflowOkay, here's a long one that's a bit of a doozy. I'm looking at setting up some sort of tooling/workItem Specific Crit DetailsOh that critical hit thing is gonna hit MRE too isn't it... hrm...1.5.x 72%@dnd5e No action required (but suggested 🙂 ) The 1.5.0 milestone is ~72% complete. It has a due da