/** * class designed to store actor modification instructions, to apply them in a single operation, and have the ability to revert these */ export class ActorImpacts { constructor(actorToken) { this.actorToken = actorToken this.updates = [] this.deltas = [] this.itemCreates = [] this.itemUpdates = [] this.itemDeletes = [] } addActorUpdate(path, value) { this.updates.push([path, value]) } addActorDelta(path, value) { const intValue = Number.parseInt(value) if (Number.isInteger(intValue) && intValue != 0) { const delta = [path, intValue] this.deltas.push(delta) } else { console.error('Cannot use non integer value {} for delta update', value) } } addDeletedItem(item) { this.itemDeletes.push(item) } addCreatedItem(item) { this.itemCreates.push(item) } addItemUpdate(item, path, value) { const existing = this.itemUpdates.find(it => it.id == item.id) const update = [path, value] if (existing) { existing.updates.push(update) } else { this.itemUpdates.push({ id: item.id, updates: [update], deltas: [] }) } } addItemDelta(item, path, value) { const intValue = Number.parseInt(value) if (Number.isInteger(intValue) && intValue != 0) { const delta = [path, intValue] const existing = this.itemUpdates.find(it => it.id == item.id) if (existing) { existing.deltas.push(delta) } else { this.itemUpdates.push({ id: item.id, updates: [], deltas: [delta] }) } } else { console.error('Cannot use non integer value {} for delta update', value) } } reverseImpacts() { const reverse = ActorImpacts.$computeReverts(new ActorImpacts(this.actorToken), this, __ => this.actorToken.actor) reverse.itemCreates = this.itemDeletes.map(it => foundry.utils.duplicate(it)) reverse.itemDeletes = this.itemCreates.map(it => { return { id: it.id } }) reverse.itemUpdates = this.itemUpdates.map(it => ActorImpacts.$computeReverts({ id: it.id }, it, id => this.$getActorItem(id))) return reverse } toStorable() { delete this.actorToken return this } async applyImpacts() { const actor = this.actorToken.actor const isItemsDelete = this.itemDeletes.length > 0 const isItemsCreate = this.itemCreates.length > 0 const isItemsUpdate = this.itemUpdates.length > 0 const isActorUpdate = this.updates.length > 0 || this.deltas.length > 0 if (isItemsDelete) { const deletes = this.itemDeletes.map(it => it.id) await actor.deleteEmbeddedDocuments('Item', deletes, { render: !(isItemsCreate || isItemsUpdate || isActorUpdate) }) } if (isItemsCreate) { const creates = this.itemCreates const created = await actor.createEmbeddedDocuments('Item', creates, { render: !(isItemsUpdate || isActorUpdate)}) for (let i=0; i ActorImpacts.$computeUpdates(u, id => this.$getActorItem(id))) await actor.updateEmbeddedDocuments('Item', updates, { render: !isActorUpdate }) } if (isActorUpdate) { const updates = ActorImpacts.$computeUpdates(this, id => actor) await actor.update(updates, { render: true }) } } $getActorItem(id) { return this.actorToken.actor.items.get(id) } static $computeUpdates(u, getSource) { const source = getSource(u.id) const instruction = { _id: u.id } u.updates.forEach(u => instruction[u[0]] = u[1]) u.deltas.forEach(u => instruction[u[0]] = foundry.utils.getProperty(source, u[0]) + u[1]) return instruction } static $computeReverts(target, u, getSource) { const source = getSource(u.id) target.updates = u.updates.map(u => [u[0], foundry.utils.getProperty(source, u[0])]) target.deltas = u.deltas.map(d => [d[0], -d[1]]) return target } }