From ec2acf1648587a2b19b39eff1a8bb9e6c914a960 Mon Sep 17 00:00:00 2001 From: Vlyan Date: Sun, 13 Dec 2020 16:28:25 +0100 Subject: [PATCH] Template and raw sheet for npc --- system/l5r-ui/scripts/help/l5rui-help.js | 2 +- system/scripts/actor-l5r5e.js | 30 ++-- system/scripts/dice/dice-picker-dialog.js | 24 ++- system/scripts/dice/roll.js | 11 +- system/scripts/items/feat-sheet.js | 2 +- system/scripts/items/item-sheet.js | 2 +- system/scripts/items/weapon-sheet.js | 2 +- system/scripts/l5r5e-config.js | 8 + system/scripts/main-l5r5e.js | 24 +-- system/scripts/sheets/actor-sheet.js | 16 +- system/scripts/sheets/npc-sheet.js | 141 ++++++++++++++++++ system/template.json | 31 +++- system/templates/dice/dice-picker-dialog.html | 4 + system/templates/sheets/npc-sheet.html | 103 +++++++++++++ 14 files changed, 352 insertions(+), 48 deletions(-) create mode 100644 system/scripts/l5r5e-config.js create mode 100644 system/scripts/sheets/npc-sheet.js create mode 100644 system/templates/sheets/npc-sheet.html diff --git a/system/l5r-ui/scripts/help/l5rui-help.js b/system/l5r-ui/scripts/help/l5rui-help.js index bf6f766..3107a30 100644 --- a/system/l5r-ui/scripts/help/l5rui-help.js +++ b/system/l5r-ui/scripts/help/l5rui-help.js @@ -37,7 +37,7 @@ Hooks.once("ready", async function () { //----logo image var logo = document.getElementById("logo"); - logo.setAttribute("src", "systems/l5r5e/assets/l5r-logo.webp"); + logo.setAttribute("src", CONFIG.L5r5e.paths.assets + "l5r-logo.webp"); //--------------ouvrir le menu lien sur click logo logo.setAttribute("title", "Aide en Ligne"); diff --git a/system/scripts/actor-l5r5e.js b/system/scripts/actor-l5r5e.js index 41e049b..c6bd065 100644 --- a/system/scripts/actor-l5r5e.js +++ b/system/scripts/actor-l5r5e.js @@ -1,34 +1,38 @@ - - /** -* Extends the actor to process special things from L5R. -*/ + * Extends the actor to process special things from L5R. + */ export class ActorL5r5e extends Actor { + /** + * Create a new entity using provided input data + * @override + */ + static async create(data, options = {}) { + if (!Object.keys(data).includes("type")) { + data.type = "character"; + } - /** @override */ - static async create(data, options={}) { - if(!Object.keys(data).includes("type")) data.type = "character"; await super.create(data, options); } - + + /** @override */ prepareData() { super.prepareData(); - + const actorData = this.data; const data = actorData.data; - const isCharacter = actorData.type === "character"; - + const isCharacter = ["character", "npc"].includes(actorData.type); + if (isCharacter) { data.endurance = (Number(data.rings.earth) + Number(data.rings.fire)) * 2; data.composure = (Number(data.rings.earth) + Number(data.rings.water)) * 2; data.focus = Number(data.rings.air) + Number(data.rings.fire); data.vigilante = Math.floor((Number(data.rings.air) + Number(data.rings.water)) / 2); data.void_points.max = data.rings.void; - + // Make sure void points are never greater than max if (data.void_points.current > data.void_points.max) { data.void_points.current = data.void_points.max; } } } -} \ No newline at end of file +} diff --git a/system/scripts/dice/dice-picker-dialog.js b/system/scripts/dice/dice-picker-dialog.js index 9298128..82e1ebc 100644 --- a/system/scripts/dice/dice-picker-dialog.js +++ b/system/scripts/dice/dice-picker-dialog.js @@ -5,8 +5,6 @@ import { RollL5r5e } from "./roll.js"; export class DicePickerDialog extends Application { - static stances = ["earth", "air", "water", "fire", "void"]; - /** * Current Actor */ @@ -43,7 +41,7 @@ export class DicePickerDialog extends Application { return mergeObject(super.defaultOptions, { id: "l5r5e-dice-picker-dialog", classes: ["l5r5e", "dice-picker-dialog"], - template: "systems/l5r5e/templates/dice/dice-picker-dialog.html", + template: CONFIG.L5r5e.paths.templates + "dice/dice-picker-dialog.html", title: "L5R Dice Roller", width: 660, height: 460, @@ -106,7 +104,7 @@ export class DicePickerDialog extends Application { * @param ringId */ set ringId(ringId) { - this._ringId = DicePickerDialog.stances.includes(ringId) || null; + this._ringId = CONFIG.L5r5e.stances.includes(ringId) || null; } /** @@ -131,7 +129,20 @@ export class DicePickerDialog extends Application { } this._skillData.cat = cat; this._skillData.name = game.i18n.localize("l5r5e.skills." + cat + "." + this._skillData.id); - this._skillData.value = this._actor?.data?.data?.skills[cat]?.[this._skillData.id].value || 0; + + if (!this._actor) { + return; + } + switch (this._actor.data.type) { + case "character": + this._skillData.value = this._actor.data.data.skills[cat]?.[this._skillData.id]?.value || 0; + break; + + case "npc": + // Skill value is in categories for npc + this._skillData.value = this._actor.data.data.skills[cat] || 0; + break; + } } /** @@ -174,6 +185,7 @@ export class DicePickerDialog extends Application { dicesList: [0, 1, 2, 3, 4, 5, 6], skillData: this._skillData, actor: this._actor, + actorIsPc: !this._actor || this._actor.data?.type === "character", difficulty: this._difficulty, canUseVoidPoint: !this._actor || this._actor.data.data.void_points.current > 0, }; @@ -362,7 +374,7 @@ export class DicePickerDialog extends Application { * @private */ _getElements() { - return DicePickerDialog.stances.map((e) => { + return CONFIG.L5r5e.stances.map((e) => { return { id: e, label: game.i18n.localize(`l5r5e.rings.${e}`), diff --git a/system/scripts/dice/roll.js b/system/scripts/dice/roll.js index 1cf9ca4..be70602 100644 --- a/system/scripts/dice/roll.js +++ b/system/scripts/dice/roll.js @@ -4,11 +4,8 @@ import { L5rBaseDie } from "./dietype/l5r-base-die.js"; * Roll for L5R5e */ export class RollL5r5e extends Roll { - static CHAT_TEMPLATE = "systems/l5r5e/templates/dice/chat-roll.html"; - static TOOLTIP_TEMPLATE = "systems/l5r5e/templates/dice/tooltip.html"; - - // static CHAT_TEMPLATE = `${CONFIG.L5r5e.pathTemplates}dice/chat-roll.html`; - // static TOOLTIP_TEMPLATE = `${CONFIG.L5r5e.pathTemplates}dice/tooltip.html`; + static CHAT_TEMPLATE = "dice/chat-roll.html"; + static TOOLTIP_TEMPLATE = "dice/tooltip.html"; constructor(...args) { super(...args); @@ -175,7 +172,7 @@ export class RollL5r5e extends Roll { displaySummary: contexte?.from !== "render", }; - return renderTemplate(this.constructor.TOOLTIP_TEMPLATE, { chatData }); + return renderTemplate(CONFIG.L5r5e.paths.templates + this.constructor.TOOLTIP_TEMPLATE, { chatData }); } /** @@ -187,7 +184,7 @@ export class RollL5r5e extends Roll { { user: game.user._id, flavor: null, - template: this.constructor.CHAT_TEMPLATE, + template: CONFIG.L5r5e.paths.templates + this.constructor.CHAT_TEMPLATE, blind: false, }, chatOptions diff --git a/system/scripts/items/feat-sheet.js b/system/scripts/items/feat-sheet.js index ec7744d..930ecff 100644 --- a/system/scripts/items/feat-sheet.js +++ b/system/scripts/items/feat-sheet.js @@ -8,7 +8,7 @@ export class FeatSheetL5r5e extends ItemSheetL5r5e { static get defaultOptions() { return mergeObject(super.defaultOptions, { classes: ["l5r5e", "sheet", "feat"], - template: "systems/l5r5e/templates/item/feat-sheet.html", + template: CONFIG.L5r5e.paths.templates + "item/feat-sheet.html", width: 520, height: 480, tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }], diff --git a/system/scripts/items/item-sheet.js b/system/scripts/items/item-sheet.js index 4650f7e..1068017 100644 --- a/system/scripts/items/item-sheet.js +++ b/system/scripts/items/item-sheet.js @@ -7,7 +7,7 @@ export class ItemSheetL5r5e extends ItemSheet { static get defaultOptions() { return mergeObject(super.defaultOptions, { classes: ["l5r5e", "sheet", "item"], - template: "systems/l5r5e/templates/item/item-sheet.html", + template: CONFIG.L5r5e.paths.templates + "item/item-sheet.html", width: 520, height: 480, tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }], diff --git a/system/scripts/items/weapon-sheet.js b/system/scripts/items/weapon-sheet.js index bc96351..0345843 100644 --- a/system/scripts/items/weapon-sheet.js +++ b/system/scripts/items/weapon-sheet.js @@ -8,7 +8,7 @@ export class WeaponSheetL5r5e extends ItemSheetL5r5e { static get defaultOptions() { return mergeObject(super.defaultOptions, { classes: ["l5r5e", "sheet", "weapon"], - template: "systems/l5r5e/templates/item/weapon-sheet.html", + template: CONFIG.L5r5e.paths.templates + "item/weapon-sheet.html", width: 520, height: 480, tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }], diff --git a/system/scripts/l5r5e-config.js b/system/scripts/l5r5e-config.js new file mode 100644 index 0000000..a533425 --- /dev/null +++ b/system/scripts/l5r5e-config.js @@ -0,0 +1,8 @@ +export const L5R5E = {}; + +L5R5E.stances = ["earth", "air", "water", "fire", "void"]; + +L5R5E.paths = { + assets: `systems/l5r5e/assets/`, + templates: `systems/l5r5e/templates/`, +}; diff --git a/system/scripts/main-l5r5e.js b/system/scripts/main-l5r5e.js index ddd0b67..4fa80dc 100644 --- a/system/scripts/main-l5r5e.js +++ b/system/scripts/main-l5r5e.js @@ -1,8 +1,10 @@ // Import Modules +import { L5R5E } from "./l5r5e-config.js"; import { RegisterSettings } from "./settings.js"; import { PreloadTemplates } from "./preloadTemplates.js"; import { ActorL5r5e } from "./actor-l5r5e.js"; import { ActorSheetL5r5e } from "./sheets/actor-sheet.js"; +import { NpcSheetL5r5e } from "./sheets/npc-sheet.js"; import { RollL5r5e, AbilityDie, RingDie, DicePickerDialog } from "./dice-l5r5e.js"; import { ItemL5r5e } from "./items/item.js"; import { ItemSheetL5r5e } from "./items/item-sheet.js"; @@ -15,7 +17,18 @@ import { FeatSheetL5r5e } from "./items/feat-sheet.js"; /* Initialize system */ /* ------------------------------------ */ Hooks.once("init", async function () { - console.log("l5r5e | Initializing l5r5e"); + // console.log("L5R5e | Initializing l5r5e"); + // Ascii art + console.log( + " _ ___ ___ ___\n" + + " | | | __| _ \\ | __| ___ \n" + + " | |__|__ \\ / |__ \\/ -_)\n" + + " |____|___/_|_\\ |___/\\___|\n" + + " " + ); + + // Global access to L5R Config + CONFIG.L5r5e = L5R5E; // Assign custom classes and constants here CONFIG.Actor.entityClass = ActorL5r5e; @@ -35,14 +48,6 @@ Hooks.once("init", async function () { DicePickerDialog, }; - // Define L5R Paths - CONFIG.L5r5e = { - paths: { - assets: `systems/l5r5e/assets/`, - templates: `systems/l5r5e/templates/`, - }, - }; - // Register custom system settings RegisterSettings(); @@ -53,6 +58,7 @@ Hooks.once("init", async function () { // Actors sheet Actors.unregisterSheet("core", ActorSheet); Actors.registerSheet("l5r5e", ActorSheetL5r5e, { types: ["character"], makeDefault: true }); + Actors.registerSheet("l5r5e", NpcSheetL5r5e, { types: ["npc"], makeDefault: false }); // Items sheet Items.unregisterSheet("core", ItemSheet); diff --git a/system/scripts/sheets/actor-sheet.js b/system/scripts/sheets/actor-sheet.js index 7c27571..64c1891 100644 --- a/system/scripts/sheets/actor-sheet.js +++ b/system/scripts/sheets/actor-sheet.js @@ -2,7 +2,7 @@ export class ActorSheetL5r5e extends ActorSheet { static get defaultOptions() { return mergeObject(super.defaultOptions, { classes: ["l5r5e", "sheet", "actor"], - template: "systems/l5r5e/templates/sheets/actor-sheet.html", + template: CONFIG.L5r5e.paths.templates + "sheets/actor-sheet.html", width: 600, height: 800, tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }], @@ -61,7 +61,7 @@ export class ActorSheetL5r5e extends ActorSheet { if (!this.options.editable) return; // Update Inventory Item - html.find(".item-edit").click((ev) => { + html.find(".item-edit").on("click", (ev) => { const li = $(ev.currentTarget).parents(".item"); const itemId = li.data("itemId"); const item = this.actor.getOwnedItem(itemId); @@ -69,17 +69,17 @@ export class ActorSheetL5r5e extends ActorSheet { }); // Delete Inventory Item - html.find(".item-delete").click((ev) => { + html.find(".item-delete").on("click", (ev) => { const li = $(ev.currentTarget).parents(".item"); const itemId = li.data("itemId"); this.actor.deleteOwnedItem(itemId); }); - html.find(".feat-add").click((ev) => { + html.find(".feat-add").on("click", (ev) => { this._createFeat(); }); - html.find(".feat-delete").click((ev) => { + html.find(".feat-delete").on("click", (ev) => { const li = $(ev.currentTarget).parents(".feat"); const featId = li.data("featId"); console.log("Remove feat" + featId + " clicked"); @@ -87,21 +87,21 @@ export class ActorSheetL5r5e extends ActorSheet { this.actor.deleteOwnedItem(featId); }); - html.find(".feat-edit").click((ev) => { + html.find(".feat-edit").on("click", (ev) => { const li = $(ev.currentTarget).parents(".feat"); const featId = li.data("featId"); const feat = this.actor.getOwnedItem(featId); feat.sheet.render(true); }); - html.find(".skill-name").click((ev) => { + html.find(".skill-name").on("click", (ev) => { const li = $(ev.currentTarget).parents(".skill"); const skillId = li.data("skill"); this._onSkillClicked(skillId); }); - html.find(".acquisition-add").click((ev) => { + html.find(".acquisition-add").on("click", (ev) => { this._createFeat(); }); } diff --git a/system/scripts/sheets/npc-sheet.js b/system/scripts/sheets/npc-sheet.js new file mode 100644 index 0000000..b32b264 --- /dev/null +++ b/system/scripts/sheets/npc-sheet.js @@ -0,0 +1,141 @@ +// TODO extend ActorSheetL5r5e ? +export class NpcSheetL5r5e extends ActorSheet { + static types = ["minion", "adversary"]; + + static get defaultOptions() { + return mergeObject(super.defaultOptions, { + classes: ["l5r5e", "sheet", "npc"], + template: CONFIG.L5r5e.paths.templates + "sheets/npc-sheet.html", + width: 600, + height: 800, + tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }], + dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }], + }); + } + + getData() { + const sheetData = super.getData(); + + this._prepareItems(sheetData); + + sheetData.data.feats = sheetData.items.filter((item) => item.type === "feat"); + sheetData.data.types = NpcSheetL5r5e.types; + sheetData.data.stances = CONFIG.L5r5e.stances; + + return sheetData; + } + + /** + * Update the actor. + * @param event + * @param formData + */ + _updateObject(event, formData) { + return this.object.update(formData); + } + + /** + * Prepare item data to be displayed in the actor sheet. + * @param sheetData Data of the actor been displayed in the sheet. + */ + _prepareItems(sheetData) { + for (let item of sheetData.items) { + if (item.type === "weapon") { + item.isWeapon = true; + item.isEquipment = true; + } else if (item.type === "feat") { + item.isFeat = true; + } else { + item.isEquipment = true; + } + } + } + + _prepareFeats() {} + + /** + * Subscribe to events from the sheet. + * @param html HTML content of the sheet. + */ + activateListeners(html) { + super.activateListeners(html); + + // Everything below here is only needed if the sheet is editable + if (!this.options.editable) return; + + // Update Inventory Item + html.find(".item-edit").on("click", (ev) => { + const li = $(ev.currentTarget).parents(".item"); + const itemId = li.data("itemId"); + const item = this.actor.getOwnedItem(itemId); + item.sheet.render(true); + }); + + // Delete Inventory Item + html.find(".item-delete").on("click", (ev) => { + const li = $(ev.currentTarget).parents(".item"); + const itemId = li.data("itemId"); + this.actor.deleteOwnedItem(itemId); + }); + + html.find(".feat-add").on("click", (ev) => { + this._createFeat(); + }); + + html.find(".feat-delete").on("click", (ev) => { + const li = $(ev.currentTarget).parents(".feat"); + const featId = li.data("featId"); + console.log("Remove feat" + featId + " clicked"); + + this.actor.deleteOwnedItem(featId); + }); + + html.find(".feat-edit").on("click", (ev) => { + const li = $(ev.currentTarget).parents(".feat"); + const featId = li.data("featId"); + const feat = this.actor.getOwnedItem(featId); + feat.sheet.render(true); + }); + + html.find(".skill-name").on("click", (ev) => { + const li = $(ev.currentTarget).parents(".skill"); + const skillId = li.data("skill"); + + this._onSkillClicked(skillId); + }); + + html.find(".acquisition-add").on("click", (ev) => { + this._createFeat(); + }); + } + + /** + * Creates a new feat for the character and shows a window to edit it. + */ + async _createFeat() { + const data = { + name: game.i18n.localize("l5r5e.featplaceholdername"), + type: "feat", + }; + const created = await this.actor.createEmbeddedEntity("OwnedItem", data); + const feat = this.actor.getOwnedItem(created._id); + + // Default values + //feat.rank = 1; + //feat.xp_used = 0; + + feat.sheet.render(true); + + return feat; + } + + /** + * React to a skill from the skills list been clicked. + * @param {string} skillId Unique ID of the skill been clicked. + */ + async _onSkillClicked(skillId) { + console.log("Clicked on skill " + skillId); + + new game.l5r5e.DicePickerDialog({ skillId: skillId, actor: this.actor }).render(); + } +} diff --git a/system/template.json b/system/template.json index d258758..aa8d004 100644 --- a/system/template.json +++ b/system/template.json @@ -1,6 +1,6 @@ { "Actor": { - "types": ["character"], + "types": ["character", "npc"], "templates": { "identity": { "clan": "", @@ -89,6 +89,35 @@ "templates": ["identity", "rings", "social", "skills", "conflict", "xp"], "notes": "", "feats": [] + }, + "npc": { + "templates": ["rings", "social", "conflict"], + "name": "", + "type": "minion", + "attitude": "", + "notes": "", + "conflict_rank": { + "martial": 0, + "social": 0 + }, + "rings_affinities": { + "strength": { + "ring": "fire", + "value": 2 + }, + "weakness": { + "ring": "water", + "value": -2 + } + }, + "skills": { + "artisan": 0, + "martial": 0, + "scholar": 0, + "social": 0, + "trade": 0 + }, + "feats": [] } }, "Item": { diff --git a/system/templates/dice/dice-picker-dialog.html b/system/templates/dice/dice-picker-dialog.html index 2efe69c..767957b 100644 --- a/system/templates/dice/dice-picker-dialog.html +++ b/system/templates/dice/dice-picker-dialog.html @@ -35,9 +35,13 @@
+ + {{#if actorIsPc}}
+ {{/if}} +
diff --git a/system/templates/sheets/npc-sheet.html b/system/templates/sheets/npc-sheet.html new file mode 100644 index 0000000..e3304cd --- /dev/null +++ b/system/templates/sheets/npc-sheet.html @@ -0,0 +1,103 @@ +
+ + {{json this}} + + {{!-- Sheet Header --}} +
+ +
+

+
+
+ {{> 'systems/l5r5e/templates/sheets/actor/social.html' }} +

{{ localize 'l5r5e.attributes.title' }}

+ {{> 'systems/l5r5e/templates/sheets/actor/rings.html' }} + {{> 'systems/l5r5e/templates/sheets/actor/attributes.html' }} +
+
+ +
+ conflict rank +
martial : +
social : +
+
+ Npc Type +
+ + +
+ +
+ attitude: + +
+ +
+ Skills +
    + {{#each data.skills as |skillValue skillCatId|}} +
  • + {{localizeSkill skillCatId "title"}} : + +
  • + {{/each}} +
+
+ +
+ rings_affinities + +
+ strengths : + + + +
+ weakness : + + + +
+ + {{!-- Sheet Body --}} +
+ + + {{!-- Skills Tab --}} +
+
    + {{#each data.skills as |category id|}} + {{> 'systems/l5r5e/templates/sheets/actor/category.html' category=category categoryId=id}} + {{/each}} +
+ {{> 'systems/l5r5e/templates/sheets/actor/feats.html' }} +
+ + + {{> 'systems/l5r5e/templates/sheets/actor/narrative.html' }} + + {{> 'systems/l5r5e/templates/item/items.html' }} + + {{> 'systems/l5r5e/templates/item/weapons.html' }} + +
+
\ No newline at end of file