Working on 0.8.x

- Title's Advancements are now reflected on actor
- Migration update
This commit is contained in:
Vlyan
2021-05-22 17:20:17 +02:00
parent 4f9b72c63f
commit 53f04e6cef
33 changed files with 423 additions and 281 deletions

View File

@@ -108,4 +108,65 @@ export class ActorL5r5e extends Actor {
}
}
}
/**
* Add a Ring/Skill point to the current actor if the item is a advancement
* @param {Item} item
* @return {Promise<void>}
*/
async addBonus(item) {
return this._updateActorFromAdvancement(item, true);
}
/**
* Remove a Ring/Skill point to the current actor if the item is a advancement
* @param {Item} item
* @return {Promise<void>}
*/
async removeBonus(item) {
return this._updateActorFromAdvancement(item, false);
}
/**
* Alter Actor skill/ring from a advancement
* @param {Item} item
* @param {boolean} isAdd True=add, false=remove
* @return {Promise<void>}
* @private
*/
async _updateActorFromAdvancement(item, isAdd) {
if (item && item.type === "advancement") {
const actor = foundry.utils.duplicate(this.data.data);
const itemData = item.data.data;
if (itemData.advancement_type === "ring") {
// Ring
if (isAdd) {
actor.rings[itemData.ring] = Math.min(9, actor.rings[itemData.ring] + 1);
} else {
actor.rings[itemData.ring] = Math.max(1, actor.rings[itemData.ring] - 1);
}
} else {
// Skill
const skillCatId = CONFIG.l5r5e.skills.get(itemData.skill);
if (skillCatId) {
if (isAdd) {
actor.skills[skillCatId][itemData.skill] = Math.min(
9,
actor.skills[skillCatId][itemData.skill] + 1
);
} else {
actor.skills[skillCatId][itemData.skill] = Math.max(
0,
actor.skills[skillCatId][itemData.skill] - 1
);
}
}
}
// Update Actor
await this.update({
data: foundry.utils.diffObject(this.data.data, actor),
});
}
}
}

View File

