diff --git a/system/scripts/actor.js b/system/scripts/actor.js index 793d957..a7a5e5c 100644 --- a/system/scripts/actor.js +++ b/system/scripts/actor.js @@ -216,38 +216,51 @@ export class ActorL5r5e extends Actor { * @private */ async _updateActorFromAdvancement(item, isAdd) { - if (item && item.type === "advancement") { - const actor = foundry.utils.duplicate(this.system); - const itemData = item.system; - 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); - } + if (!item || item.type !== "advancement") { + return; + } + + const actorSystem = foundry.utils.duplicate(this.system); + const itemData = item.system; + if (itemData.advancement_type === "ring") { + // Ring + if (isAdd) { + actorSystem.rings[itemData.ring] = Math.min(9, actorSystem.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 - ); - } - } + actorSystem.rings[itemData.ring] = Math.max(1, actorSystem.rings[itemData.ring] - 1); } // Update Actor await this.update({ - system: foundry.utils.diffObject(this.system, actor), + system: foundry.utils.diffObject(this.system, actorSystem), }); + + } else { + // Skill + let skillItem = await fromUuid(itemData.skill); // Skill itemUuid + if (!skillItem) { + console.warn("L5R5E | Unknown skill item uuid", itemData.skill); + return; + } + // Out of actor item ? + if (!skillItem.actor || skillItem.actor._id !== this._id) { + const checkItem = this.items.getName(skillItem.name); + if (checkItem) { + skillItem = checkItem; + } else { + throw new Error(`Unable to find "${skillItem.name}" on this actor`); + } + } + + const newRank = isAdd + ? Math.min(9, skillItem.system.rank + 1) + : Math.max(0, skillItem.system.rank - 1); + if (skillItem.system.rank === newRank) { + return; + } + + // Update Item + await skillItem.update({ "system.rank": newRank }); } } diff --git a/system/scripts/actors/base-character-sheet.js b/system/scripts/actors/base-character-sheet.js index 9aa91d8..3f36526 100644 --- a/system/scripts/actors/base-character-sheet.js +++ b/system/scripts/actors/base-character-sheet.js @@ -133,146 +133,151 @@ export class BaseCharacterSheetL5r5e extends BaseSheetL5r5e { * @param {DragEvent} event */ async _onDrop(event) { - // *** Everything below here is only needed if the sheet is editable *** - if (!this.isEditable || this.actor.system.soft_locked) { - console.log("L5R5E | This sheet is not editable"); - return; - } - - // Check item type and subtype - const item = await game.l5r5e.HelpersL5r5e.getDragnDropTargetObject(event); - if (!item || !["Item", "JournalEntry"].includes(item.documentName) || item.type === "property") { - console.log(`L5R5E | Wrong subtype ${item?.type}`, item); - return; - } - - // Specific curriculum journal drop - if (item.documentName === "JournalEntry") { - // npc does not have this - if (!this.actor.system.identity?.school_curriculum_journal) { - console.log("L5R5E | NPC won't go to school :'("); - return; - } - this.actor.system.identity.school_curriculum_journal = { - id: item._id, - name: item.name, - pack: item.pack || null, - }; - await this.actor.update({ - system: { - identity: { - school_curriculum_journal: this.actor.system.identity.school_curriculum_journal, - }, - }, - }); - return; - } - - // Dropped an item with same "id" as one owned - if (this.actor.items) { - // Exit if we already owned exactly this id (drag a personal item on our own sheet) - if ( - this.actor.items.some((embedItem) => { - // Search in children - if (embedItem.items instanceof Map && embedItem.items.has(item._id)) { - return true; - } - return embedItem._id === item._id; - }) - ) { - console.log("L5R5E | This element has been ignored because it already exists in this actor", item.uuid); + try { + // *** Everything below here is only needed if the sheet is editable *** + if (!this.isEditable || this.actor.system.soft_locked) { + console.log("L5R5E | This sheet is not editable"); return; } - // Add quantity instead if they have (id is different so use type and name) - if (item.system.quantity) { - const tmpItem = this.actor.items.find( - (embedItem) => embedItem.name === item.name && embedItem.type === item.type - ); - if (tmpItem && this._modifyQuantity(tmpItem.id, 1)) { + // Check item type and subtype + const item = await game.l5r5e.HelpersL5r5e.getDragnDropTargetObject(event); + if (!item || !["Item", "JournalEntry"].includes(item.documentName) || item.type === "property") { + console.log(`L5R5E | Wrong subtype ${item?.type}`, item); + return; + } + + // Specific curriculum journal drop + if (item.documentName === "JournalEntry") { + // npc does not have this + if (!this.actor.system.identity?.school_curriculum_journal) { + console.log("L5R5E | NPC won't go to school :'("); return; } - } - } - - // Can add the item - Foundry override cause props - const allowed = Hooks.call("dropActorSheetData", this.actor, this, item); - if (allowed === false) { - return; - } - - let itemData = item.toObject(true); - - // Item subtype specific - switch (itemData.type) { - case "army_cohort": - case "army_fortification": - console.warn("L5R5E | Army items are not allowed", item?.type, item); + this.actor.system.identity.school_curriculum_journal = { + id: item._id, + name: item.name, + pack: item.pack || null, + }; + await this.actor.update({ + system: { + identity: { + school_curriculum_journal: this.actor.system.identity.school_curriculum_journal, + }, + }, + }); return; + } - case "advancement": - // Specific advancements, remove 1 to selected ring/skill - await this.actor.addBonus(item); - break; - - case "title": - // Generate new Ids for the embed items - await item.generateNewIdsForAllEmbedItems(); - - // Add embed advancements bonus - for (let [embedId, embedItem] of item.system.items) { - if (embedItem.type === "advancement") { - await this.actor.addBonus(embedItem); - } + // Dropped an item with same "id" as one owned + if (this.actor.items) { + // Exit if we already owned exactly this id (drag a personal item on our own sheet) + if ( + this.actor.items.some((embedItem) => { + // Search in children + if (embedItem.items instanceof Map && embedItem.items.has(item._id)) { + return true; + } + return embedItem._id === item._id; + }) + ) { + console.log("L5R5E | This element has been ignored because it already exists in this actor", item.uuid); + return; } - // refresh data - itemData = item.toObject(true); - break; - - case "skill": - itemData.system.rank = 0; - itemData.system.modifier = 0; - break; - - case "technique": - // School_ability and mastery_ability, allow only 1 per type - if (CONFIG.l5r5e.techniques.get(itemData.system.technique_type)?.type === "school") { - if ( - Array.from(this.actor.items).some((e) => { - return e.type === "technique" && e.system.technique_type === itemData.system.technique_type; - }) - ) { - ui.notifications.info(game.i18n.localize("l5r5e.techniques.only_one")); + // Add quantity instead if they have (id is different so use type and name) + if (item.system.quantity) { + const tmpItem = this.actor.items.find( + (embedItem) => embedItem.name === item.name && embedItem.type === item.type + ); + if (tmpItem && this._modifyQuantity(tmpItem.id, 1)) { return; } - - // No cost for schools - itemData.system.xp_cost = 0; - itemData.system.xp_used = 0; - itemData.system.in_curriculum = true; - } else { - // Check if technique is allowed for this character - // if (!game.user.isGM && !this.actor.system.techniques[itemData.system.technique_type]) { - // ui.notifications.info(game.i18n.localize("l5r5e.techniques.not_allowed")); - // return; - // } - - // Verify cost - itemData.system.xp_cost = - itemData.system.xp_cost > 0 ? itemData.system.xp_cost : CONFIG.l5r5e.xp.techniqueCost; - itemData.system.xp_used = itemData.system.xp_cost; } - break; - } + } - // Modify the bought at rank to the current actor rank - if (itemData.system.bought_at_rank !== undefined && this.actor.system.identity?.school_rank) { - itemData.system.bought_at_rank = this.actor.system.identity.school_rank; - } + // Can add the item - Foundry override cause props + const allowed = Hooks.call("dropActorSheetData", this.actor, this, item); + if (allowed === false) { + return; + } - // Finally create the embed - return this.actor.createEmbeddedDocuments("Item", [itemData]); + let itemData = item.toObject(true); + + // Item subtype specific + switch (itemData.type) { + case "army_cohort": + case "army_fortification": + console.warn("L5R5E | Army items are not allowed", item?.type, item); + return; + + case "advancement": + // Specific advancements, add x to selected ring/skill + await this.actor.addBonus(item); + break; + + case "title": + // Generate new Ids for the embed items + await item.generateNewIdsForAllEmbedItems(); + + // Add embed advancements bonus + for (let [embedId, embedItem] of item.system.items) { + if (embedItem.type === "advancement") { + await this.actor.addBonus(embedItem); + } + } + + // refresh data + itemData = item.toObject(true); + break; + + case "skill": + itemData.system.rank = 0; + itemData.system.modifier = 0; + break; + + case "technique": + // School_ability and mastery_ability, allow only 1 per type + if (CONFIG.l5r5e.techniques.get(itemData.system.technique_type)?.type === "school") { + if ( + Array.from(this.actor.items).some((e) => { + return e.type === "technique" && e.system.technique_type === itemData.system.technique_type; + }) + ) { + ui.notifications.info(game.i18n.localize("l5r5e.techniques.only_one")); + return; + } + + // No cost for schools + itemData.system.xp_cost = 0; + itemData.system.xp_used = 0; + itemData.system.in_curriculum = true; + } else { + // Check if technique is allowed for this character + // if (!game.user.isGM && !this.actor.system.techniques[itemData.system.technique_type]) { + // ui.notifications.info(game.i18n.localize("l5r5e.techniques.not_allowed")); + // return; + // } + + // Verify cost + itemData.system.xp_cost = + itemData.system.xp_cost > 0 ? itemData.system.xp_cost : CONFIG.l5r5e.xp.techniqueCost; + itemData.system.xp_used = itemData.system.xp_cost; + } + break; + } + + // Modify the bought at rank to the current actor rank + if (itemData.system.bought_at_rank !== undefined && this.actor.system.identity?.school_rank) { + itemData.system.bought_at_rank = this.actor.system.identity.school_rank; + } + + // Finally create the embed + return this.actor.createEmbeddedDocuments("Item", [itemData]); + + } catch (ex) { + console.warn("L5R5E |", ex.message); + } } /** @inheritdoc */ diff --git a/system/scripts/config.js b/system/scripts/config.js index bed9d04..ea7abf9 100644 --- a/system/scripts/config.js +++ b/system/scripts/config.js @@ -47,7 +47,7 @@ L5R5E.techniques.set("title_ability", { type: "title", displayInTypes: false }); L5R5E.techniques.set("specificity", { type: "custom", displayInTypes: false }); // *** SkillId - CategoryId *** -// TODO @deprecated hardcoded skills +// Hardcoded skills are still required for compatibility (migration & olds worlds) L5R5E.skills = new Map(); L5R5E.skills.set("aesthetics", "artisan"); L5R5E.skills.set("composition", "artisan"); diff --git a/system/scripts/helpers.js b/system/scripts/helpers.js index c153a85..a6b9102 100644 --- a/system/scripts/helpers.js +++ b/system/scripts/helpers.js @@ -90,7 +90,7 @@ export class HelpersL5r5e { * @return {{cat: any, id: any, label: *}[]} */ static getSkillsList(useGroup = false) { - console.warn('@deprecated hardcoded skills - helpers.getSkillsList()'); // TODO @deprecated hardcoded skills + console.warn('@deprecated hardcoded skills - helpers.getSkillsList() - Use getSkillsItemsList() + splitSkillByCategory() instead'); // TODO @deprecated hardcoded skills if (!useGroup) { return Array.from(CONFIG.l5r5e.skills).map(([id, cat]) => ({ diff --git a/system/scripts/item.js b/system/scripts/item.js index d96726a..cfce215 100644 --- a/system/scripts/item.js +++ b/system/scripts/item.js @@ -242,7 +242,11 @@ export class ItemL5r5e extends Item { if (addBonusToActor) { const actor = this.actor; if (item instanceof Item && actor instanceof Actor) { - actor.addBonus(item); + try { + await actor.addBonus(item); + } catch (ex) { + console.warn("L5R5E |", ex.message); + } } } diff --git a/system/scripts/items/advancement-sheet.js b/system/scripts/items/advancement-sheet.js index eaf47dd..4692377 100644 --- a/system/scripts/items/advancement-sheet.js +++ b/system/scripts/items/advancement-sheet.js @@ -24,7 +24,7 @@ export class AdvancementSheetL5r5e extends ItemSheetL5r5e { const sheetData = await super.getData(options); sheetData.data.subTypesList = AdvancementSheetL5r5e.types; - sheetData.data.skillsList = game.l5r5e.HelpersL5r5e.getSkillsList(true); + sheetData.data.skillsList = game.l5r5e.HelpersL5r5e.splitSkillByCategory(await game.l5r5e.HelpersL5r5e.getSkillsItemsList(this.actor)); return sheetData; } @@ -82,33 +82,45 @@ export class AdvancementSheetL5r5e extends ItemSheetL5r5e { let name = this.object.name; let img = this.object.img; + const getLocalItemByUuid = async (uuid) => { + const item = await fromUuid(uuid); + if (!item) { + return null; + } + if (item?.actor?._id === this.actor._id) { + return item; + } + return this.items.getName(item.name); + } + const skillItemNew = newChoice.skill ? (await getLocalItemByUuid(newChoice.skill)) : null; + const skillItemOld = oldChoice.skill ? (await getLocalItemByUuid(oldChoice.skill)) : null; + // 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"; + + } else if (newChoice.skill && skillItemNew) { + name = skillItemNew.name +"+1"; img = `systems/l5r5e/assets/dices/default/skill_blank.svg`; } // Object embed in actor ? const actor = this.document.actor; if (actor) { + if (newChoice.skill && !skillItemNew.actor) { + ui.notifications.warn(`Unable to find "${skillItemNew?.name}" on this actor`); + return; + } + const actorData = foundry.utils.duplicate(actor.system); - let skillCatId = null; // Old choices if (oldChoice.ring) { actorData.rings[oldChoice.ring] = Math.max(1, actorData.rings[oldChoice.ring] - 1); } - if (oldChoice.skill) { - skillCatId = CONFIG.l5r5e.skills.get(oldChoice.skill); - actorData.skills[skillCatId][oldChoice.skill] = Math.max( - 0, - actorData.skills[skillCatId][oldChoice.skill] - 1 - ); + if (oldChoice.skill && skillItemOld) { + await skillItemOld.update({ "system.rank": Math.max(0, skillItemOld.system.rank - 1) }); } // new choices @@ -119,15 +131,11 @@ export class AdvancementSheetL5r5e extends ItemSheetL5r5e { game.i18n.localize(`l5r5e.rings.${newChoice.ring}`) + ` +1 (${actorData.rings[newChoice.ring] - 1} -> ${actorData.rings[newChoice.ring]})`; } - if (newChoice.skill) { - skillCatId = CONFIG.l5r5e.skills.get(newChoice.skill); - 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 (${actorData.skills[skillCatId][newChoice.skill] - 1} -> ${ - actorData.skills[skillCatId][newChoice.skill] - })`; + if (newChoice.skill && skillItemNew) { + const newRank = Math.min(9, skillItemNew.system.rank + 1); + await skillItemNew.update({ "system.rank": newRank }); + xp_used = newRank * CONFIG.l5r5e.xp.skillCostMultiplier; + name = `${skillItemNew.name} +1 (${newRank - 1} -> ${newRank})`; } // Update Actor @@ -141,7 +149,7 @@ export class AdvancementSheetL5r5e extends ItemSheetL5r5e { name: name, img: img, system: { - xp_used: xp_used, + xp_used, }, }); diff --git a/system/scripts/items/technique-sheet.js b/system/scripts/items/technique-sheet.js index 813acec..878319c 100644 --- a/system/scripts/items/technique-sheet.js +++ b/system/scripts/items/technique-sheet.js @@ -160,18 +160,6 @@ export class TechniqueSheetL5r5e extends ItemSheetL5r5e { } }); - // List skill (not include in cat) - const unqSkillList = new Set(); - skillList.forEach((s) => { - s = s?.trim(); - if (!!s && CONFIG.l5r5e.skills.has(s)) { - const cat = CONFIG.l5r5e.skills.get(s); - if (!unqCatList.has(cat)) { - unqSkillList.add(s); - } - } - }); - - return [...unqCatList, ...unqSkillList]; + return [...unqCatList]; } } diff --git a/system/templates/items/advancement/advancement-sheet.html b/system/templates/items/advancement/advancement-sheet.html index a38f38e..a434446 100644 --- a/system/templates/items/advancement/advancement-sheet.html +++ b/system/templates/items/advancement/advancement-sheet.html @@ -32,7 +32,7 @@ {{#each data.skillsList as |skills catId|}} {{#each skills as |obj|}} - + {{/each}} {{/each}}