@@ -58,9 +58,19 @@ export class BaseSheetL5r5e extends ActorSheet {
// Add tech the character knows
sheetData.items.forEach((item) => {
if (item.type === "technique") {
out[item.data.technique_type].push(item);
}
switch (item.type) {
case "technique":
out[item.data.technique_type].push(item);
break;
case "title":
Array.from(item.data.items).forEach(([id, embedItem]) => {
if (embedItem.data.type === "technique") {
out[embedItem.data.data.technique_type].push(embedItem.data);
}
});
break;
} //swi
});
// Remove unused techs
@@ -170,35 +180,44 @@ export class BaseSheetL5r5e extends ActorSheet {
) {
return;
}
item = item.toJSON();
// Dropped a item with same "id" as one owned, add qte instead
if (item.data.quantity && this.actor.data.items) {
const tmpItem = this.actor.data.items.find((e) => e.name === item.name && e.type === item.type);
if (item.data.data.quantity && this.actor.data.items) {
const tmpItem = this.actor.data.items.find((e) => e.name === item.data.name && e.type === item.data.type);
if (tmpItem && this._modifyQuantity(tmpItem.id, 1)) {
return;
}
}
// Item subtype specific
switch (item.type) {
switch (item.data.type) {
case "bond": // no break
case "advancement": // no break
case "peculiarity": // no break
case "item_pattern": // no break
case "signature_scroll":
// Modify the bought at rank to the current actor rank
if (this.actor.data.data.identity?.school_rank) {
item.data.bought_at_rank = this.actor.data.data.identity.school_rank;
item.data.data.bought_at_rank = this.actor.data.data.identity.school_rank;
}
break;
case "advancement":
// Modify the bought at rank to the current actor rank
if (this.actor.data.data.identity?.school_rank) {
item.data.data.bought_at_rank = this.actor.data.data.identity.school_rank;
}
// Specific advancements, remove 1 to selected ring/skill
await this.actor.addBonus(item);
break;
case "technique":
// School_ability and mastery_ability, allow only 1 per type
if (CONFIG.l5r5e.techniques.get(item.data.technique_type)?.type === "school") {
if (CONFIG.l5r5e.techniques.get(item.data.data.technique_type)?.type === "school") {
if (
Array.from(this.actor.items).some(
(e) => e.type === "technique" && e.data.data.technique_type === item.data.technique_type
(e) =>
e.type === "technique" && e.data.data.technique_type === item.data.data.technique_type
)
) {
ui.notifications.info(game.i18n.localize("l5r5e.techniques.only_one"));
@@ -206,24 +225,25 @@ export class BaseSheetL5r5e extends ActorSheet {
}
// No cost for schools
item.data.xp_cost = 0;
item.data.xp_used = 0;
item.data.in_curriculum = true;
item.data.data.xp_cost = 0;
item.data.data.xp_used = 0;
item.data.data.in_curriculum = true;
} else {
// Check if technique is allowed for this character
if (!game.user.isGM && !this.actor.data.data.techniques[item.data.technique_type]) {
if (!game.user.isGM && !this.actor.data.data.techniques[item.data.data.technique_type]) {
ui.notifications.info(game.i18n.localize("l5r5e.techniques.not_allowed"));
return;
}
// Verify cost
item.data.xp_cost = item.data.xp_cost > 0 ? item.data.xp_cost : CONFIG.l5r5e.xp.techniqueCost;
item.data.xp_used = item.data.xp_cost;
item.data.data.xp_cost =
item.data.data.xp_cost > 0 ? item.data.data.xp_cost : CONFIG.l5r5e.xp.techniqueCost;
item.data.data.xp_used = item.data.data.xp_cost;
}
// Modify the bought at rank to the current actor rank
if (this.actor.data.data.identity?.school_rank) {
item.data.bought_at_rank = this.actor.data.data.identity.school_rank;
item.data.data.bought_at_rank = this.actor.data.data.identity.school_rank;
}
break;
}
@@ -234,7 +254,7 @@ export class BaseSheetL5r5e extends ActorSheet {
return;
}
return this._onDropItemCreate(item);
return this._onDropItemCreate(item.data.toObject(false));
}
/**
@@ -395,12 +415,25 @@ export class BaseSheetL5r5e extends ActorSheet {
event.preventDefault();
event.stopPropagation();
let item;
const itemId = $(event.currentTarget).data("item-id");
if (!itemId) {
return;
}
const item = this.actor.items.get(itemId);
const itemParentId = $(event.currentTarget).data("item-parent-id");
if (itemParentId) {
// Embed Item
const parentItem = this.actor.items.get(itemParentId);
if (!parentItem) {
return;
}
item = parentItem.items.get(itemId);
} else {
// Regular item
item = this.actor.items.get(itemId);
}
if (!item) {
return;
}
@@ -421,35 +454,20 @@ export class BaseSheetL5r5e extends ActorSheet {
return;
}
// Remove 1 qty if possible
const tmpItem = this.actor.items.get(itemId);
if (tmpItem && tmpItem.data.data.quantity > 1 && this._modifyQuantity(tmpItem.id, -1)) {
if (!tmpItem) {
return;
}
// Remove 1 qty if possible
if (tmpItem.data.data.quantity > 1 && this._modifyQuantity(tmpItem.id, -1)) {
return;
}
const callback = async () => {
// Specific advancements, remove 1 to selected ring/skill
if (tmpItem.type === "advancement") {
const actor = duplicate(this.actor.data.data);
const itmData = tmpItem.data.data;
if (itmData.advancement_type === "ring") {
// Ring
actor.rings[itmData.ring] = Math.max(1, actor.rings[itmData.ring] - 1);
} else {
// Skill
const skillCatId = CONFIG.l5r5e.skills.get(itmData.skill);
if (skillCatId) {
actor.skills[skillCatId][itmData.skill] = Math.max(
0,
actor.skills[skillCatId][itmData.skill] - 1
);
}
}
// Update Actor
this.actor.update({
data: diffObject(this.actor.data.data, actor),
});
await this.actor.removeBonus(tmpItem);
}
return this.actor.deleteEmbeddedDocuments("Item", [itemId]);
};

View File

@@ -49,13 +49,10 @@ export class CharacterSheetL5r5e extends BaseSheetL5r5e {
// Split Money
sheetData.data.data.money = this._zeniToMoney(this.actor.data.data.zeni);
// Split school advancements by rank, and calculate xp spent
// Split school advancements by rank, and calculate xp spent and add it to total
this._prepareSchoolAdvancement(sheetData);
// Titles
this._prepareTitles(sheetData);
// Others
// Split Others advancements, and calculate xp spent and add it to total
this._prepareOthersAdvancement(sheetData);
// Total
@@ -105,13 +102,6 @@ export class CharacterSheetL5r5e extends BaseSheetL5r5e {
.activate("advancement_rank_" + (this.actor.data.data.identity.school_rank || 0));
}
/**
* Prepare Titles, and get xp spend
*/
_prepareTitles(sheetData) {
// TODO
}
/**
* Split the school advancement, calculate the total xp spent and the current total xp spent by rank
*/
@@ -148,12 +138,23 @@ export class CharacterSheetL5r5e extends BaseSheetL5r5e {
* Prepare Bonds, Item Pattern, Signature Scroll and get xp spend
*/
_prepareOthersAdvancement(sheetData) {
// Split OthersAdvancement from items
sheetData.data.advancementsOthers = sheetData.items.filter((item) =>
["bond", "item_pattern", "title", "signature_scroll"].includes(item.type)
);
// Sort by rank desc
// sheetData.data.bondsList.sort((a, b) => (b.data.rank || 0) - (a.data.rank || 0));
sheetData.data.advancementsOthers.sort((a, b) => (b.data.rank || 0) - (a.data.rank || 0));
// Total xp spent
sheetData.data.advancementsOthersTotalXp = sheetData.data.advancementsOthers.reduce(
(acc, item) => acc + (item.data.xp_used || 0),
0
);
// Update the total spent
sheetData.data.data.xp_spent =
parseInt(sheetData.data.data.xp_spent) + sheetData.data.advancementsOthersTotalXp;
}
/**
@@ -177,6 +178,12 @@ export class CharacterSheetL5r5e extends BaseSheetL5r5e {
return super._updateObject(event, formData);
}
/**
* Convert a sum in Zeni to Zeni, Bu and Koku
* @param {number} zeni
* @return {{bu: number, koku: number, zeni: number}}
* @private
*/
_zeniToMoney(zeni) {
const money = {
koku: 0,
@@ -196,6 +203,14 @@ export class CharacterSheetL5r5e extends BaseSheetL5r5e {
return money;
}
/**
* Convert a sum in Zeni, Bu and Koku to Zeni
* @param {number} koku
* @param {number} bu
* @param {number} zeni
* @return {number}
* @private
*/
_moneyToZeni(koku, bu, zeni) {
return Math.floor(koku * CONFIG.l5r5e.money[0]) + Math.floor(bu * CONFIG.l5r5e.money[1]) + Math.floor(zeni);
}

View File

@@ -287,11 +287,14 @@ export class TwentyQuestions {
// Clear and add items to actor
const deleteIds = actor.data.items.map((e) => e.id);
await actor.deleteEmbeddedDocuments("Item", deleteIds);
if (deleteIds.length > 0) {
await actor.deleteEmbeddedDocuments("Item", deleteIds);
}
// Add items in 20Q to actor
for (const types of Object.values(itemsCache)) {
for (const item of types) {
const newItemsData = [];
Object.values(itemsCache).forEach((types) => {
types.forEach((item) => {
const itemData = foundry.utils.duplicate(item.data);
if (itemData.data?.bought_at_rank) {
itemData.data.bought_at_rank = 0;
@@ -299,8 +302,11 @@ export class TwentyQuestions {
if (itemData.data?.xp_spent) {
itemData.data.xp_spent = 0;
}
await actor.createEmbeddedDocuments("Item", [itemData]);
}
newItemsData.push(itemData);
});
});
if (newItemsData.length > 0) {
await actor.createEmbeddedDocuments("Item", newItemsData);
}
// Update actor

View File

@@ -23,7 +23,7 @@ L5R5E.techniques.set("ninjutsu", { type: "core", displayInTypes: true });
L5R5E.techniques.set("school_ability", { type: "school", displayInTypes: false });
L5R5E.techniques.set("mastery_ability", { type: "school", displayInTypes: false });
// Title
// L5R5E.techniques.set("title_ability", { type: "title", displayInTypes: false });
L5R5E.techniques.set("title_ability", { type: "title", displayInTypes: false });
// Custom
L5R5E.techniques.set("specificity", { type: "custom", displayInTypes: false });

View File

@@ -71,14 +71,14 @@ export class L5rBaseDie extends DiceTerm {
* Evaluate the roll term, populating the results Array
* @override
*/
evaluate({ minimize = false, maximize = false } = {}) {
evaluate({ minimize = false, maximize = false, async = false } = {}) {
if (this._evaluated) {
throw new Error(`This ${this.constructor.name} has already been evaluated and is immutable`);
}
// Roll the initial number of dice
for (let n = 1; n <= this.number; n++) {
this.roll({ minimize, maximize });
this.roll({ minimize, maximize, async });
}
// Apply modifiers

View File

@@ -294,14 +294,6 @@ export class RollL5r5e extends Roll {
return renderTemplate(chatOptions.template, chatData);
}
/**
* Render the HTML for the ChatMessage which should be added to the log
* @return {Promise<jQuery>}
*/
async getHTML() {
console.log(" --------- getHTML");
}
/**
* Transform a Roll instance into a ChatMessage, displaying the roll result.
* This function can either create the ChatMessage directly, or return the data object that will be used to create.

View File

@@ -7,6 +7,14 @@ export class ItemL5r5e extends Item {
return this.data.data.items || null;
}
/**
* Return the linked Actor instance if any (current or embed)
* @return {Actor|null}
*/
get actor() {
return super.actor || game.actors.get(this.data.data.parent_id?.actor_id) || null;
}
/**
* Create a new entity using provided input data
* @override
@@ -27,7 +35,7 @@ export class ItemL5r5e extends Item {
*/
async update(data = {}, context = {}) {
// Regular
if (!this.data.data.parentId) {
if (!this.data.data.parent_id) {
return super.update(data, context);
}
@@ -67,7 +75,7 @@ export class ItemL5r5e extends Item {
this.data.data.items = new Map();
itemsData.forEach((item) => {
this.addEmbedItem(item, { save: false, newId: false });
this.addEmbedItem(item, { save: false, newId: false, addBonusToActor: false });
});
}
}
@@ -75,10 +83,16 @@ export class ItemL5r5e extends Item {
// ***** parent ids management *****
/**
* Return a string with idemId + actorId if any
* @return {string} itemId|actor
* @return {{item_id: (string|null), actor_id?: (string|null)}}
*/
getParentsIds() {
return this.id + (this.actor?.data?._id ? `|${this.actor.data._id}` : "");
const parent = {
item_id: this.id,
};
if (this.actor?.data?._id) {
parent.actor_id = this.actor.data._id;
}
return parent;
}
/**
@@ -86,17 +100,18 @@ export class ItemL5r5e extends Item {
* @return {ItemL5r5e|null}
*/
getItemFromParentId() {
const parentIds = this.data.data.parent_id;
let parentItem;
let [parentItemId, parentActorId] = this.data.data.parentId.split("|");
if (parentActorId) {
if (parentIds?.actor_id) {
// Actor item object
const parentActor = parentActorId ? game.actors.get(parentActorId) : null;
parentItem = parentActor?.items.get(parentItemId);
} else {
const parentActor = parentIds.actor_id ? game.actors.get(parentIds.actor_id) : null;
parentItem = parentActor?.items.get(parentIds.item_id);
} else if (parentIds?.item_id) {
// World Object
parentItem = game.items.get(parentItemId);
parentItem = game.items.get(parentIds.item_id);
}
return parentItem;
}
@@ -115,9 +130,10 @@ export class ItemL5r5e extends Item {
* @param {ItemL5r5e} item Object to add
* @param {boolean} save if we save in db or not (used internally)
* @param {boolean} newId if we change the id
* @param {boolean} addBonusToActor if we update the actor bonus for advancements
* @return {Promise<void>}
*/
async addEmbedItem(item, { save = true, newId = true } = {}) {
async addEmbedItem(item, { save = true, newId = true, addBonusToActor = true } = {}) {
if (!item) {
return;
}
@@ -133,14 +149,17 @@ export class ItemL5r5e extends Item {
}
// Tag parent (flags won't work as we have no id in db)
item.data.data.parentId = this.getParentsIds();
item.data.data.parent_id = this.getParentsIds();
// Object
this.data.data.items.set(item.data._id, item);
// TODO add bonus from actor
if (this.actor instanceof Actor) {
// const item = this.data.data.items.get(id);
// Add bonus to actor
if (addBonusToActor) {
const actor = this.actor;
if (item instanceof Item && actor instanceof Actor) {
actor.addBonus(item);
}
}
if (save) {
@@ -155,23 +174,28 @@ export class ItemL5r5e extends Item {
* @return {Promise<void>}
*/
async updateEmbedItem(item, { save = true } = {}) {
await this.addEmbedItem(item, { save, newId: false });
await this.addEmbedItem(item, { save, newId: false, addBonusToActor: false });
}
/**
* Delete the Embed Item and clear the actor bonus if any
* @param {ItemL5r5e} item Object to add
* @param id Item id
* @param {boolean} save if we save in db or not (used internally)
* @param {boolean} removeBonusFromActor if we update the actor bonus for advancements
* @return {Promise<void>}
*/
async deleteEmbedItem(id, { save = true } = {}) {
async deleteEmbedItem(id, { save = true, removeBonusFromActor = true } = {}) {
if (!this.data.data.items.has(id)) {
return;
}
// TODO remove bonus from actor
if (this.actor instanceof Actor) {
// const item = this.data.data.items.get(id);
// Remove bonus from actor
if (removeBonusFromActor) {
const actor = this.actor;
const item = this.data.data.items.get(id);
if (item instanceof Item && actor instanceof Actor) {
actor.removeBonus(item);
}
}
// Remove the embed item

View File

@@ -7,7 +7,7 @@ export class AdvancementSheetL5r5e extends ItemSheetL5r5e {
/**
* Sub Types of advancements
*/
static types = ["ring", "skill"]; // "peculiarity" and "technique" have theirs own xp count
static types = ["ring", "skill"]; // others have theirs own xp count
/** @override */
static get defaultOptions() {
@@ -73,47 +73,55 @@ export class AdvancementSheetL5r5e extends ItemSheetL5r5e {
// Modify image to reflect choice
if (newChoice.ring) {
name = game.i18n.localize(`l5r5e.rings.${newChoice.ring}`) + "+1";
img = `systems/l5r5e/assets/icons/rings/${newChoice.ring}.svg`;
} else if (newChoice.skill) {
name =
game.i18n.localize(`l5r5e.skills.${CONFIG.l5r5e.skills.get(newChoice.skill)}.${newChoice.skill}`) +
"+1";
img = `systems/l5r5e/assets/dices/default/skill_blank.svg`;
}
// Object embed in actor ?
if (this.actor) {
const actor = duplicate(this.actor.data.data);
const actor = this.document.actor;
if (actor) {
const actorData = foundry.utils.duplicate(actor.data.data);
let skillCatId = null;
// Old choices
if (oldChoice.ring) {
actor.rings[oldChoice.ring] = Math.max(1, actor.rings[oldChoice.ring] - 1);
actorData.rings[oldChoice.ring] = Math.max(1, actorData.rings[oldChoice.ring] - 1);
}
if (oldChoice.skill) {
skillCatId = CONFIG.l5r5e.skills.get(oldChoice.skill);
actor.skills[skillCatId][oldChoice.skill] = Math.max(0, actor.skills[skillCatId][oldChoice.skill] - 1);
actorData.skills[skillCatId][oldChoice.skill] = Math.max(
0,
actorData.skills[skillCatId][oldChoice.skill] - 1
);
}
// new choices
if (newChoice.ring) {
actor.rings[newChoice.ring] = actor.rings[newChoice.ring] + 1;
xp_used = actor.rings[newChoice.ring] * CONFIG.l5r5e.xp.ringCostMultiplier;
actorData.rings[newChoice.ring] = actorData.rings[newChoice.ring] + 1;
xp_used = actorData.rings[newChoice.ring] * CONFIG.l5r5e.xp.ringCostMultiplier;
name =
game.i18n.localize(`l5r5e.rings.${newChoice.ring}`) +
` +1 (${actor.rings[newChoice.ring] - 1} -> ${actor.rings[newChoice.ring]})`;
` +1 (${actorData.rings[newChoice.ring] - 1} -> ${actorData.rings[newChoice.ring]})`;
}
if (newChoice.skill) {
skillCatId = CONFIG.l5r5e.skills.get(newChoice.skill);
actor.skills[skillCatId][newChoice.skill] = actor.skills[skillCatId][newChoice.skill] + 1;
xp_used = actor.skills[skillCatId][newChoice.skill] * CONFIG.l5r5e.xp.skillCostMultiplier;
actorData.skills[skillCatId][newChoice.skill] = actorData.skills[skillCatId][newChoice.skill] + 1;
xp_used = actorData.skills[skillCatId][newChoice.skill] * CONFIG.l5r5e.xp.skillCostMultiplier;
name =
game.i18n.localize(`l5r5e.skills.${skillCatId}.${newChoice.skill}`) +
` +1 (${actor.skills[skillCatId][newChoice.skill] - 1} -> ${
actor.skills[skillCatId][newChoice.skill]
` +1 (${actorData.skills[skillCatId][newChoice.skill] - 1} -> ${
actorData.skills[skillCatId][newChoice.skill]
})`;
}
// Update Actor
await this.actor.update({
data: diffObject(this.actor.data.data, actor),
await actor.update({
data: foundry.utils.diffObject(actor.data.data, actorData),
});
}

View File

@@ -21,9 +21,6 @@ export class ItemPatternSheetL5r5e extends ItemSheetL5r5e {
async getData(options = {}) {
const sheetData = await super.getData(options);
sheetData.data.dtypes = ["String", "Number", "Boolean"];
sheetData.data.ringsList = game.l5r5e.HelpersL5r5e.getRingsList();
// Linked Property
sheetData.data.linkedProperty = await this.getLinkedProperty(sheetData);

View File

@@ -256,6 +256,23 @@ export class ItemSheetL5r5e extends ItemSheet {
event.preventDefault();
event.stopPropagation();
const itemId = $(event.currentTarget).data("item-id");
this.document.deleteEmbedItem(itemId);
const item = this.document.getEmbedItem(itemId);
if (!item) {
return;
}
const callback = async () => {
this.document.deleteEmbedItem(itemId);
};
// Holing Ctrl = without confirm
if (event.ctrlKey) {
return callback();
}
game.l5r5e.HelpersL5r5e.confirmDeleteDialog(
game.i18n.format("l5r5e.global.delete_confirm", { name: item.name }),
callback
);
}
}

View File

@@ -21,14 +21,15 @@ export class TitleSheetL5r5e extends ItemSheetL5r5e {
async getData(options = {}) {
const sheetData = await super.getData(options);
sheetData.data.dtypes = ["String", "Number", "Boolean"];
sheetData.data.ringsList = game.l5r5e.HelpersL5r5e.getRingsList();
console.log(sheetData.data.data.items); // todo tmp
// Prepare OwnedItems
sheetData.data.embedItemsList = this._prepareEmbedItems(sheetData.data.data.items);
console.log(sheetData); // todo tmp
// Automatically compute the xp cost
sheetData.data.data.xp_used = sheetData.data.embedItemsList.reduce(
(acc, item) => acc + (+item.data.xp_used || 0),
0
);
return sheetData;
}
@@ -63,17 +64,19 @@ export class TitleSheetL5r5e extends ItemSheetL5r5e {
// Check item type and subtype
let item = await game.l5r5e.HelpersL5r5e.getDragnDropTargetObject(event);
if (!item || (item.documentName !== "Item" && !["technique", "advancement"].includes(item.data.type))) {
if (!item || item.documentName !== "Item" || !["technique", "advancement"].includes(item.data.type)) {
return;
}
const data = item.data.toJSON();
const data = item.data.toObject(false);
console.log("------ data", data); // todo tmp
// Check xp for techs
if (item.data.type === "technique") {
data.data.xp_cost = data.data.xp_cost > 0 ? data.data.xp_cost : CONFIG.l5r5e.xp.techniqueCost;
data.data.xp_used = data.data.xp_cost;
}
this.document.addEmbedItem(data);
console.log(this.document); // todo tmp
}
/**
@@ -93,16 +96,4 @@ export class TitleSheetL5r5e extends ItemSheetL5r5e {
html.find(`.item-edit`).on("click", this._editSubItem.bind(this));
html.find(`.item-delete`).on("click", this._deleteSubItem.bind(this));
}
/**
* This method is called upon form submission after form data is validated
* @param {Event} event The initial triggering submission event
* @param {object} formData The object of validated form data with which to update the object
* @returns {Promise} A Promise which resolves once the update operation has completed
* @abstract
*/
// async _updateObject(event, formData) {
// console.log("------- _updateObject.", formData); // todo TMP
// return super._updateObject(event, formData);
// }
}

View File

@@ -3,10 +3,10 @@
*/
export class MigrationL5r5e {
/**
* Version needed for migration stuff to trigger
* Minimum Version needed for migration stuff to trigger
* @type {string}
*/
static NEEDED_VERSION = "1.1.0";
static NEEDED_VERSION = "1.3.0";
/**
* Return true if the current world need some updates
@@ -14,7 +14,7 @@ export class MigrationL5r5e {
*/
static needUpdate() {
const currentVersion = game.settings.get("l5r5e", "systemMigrationVersion");
return currentVersion && isNewerVersion(MigrationL5r5e.NEEDED_VERSION, currentVersion);
return currentVersion && foundry.utils.isNewerVersion(MigrationL5r5e.NEEDED_VERSION, currentVersion);
}
/**
@@ -26,6 +26,7 @@ export class MigrationL5r5e {
return;
}
// Warn the users
ui.notifications.info(
`Applying L5R5e System Migration for version ${game.system.data.version}.` +
` Please be patient and do not close your game or shut down your server.`,
@@ -33,56 +34,53 @@ export class MigrationL5r5e {
);
// Migrate World Actors
for (let a of game.actors.contents) {
for (let actor of game.actors.contents) {
try {
const updateData = MigrationL5r5e._migrateActorData(a.data);
if (!isObjectEmpty(updateData)) {
console.log(`Migrating Actor entity ${a.name}`);
await a.update(updateData, { enforceTypes: false }); // TODO use Actor.updateDocuments(data, context) for multiple actors
const updateData = MigrationL5r5e._migrateActorData(actor.data);
if (!foundry.utils.isObjectEmpty(updateData)) {
console.log(`L5R5E | Migrating Actor entity ${actor.name}`);
await actor.update(updateData);
}
} catch (err) {
err.message = `Failed L5R5e system migration for Actor ${a.name}: ${err.message}`;
err.message = `L5R5E | Failed L5R5e system migration for Actor ${actor.name}: ${err.message}`;
console.error(err);
}
}
// Migrate World Items
for (let i of game.items.contents) {
for (let item of game.items.contents) {
try {
const updateData = MigrationL5r5e._migrateItemData(i.data);
if (!isObjectEmpty(updateData)) {
console.log(`Migrating Item entity ${i.name}`);
await i.update(updateData, { enforceTypes: false }); // TODO use Item.updateDocuments(data, context) for multiple actors
const updateData = MigrationL5r5e._migrateItemData(item.data);
if (!foundry.utils.isObjectEmpty(updateData)) {
console.log(`L5R5E | Migrating Item entity ${item.name}`);
await item.update(updateData);
}
} catch (err) {
err.message = `Failed L5R5e system migration for Item ${i.name}: ${err.message}`;
err.message = `L5R5E | Failed L5R5e system migration for Item ${item.name}: ${err.message}`;
console.error(err);
}
}
// Migrate Actor Override Tokens
for (let s of game.scenes.contents) {
for (let scene of game.scenes.contents) {
try {
const updateData = MigrationL5r5e._migrateSceneData(s.data);
if (!isObjectEmpty(updateData)) {
console.log(`Migrating Scene entity ${s.name}`);
await s.update(updateData, { enforceTypes: false }); // TODO use Scene.updateDocuments(data, context) for multiple actors
const updateData = MigrationL5r5e._migrateSceneData(scene.data);
if (!foundry.utils.isObjectEmpty(updateData)) {
console.log(`L5R5E | Migrating Scene entity ${scene.name}`);
await scene.update(updateData);
}
} catch (err) {
err.message = `Failed L5R5e system migration for Scene ${s.name}: ${err.message}`;
err.message = `L5R5E | Failed L5R5e system migration for Scene ${scene.name}: ${err.message}`;
console.error(err);
}
}
// Migrate World Compendium Packs
for (let p of game.packs) {
if (p.metadata.package !== "world") {
for (let pack of game.packs) {
if (pack.metadata.package !== "world" || !["Actor", "Item", "Scene"].includes(pack.metadata.entity)) {
continue;
}
if (!["Actor", "Item", "Scene"].includes(p.metadata.entity)) {
continue;
}
await MigrationL5r5e._migrateCompendium(p);
await MigrationL5r5e._migrateCompendium(pack);
}
// Set the migration as complete
@@ -94,7 +92,7 @@ export class MigrationL5r5e {
/**
* Apply migration rules to all Entities within a single Compendium pack
* @param pack
* @param {Compendium} pack
* @return {Promise}
*/
static async _migrateCompendium(pack) {
@@ -103,18 +101,20 @@ export class MigrationL5r5e {
return;
}
// Unlock the pack for editing
const wasLocked = pack.locked;
await pack.configure({ locked: false });
try {
// Unlock the pack for editing
await pack.configure({ locked: false });
// Begin by requesting server-side data model migration and get the migrated content
await pack.migrate();
const content = await pack.getContent();
// Begin by requesting server-side data model migration and get the migrated content
await pack.migrate();
const documents = await pack.getDocuments();
// Iterate over compendium entries - applying fine-tuned migration functions
const updateDatasList = [];
for (let ent of documents) {
let updateData = {};
// Iterate over compendium entries - applying fine-tuned migration functions
for (let ent of content) {
let updateData = {};
try {
switch (entity) {
case "Actor":
updateData = MigrationL5r5e._migrateActorData(ent.data);
@@ -126,24 +126,30 @@ export class MigrationL5r5e {
updateData = MigrationL5r5e._migrateSceneData(ent.data);
break;
}
if (isObjectEmpty(updateData)) {
if (foundry.utils.isObjectEmpty(updateData)) {
continue;
}
// Save the entry, if data was changed
updateData["_id"] = ent._id;
await pack.updateEntity(updateData); // TODO use Item/Actor.updateDocuments(data, context) for multiple actors
console.log(`Migrated ${entity} entity ${ent.name} in Compendium ${pack.collection}`);
} catch (err) {
// Handle migration failures
err.message = `Failed L5R5e system migration for entity ${ent.name} in pack ${pack.collection}: ${err.message}`;
console.error(err);
// Add the entry, if data was changed
updateData["_id"] = ent.data._id;
updateDatasList.push(updateData);
console.log(`L5R5E | Migrating ${entity} entity ${ent.name} in Compendium ${pack.collection}`);
}
// Save the modified entries
if (updateDatasList.length > 0) {
await pack.documentClass.updateDocuments(updateDatasList, { pack: pack.collection });
}
} catch (err) {
// Handle migration failures
err.message = `L5R5E | Failed system migration for entities ${entity} in pack ${pack.collection}: ${err.message}`;
console.error(err);
}
// Apply the original locked status for the pack
pack.configure({ locked: wasLocked });
console.log(`Migrated all ${entity} contents from Compendium ${pack.collection}`);
console.log(`L5R5E | Migrated all ${entity} contents from Compendium ${pack.collection}`);
}
/**
@@ -153,7 +159,7 @@ export class MigrationL5r5e {
* @return {Object} The updateData to apply
*/
static _migrateSceneData(scene) {
const tokens = duplicate(scene.tokens);
const tokens = foundry.utils.duplicate(scene.tokens);
return {
tokens: tokens.map((t) => {
if (!t.actorId || t.actorLink || !t.actorData.data) {
@@ -183,8 +189,6 @@ export class MigrationL5r5e {
const updateData = {};
const actorData = actor.data;
console.log(actorData); // TODO TMP data.data ? à vérifier
// ***** Start of 1.1.0 *****
// Add "Prepared" in actor
if (actorData.prepared === undefined) {
@@ -211,6 +215,10 @@ export class MigrationL5r5e {
actorData.rings_affinities.strength.value;
updateData["data.rings_affinities." + actorData.rings_affinities.weakness.ring] =
actorData.rings_affinities.weakness.value;
// Delete old keys : not working :'(
updateData["-=data.rings_affinities.strength"] = null;
updateData["-=data.rings_affinities.weakness"] = null;
}
// ***** End of 1.3.0 *****
@@ -224,7 +232,7 @@ export class MigrationL5r5e {
*/
static cleanActorData(actorData) {
const model = game.system.model.Actor[actorData.type];
actorData.data = filterObject(actorData.data, model);
actorData.data = foundry.utils.filterObject(actorData.data, model);
return actorData;
}