COrrections, WIP
This commit is contained in:
@@ -56,22 +56,6 @@ export class ActorL5r5e extends Actor {
|
||||
);
|
||||
break;
|
||||
|
||||
case "army":
|
||||
foundry.utils.mergeObject(
|
||||
docData.prototypeToken,
|
||||
{
|
||||
actorLink: true,
|
||||
disposition: 0, // neutral
|
||||
bar1: {
|
||||
attribute: "battle_readiness.casualties_strength",
|
||||
},
|
||||
bar2: {
|
||||
attribute: "battle_readiness.panic_discipline",
|
||||
},
|
||||
},
|
||||
{ overwrite: false }
|
||||
);
|
||||
break;
|
||||
}
|
||||
await super.create(docData, options);
|
||||
}
|
||||
|
||||
@@ -1,330 +0,0 @@
|
||||
import { BaseSheetL5r5e } from "./base-sheet.js";
|
||||
|
||||
/**
|
||||
* Sheet for Army "actor"
|
||||
*/
|
||||
export class ArmySheetL5r5e extends BaseSheetL5r5e {
|
||||
/**
|
||||
* Commons options
|
||||
*/
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ["l5r5e", "sheet", "actor", "army"],
|
||||
template: CONFIG.l5r5e.paths.templates + "actors/army-sheet.html",
|
||||
width: 600,
|
||||
height: 800,
|
||||
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "army" }],
|
||||
});
|
||||
}
|
||||
|
||||
/** @override */
|
||||
constructor(options = {}) {
|
||||
super(options);
|
||||
this._initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize once
|
||||
* @private
|
||||
*/
|
||||
_initialize() {
|
||||
const data = this.object.system;
|
||||
|
||||
// update linked actor datas
|
||||
if (data.commander_actor_id) {
|
||||
const commander = game.actors.get(data.commander_actor_id);
|
||||
if (commander) {
|
||||
this._updateLinkedActorData("commander", commander, true);
|
||||
} else {
|
||||
this._removeLinkedActorData("commander");
|
||||
}
|
||||
}
|
||||
if (data.warlord_actor_id) {
|
||||
const warlord = game.actors.get(data.warlord_actor_id);
|
||||
if (warlord) {
|
||||
this._updateLinkedActorData("warlord", warlord, true);
|
||||
} else {
|
||||
this._removeLinkedActorData("warlord");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create drag-and-drop workflow handlers for this Application
|
||||
* @return An array of DragDrop handlers
|
||||
*/
|
||||
_createDragDropHandlers() {
|
||||
return [
|
||||
new foundry.applications.ux.DragDrop.implementation({
|
||||
dropSelector: ".warlord",
|
||||
callbacks: { drop: this._onDropActors.bind(this, "warlord") },
|
||||
}),
|
||||
new foundry.applications.ux.DragDrop.implementation({
|
||||
dropSelector: ".commander",
|
||||
callbacks: { drop: this._onDropActors.bind(this, "commander") },
|
||||
}),
|
||||
new foundry.applications.ux.DragDrop.implementation({
|
||||
dropSelector: null,
|
||||
callbacks: { drop: this._onDrop.bind(this) },
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate a named TinyMCE text editor
|
||||
* @param {string} name The named data field which the editor modifies.
|
||||
* @param {object} options TinyMCE initialization options passed to TextEditor.create
|
||||
* @param {string} initialContent Initial text content for the editor area.
|
||||
* @override
|
||||
*/
|
||||
activateEditor(name, options = {}, initialContent = "") {
|
||||
// Symbols Compatibility with old compendium modules (PRE l5r v1.7.2)
|
||||
if (
|
||||
["system.army_abilities", "system.supplies_logistics", "system.past_battles"].includes(name) &&
|
||||
initialContent
|
||||
) {
|
||||
initialContent = game.l5r5e.HelpersL5r5e.convertSymbols(initialContent, false);
|
||||
}
|
||||
return super.activateEditor(name, options, initialContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to events from the sheet.
|
||||
* @param {jQuery} html HTML content of the sheet.
|
||||
*/
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
|
||||
// *** Everything below here is only needed if the sheet is editable ***
|
||||
if (!this.isEditable) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Casualties/Panic +/-
|
||||
html.find(".addsub-control").on("click", this._modifyCasualtiesOrPanic.bind(this));
|
||||
|
||||
if (this.actor.system.soft_locked) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete the linked Actor (warlord/commander)
|
||||
html.find(".actor-remove-control").on("click", this._removeLinkedActor.bind(this));
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
async getData(options = {}) {
|
||||
const sheetData = await super.getData(options);
|
||||
|
||||
// Split Items by types
|
||||
sheetData.data.splitItemsList = this._splitItems(sheetData);
|
||||
|
||||
// Editors enrichment
|
||||
for (const name of ["army_abilities", "supplies_logistics", "past_battles"]) {
|
||||
sheetData.data.enrichedHtml[name] = await foundry.applications.ux.TextEditor.implementation.enrichHTML(sheetData.data.system[name], {
|
||||
async: true,
|
||||
});
|
||||
}
|
||||
|
||||
return sheetData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split Items by types for better readability
|
||||
* @private
|
||||
*/
|
||||
_splitItems(sheetData) {
|
||||
const out = {
|
||||
army_cohort: [],
|
||||
army_fortification: [],
|
||||
};
|
||||
|
||||
sheetData.items.forEach((item) => {
|
||||
if (["army_cohort", "army_fortification"].includes(item.type)) {
|
||||
out[item.type].push(item);
|
||||
}
|
||||
});
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle dropped Item data on the Actor sheet (cohort, fortification)
|
||||
* @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) {
|
||||
return;
|
||||
}
|
||||
|
||||
const item = await game.l5r5e.HelpersL5r5e.getDragnDropTargetObject(event);
|
||||
if (!item || item.documentName !== "Item" || !["army_cohort", "army_fortification"].includes(item.type)) {
|
||||
// actor dual trigger...
|
||||
if (item?.documentName !== "Actor") {
|
||||
console.warn("L5R5E | AS | Characters items are not allowed", item?.type, item);
|
||||
}
|
||||
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);
|
||||
|
||||
// Finally, create the embed
|
||||
return this.actor.createEmbeddedDocuments("Item", [itemData]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle dropped Actor data on the Actor sheet
|
||||
* @param {string} type warlord|commander|item
|
||||
* @param {DragEvent} event
|
||||
*/
|
||||
async _onDropActors(type, event) {
|
||||
// *** Everything below here is only needed if the sheet is editable ***
|
||||
if (!this.isEditable || this.actor.system.soft_locked) {
|
||||
return;
|
||||
}
|
||||
|
||||
const droppedActor = await game.l5r5e.HelpersL5r5e.getDragnDropTargetObject(event);
|
||||
return this._updateLinkedActorData(type, droppedActor, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the linked actor (commander/warlord)
|
||||
* @param {Event} event
|
||||
* @return {Promise<void>}
|
||||
* @private
|
||||
*/
|
||||
async _removeLinkedActor(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const id = $(event.currentTarget).data("actor-id");
|
||||
const type = $(event.currentTarget).data("type");
|
||||
if (!id || !type) {
|
||||
return;
|
||||
}
|
||||
return this._removeLinkedActorData(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update actor datas for this army sheet
|
||||
* @param {string} type commander|warlord
|
||||
* @param {ActorL5r5e} actor actor object
|
||||
* @param {boolean} isInit If it's initialization process
|
||||
* @return {Promise<abstract.Document>}
|
||||
* @private
|
||||
*/
|
||||
async _updateLinkedActorData(type, actor, isInit = false) {
|
||||
if (!actor || actor.documentName !== "Actor" || !actor.isCharacterType) {
|
||||
console.warn("L5R5E | AS | Wrong actor type", actor?.type, actor);
|
||||
return;
|
||||
}
|
||||
|
||||
const actorPath = `${CONFIG.l5r5e.paths.assets}icons/actors/`;
|
||||
const actorData = {};
|
||||
switch (type) {
|
||||
case "commander":
|
||||
actorData["system.commander"] = actor.name;
|
||||
actorData["system.commander_actor_id"] = actor._id;
|
||||
actorData["system.commander_standing.honor"] = actor.system.social.honor;
|
||||
actorData["system.commander_standing.glory"] = actor.system.social.glory;
|
||||
actorData["system.commander_standing.status"] = actor.system.social.status;
|
||||
|
||||
// Replace the image by commander's image
|
||||
if (
|
||||
!isInit &&
|
||||
this.actor.img !== actor.img &&
|
||||
![`${actorPath}character.svg`, `${actorPath}npc.svg`].includes(actor.img)
|
||||
) {
|
||||
actorData["img"] = actor.img;
|
||||
actorData["prototypeToken.texture.src"] = actor.prototypeToken.texture.src;
|
||||
}
|
||||
break;
|
||||
|
||||
case "warlord":
|
||||
actorData["system.warlord"] = actor.name;
|
||||
actorData["system.warlord_actor_id"] = actor._id;
|
||||
break;
|
||||
|
||||
default:
|
||||
console.warn("L5R5E | AS | Unknown type", type);
|
||||
return;
|
||||
}
|
||||
return this.actor.update(actorData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean ActorId for army sheet
|
||||
* @param {string} type commander|warlord
|
||||
* @return {Promise<abstract.Document>}
|
||||
* @private
|
||||
*/
|
||||
async _removeLinkedActorData(type) {
|
||||
const actorData = {};
|
||||
switch (type) {
|
||||
case "commander":
|
||||
actorData.commander_actor_id = null;
|
||||
break;
|
||||
|
||||
case "warlord":
|
||||
actorData.warlord_actor_id = null;
|
||||
break;
|
||||
|
||||
default:
|
||||
console.warn("L5R5E | AS | Unknown type", type);
|
||||
return;
|
||||
}
|
||||
return this.actor.update({ system: actorData });
|
||||
}
|
||||
|
||||
/**
|
||||
* Add or Subtract Casualties/Panic (+/- buttons)
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
async _modifyCasualtiesOrPanic(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const elmt = $(event.currentTarget);
|
||||
const type = elmt.data("type");
|
||||
let mod = elmt.data("value");
|
||||
if (!mod) {
|
||||
return;
|
||||
}
|
||||
switch (type) {
|
||||
case "casualties":
|
||||
await this.actor.update({
|
||||
system: {
|
||||
battle_readiness: {
|
||||
casualties_strength: {
|
||||
value: Math.max(0, this.actor.system.battle_readiness.casualties_strength.value + mod),
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
break;
|
||||
|
||||
case "panic":
|
||||
await this.actor.update({
|
||||
system: {
|
||||
battle_readiness: {
|
||||
panic_discipline: {
|
||||
value: Math.max(0, this.actor.system.battle_readiness.panic_discipline.value + mod),
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
console.warn("L5R5E | AS | Unsupported type", type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -212,9 +212,10 @@ export class BaseCharacterSheetL5r5e extends BaseSheetL5r5e {
|
||||
switch (itemData.type) {
|
||||
case "army_cohort":
|
||||
case "army_fortification":
|
||||
console.warn("L5R5E | BCS | Army items are not allowed", item?.type, item);
|
||||
// Army item types removed — skip silently
|
||||
return;
|
||||
|
||||
|
||||
case "advancement":
|
||||
// Specific advancements, remove 1 to selected ring/skill
|
||||
await this.actor.addBonus(item);
|
||||
|
||||
@@ -116,6 +116,9 @@ export class CharacterSheetL5r5e extends BaseCharacterSheetL5r5e {
|
||||
sheetData.data.enrichedHtml.identity_text2 = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||
this.actor.system.identity_text2 ?? "", { async: true }
|
||||
);
|
||||
sheetData.data.enrichedHtml.notes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||
this.actor.system.notes ?? "", { async: true }
|
||||
);
|
||||
|
||||
return sheetData;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ export const L5R5E = {
|
||||
assets: "systems/l5rx-chiaroscuro/assets/",
|
||||
templates: "systems/l5rx-chiaroscuro/templates/",
|
||||
},
|
||||
money: [50, 10],
|
||||
money: [100, 10],
|
||||
stances: ["earth", "air", "water", "fire", "void"],
|
||||
roles: ["artisan", "bushi", "courtier", "monk", "sage", "shinobi", "shugenja"],
|
||||
xp: {
|
||||
|
||||
@@ -228,6 +228,7 @@ export class ChiaroscuroDiceDialog extends FormApplication {
|
||||
difficulty: difficultyObj,
|
||||
success,
|
||||
bonus,
|
||||
missBy: success ? 0 : difficultyValue - total,
|
||||
});
|
||||
|
||||
return this.close();
|
||||
@@ -309,7 +310,10 @@ export class ChiaroscuroDiceDialog extends FormApplication {
|
||||
{
|
||||
actor: this._actor,
|
||||
profileImg: this._actor?.img ?? "icons/svg/mystery-man.svg",
|
||||
ring: this.object.ring,
|
||||
ring: {
|
||||
...this.object.ring,
|
||||
label: game.i18n.localize(`l5r5e.rings.${this.object.ring.id}`),
|
||||
},
|
||||
skill: this.object.skill,
|
||||
difficulty: this.object.difficulty,
|
||||
useAspectPoint: this.object.useAspectPoint,
|
||||
|
||||
@@ -1,671 +0,0 @@
|
||||
import { L5r5ePopupManager } from '../misc/l5r5e-popup-manager.js';
|
||||
|
||||
const HandlebarsApplicationMixin = foundry.applications.api.HandlebarsApplicationMixin;
|
||||
const ApplicationV2 = foundry.applications.api.ApplicationV2;
|
||||
|
||||
export class GmMonitor extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
/** @override ApplicationV2 */
|
||||
static get DEFAULT_OPTIONS() {
|
||||
return {
|
||||
id: "l5r5e-gm-monitor",
|
||||
tag: "div",
|
||||
window: {
|
||||
contentClasses: ["l5r5e", "gm-monitor"],
|
||||
title: "l5r5e.gm.monitor.title",
|
||||
minimizable: true,
|
||||
controls: [
|
||||
{
|
||||
label: game.i18n.localize("l5r5e.gm.monitor.add_selected_tokens"),
|
||||
icon: "fas fa-users",
|
||||
action: "add_selected_tokens",
|
||||
},
|
||||
{
|
||||
label: game.i18n.localize("l5r5e.gm.monitor.switch_view"),
|
||||
icon: "fas fa-repeat",
|
||||
action: "change_view_tab"
|
||||
}
|
||||
],
|
||||
resizable: true,
|
||||
editable: true,
|
||||
},
|
||||
position: {
|
||||
width: "600",
|
||||
height: "150"
|
||||
},
|
||||
actions: {
|
||||
add_selected_tokens: GmMonitor.#addSelectedTokens,
|
||||
change_view_tab: GmMonitor.#rotateViewTab,
|
||||
remove_actor: GmMonitor.#removeActor,
|
||||
toggle_prepared: GmMonitor.#togglePrepared,
|
||||
change_stance: {
|
||||
buttons: [0, 2],
|
||||
handler: GmMonitor.#changeStance,
|
||||
},
|
||||
modify_fatigue: {
|
||||
buttons: [0, 1, 2],
|
||||
handler: GmMonitor.#modifyFatigue,
|
||||
},
|
||||
modify_strife: {
|
||||
buttons: [0, 1, 2],
|
||||
handler: GmMonitor.#modifyStrife,
|
||||
},
|
||||
modify_voidPoint: {
|
||||
buttons: [0, 1, 2],
|
||||
handler: GmMonitor.#modifyVoidPoint,
|
||||
},
|
||||
modify_casualties: {
|
||||
buttons: [0, 1, 2],
|
||||
handler: GmMonitor.#modifyCasualties,
|
||||
},
|
||||
modify_panic: {
|
||||
buttons: [0, 1, 2],
|
||||
handler: GmMonitor.#modifyPanic,
|
||||
}
|
||||
},
|
||||
dragDrop: [{ dragSelector: null, dropSelector: null }],
|
||||
}
|
||||
};
|
||||
|
||||
/** @override HandlebarsApplicationMixin */
|
||||
static PARTS = {
|
||||
hidden_tabs: {
|
||||
template: "templates/generic/tab-navigation.hbs"
|
||||
},
|
||||
character: {
|
||||
id: "character",
|
||||
template: "systems/l5rx-chiaroscuro/templates/" + "gm/monitor/character-view.html"
|
||||
},
|
||||
army: {
|
||||
if: "army",
|
||||
template: "systems/l5rx-chiaroscuro/templates/" + "gm/monitor/army-view.html"
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {Record<string, string>}
|
||||
* @override ApplicationV2
|
||||
*/
|
||||
tabGroups = {
|
||||
view: "character"
|
||||
};
|
||||
|
||||
/**
|
||||
* Data that is pushed to html
|
||||
*/
|
||||
context = {
|
||||
actors: []
|
||||
}
|
||||
|
||||
/**
|
||||
* hooks we act upon, saved since we need to remove them when this window is not open
|
||||
*/
|
||||
#hooks = [];
|
||||
|
||||
/**
|
||||
* The DragDrop instance which handles interactivity resulting from DragTransfer events.
|
||||
* @type {DragDrop}
|
||||
*/
|
||||
#dragDrop;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.#initialize();
|
||||
}
|
||||
|
||||
/** @override ApplicationV2 */
|
||||
async _preClose(options) {
|
||||
await super._preClose(options);
|
||||
options.animate = false;
|
||||
|
||||
for (const hook of this.#hooks) {
|
||||
Hooks.off(hook.hook, hook.fn);
|
||||
}
|
||||
}
|
||||
|
||||
/** @override ApplicationV2 */
|
||||
async _onRender(context, options) {
|
||||
await super._onRender(context, options);
|
||||
|
||||
// Todo: Move this to common l5r5e application v2
|
||||
game.l5r5e.HelpersL5r5e.commonListeners($(this.element));
|
||||
|
||||
this.#dragDrop = new foundry.applications.ux.DragDrop.implementation({
|
||||
dragSelector: null,
|
||||
dropSelector: null,
|
||||
callbacks: {
|
||||
drop: this.#onDrop.bind(this)
|
||||
}
|
||||
}).bind(this.element);
|
||||
|
||||
// Tooltips
|
||||
new L5r5ePopupManager(
|
||||
$(this.element).find(".actor-infos-control"),
|
||||
async (event) => {
|
||||
const type = $(event.currentTarget).data("type");
|
||||
if (!type) return;
|
||||
|
||||
if (type === "text") {
|
||||
return $(event.currentTarget).data("text");
|
||||
}
|
||||
|
||||
const uuid = $(event.currentTarget).data("actor-uuid");
|
||||
if (!uuid) return;
|
||||
|
||||
const actor = this.context.actors.find(actor => actor.uuid === uuid);
|
||||
if (!actor) return;
|
||||
|
||||
switch (type) {
|
||||
case "armors":
|
||||
return this.#getTooltipArmors(actor);
|
||||
case "weapons":
|
||||
return this.#getTooltipWeapons(actor);
|
||||
case "global":
|
||||
return actor.isArmy
|
||||
? this.#getTooltipArmiesGlobal(actor)
|
||||
: this.#getTooltipGlobal(actor);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Apply global interface theme if it is set
|
||||
if (!this.options.classes.includes("themed")) {
|
||||
this.element.classList.remove("theme-light", "theme-dark");
|
||||
const {colorScheme} = game.settings.get("core", "uiConfig");
|
||||
if (colorScheme.interface) {
|
||||
this.element.classList.add("themed", `theme-${colorScheme.interface}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @override ApplicationV2 */
|
||||
async _prepareContext() {
|
||||
return {
|
||||
tabs: this.getTabs(),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} partId The part being rendered
|
||||
* @param {ApplicationRenderContext} context Shared context provided by _prepareContext
|
||||
* @returns {Promise<ApplicationRenderContext>} Context data for a specific part
|
||||
*
|
||||
* @override HandlebarsApplicationMixin
|
||||
*/
|
||||
async _preparePartContext(partId, context) {
|
||||
switch (partId) {
|
||||
case "character":
|
||||
context.characters = this.context.actors.filter((actor) => !actor.isArmy);
|
||||
break;
|
||||
case "army":
|
||||
context.armies = this.context.actors.filter((actor) => actor.isArmy);
|
||||
break;
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare an array of form header tabs.
|
||||
* @returns {Record<string, Partial<ApplicationTab>>}
|
||||
*/
|
||||
getTabs() {
|
||||
const tabs = {
|
||||
character: { id: "character", group: "view", icon: "fa-solid fa-tag", label: "REGION.SECTIONS.identity" },
|
||||
army: { id: "army", group: "view", icon: "fa-solid fa-shapes", label: "REGION.SECTIONS.shapes" },
|
||||
}
|
||||
for (const v of Object.values(tabs)) {
|
||||
v.active = this.tabGroups[v.group] === v.id;
|
||||
v.cssClass = v.active ? "active" : "";
|
||||
}
|
||||
return tabs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle dropped data on the Actor sheet
|
||||
* @param {DragEvent} event The originating DragEvent
|
||||
*/
|
||||
async #onDrop(event) {
|
||||
|
||||
if (!this.options.window.editable) {
|
||||
return;
|
||||
}
|
||||
|
||||
const json = event.dataTransfer.getData("text/plain");
|
||||
if (!json) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = JSON.parse(json);
|
||||
if (!data || data.type !== "Actor" || !data.uuid || !!this.context.actors.find((a) => a.uuid === data.uuid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const actor = fromUuidSync(data.uuid);
|
||||
if (!actor) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Switch view to current character type
|
||||
if (actor.isArmy) {
|
||||
this.changeTab("army", "view");
|
||||
}
|
||||
else {
|
||||
this.changeTab("character", "view");
|
||||
}
|
||||
|
||||
this.context.actors.push(actor);
|
||||
|
||||
return this.saveActorsIds();
|
||||
}
|
||||
|
||||
/** required for updating via our socket implementation game.l5r5e.HelpersL5r5e.refreshLocalAndSocket("l5r5e-gm-monitor")*/
|
||||
async refresh() {
|
||||
this.render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the actors ids in setting
|
||||
* @private
|
||||
*/
|
||||
async saveActorsIds() {
|
||||
return game.settings.set(
|
||||
CONFIG.l5r5e.namespace,
|
||||
"gm-monitor-actors",
|
||||
this.context.actors.map((a) => a.uuid)
|
||||
);
|
||||
}
|
||||
|
||||
#initialize() {
|
||||
let actors;
|
||||
const uuidList = game.settings.get(CONFIG.l5r5e.namespace, "gm-monitor-actors");
|
||||
if (uuidList.length > 0) {
|
||||
// Get actors from stored uuids
|
||||
actors = uuidList
|
||||
.map(uuid => {
|
||||
const doc = fromUuidSync(uuid);
|
||||
if (doc instanceof TokenDocument) {
|
||||
return doc.actor;
|
||||
}
|
||||
return doc;
|
||||
})
|
||||
.filter(actor => !!actor); // skip null
|
||||
|
||||
} else {
|
||||
// If empty add pc with owner
|
||||
actors = game.actors.filter((actor) => actor.type === "character" && actor.hasPlayerOwnerActive);
|
||||
this.saveActorsIds();
|
||||
}
|
||||
|
||||
// Sort by name asc
|
||||
actors.sort((a, b) => {
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
|
||||
this.context.actors = actors;
|
||||
|
||||
this.#hooks.push({
|
||||
hook: "updateActor",
|
||||
fn: Hooks.on("updateActor", (actor) => this.#onUpdateActor(actor))
|
||||
});
|
||||
this.#hooks.push({
|
||||
hook: "updateSetting",
|
||||
fn: Hooks.on("updateSetting", (actor) => this.#onUpdateSetting(actor))
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch between the available views in sequence
|
||||
*/
|
||||
static #rotateViewTab() {
|
||||
const tabArray = Object.values(this.getTabs());
|
||||
const activeTabIndex = tabArray.findIndex((tab) => tab.active);
|
||||
const nextTabIndex = activeTabIndex + 1 < tabArray.length ? activeTabIndex + 1 : 0;
|
||||
this.changeTab(tabArray[nextTabIndex].id, tabArray[nextTabIndex].group)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add selected token on monitor if not already present
|
||||
*/
|
||||
static #addSelectedTokens() {
|
||||
if (canvas.tokens.controlled.length > 0) {
|
||||
const actors2Add = canvas.tokens.controlled
|
||||
.map(t => t.actor)
|
||||
.filter(t => !!t && !this.context.actors.find((a) => a.uuid === t.uuid));
|
||||
|
||||
if (actors2Add.length < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.context.actors = [
|
||||
...this.context.actors,
|
||||
...actors2Add
|
||||
];
|
||||
this.saveActorsIds();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update baseValue based on the type of event
|
||||
* @param {Int} baseValue The Base value we can to modify
|
||||
* @param {Int} whichButton The type of click made
|
||||
*/
|
||||
static #newValue(baseValue, whichButton) {
|
||||
switch (whichButton) {
|
||||
case 0: //Left click
|
||||
return Math.max(0, baseValue + 1);
|
||||
case 1: //Middle click
|
||||
return 0;
|
||||
case 2: //Right click
|
||||
return Math.max(0, baseValue - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} target Html target to get actor information from
|
||||
*/
|
||||
static async #getActorValidated(target) {
|
||||
const uuid = $(target).data("actor-uuid");
|
||||
if (!uuid) {
|
||||
console.warn("L5R5E | GMM | actor uuid not set", type);
|
||||
return {isValid: false, actor: null};
|
||||
}
|
||||
const actor = await fromUuid(uuid);
|
||||
if (!actor) {
|
||||
console.warn("L5R5E | GMM | Actor not found", type);
|
||||
return {isValid: false, actor: null};
|
||||
}
|
||||
return {isValid:true, actor: actor};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PointerEvent} event The originating click event
|
||||
* @param {HTMLElement} target The capturing HTML element which defined a [data-action]
|
||||
*/
|
||||
static async #modifyCasualties(event, target) {
|
||||
const {isValid, actor} = await GmMonitor.#getActorValidated(target);
|
||||
if (!isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const casualties_strength = actor.system.battle_readiness.casualties_strength.value;
|
||||
return actor.update({
|
||||
system: {
|
||||
battle_readiness: {
|
||||
casualties_strength: {
|
||||
value: GmMonitor.#newValue(casualties_strength, event.button),
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PointerEvent} event The originating click event
|
||||
* @param {HTMLElement} target The capturing HTML element which defined a [data-action]
|
||||
*/
|
||||
static async #modifyPanic(event, target) {
|
||||
const {isValid, actor} = await GmMonitor.#getActorValidated(target);
|
||||
if (!isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const panic_discipline = actor.system.battle_readiness.panic_discipline.value;
|
||||
return actor.update({
|
||||
system: {
|
||||
battle_readiness: {
|
||||
panic_discipline: {
|
||||
value: GmMonitor.#newValue(panic_discipline, event.button),
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PointerEvent} event The originating click event
|
||||
* @param {HTMLElement} target The capturing HTML element which defined a [data-action]
|
||||
*/
|
||||
static async #togglePrepared(event, target) {
|
||||
const {isValid, actor} = await GmMonitor.#getActorValidated(target);
|
||||
if (!isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
return actor.update({
|
||||
system: {
|
||||
prepared: !actor.system.prepared
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PointerEvent} event The originating click event
|
||||
* @param {HTMLElement} target The capturing HTML element which defined a [data-action]
|
||||
*/
|
||||
static async #changeStance(event, target) {
|
||||
const {isValid, actor} = await GmMonitor.#getActorValidated(target);
|
||||
if (!isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
let stanceIdx = CONFIG.l5r5e.stances.findIndex((stance) => stance === actor.system.stance) + (event.button === 0 ? 1 : -1);
|
||||
if (stanceIdx < 0) {
|
||||
stanceIdx = CONFIG.l5r5e.stances.length - 1;
|
||||
} else if (stanceIdx > CONFIG.l5r5e.stances.length - 1) {
|
||||
stanceIdx = 0;
|
||||
}
|
||||
|
||||
return actor.update({
|
||||
system: {
|
||||
stance: CONFIG.l5r5e.stances[stanceIdx]
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PointerEvent} event The originating click event
|
||||
* @param {HTMLElement} target The capturing HTML element which defined a [data-action]
|
||||
*/
|
||||
static async #modifyFatigue(event, target) {
|
||||
const {isValid, actor} = await GmMonitor.#getActorValidated(target);
|
||||
if (!isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fatigue = actor.system.fatigue.value;
|
||||
return actor.update({
|
||||
system: {
|
||||
fatigue: {
|
||||
value: GmMonitor.#newValue(fatigue, event.button)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PointerEvent} event The originating click event
|
||||
* @param {HTMLElement} target The capturing HTML element which defined a [data-action]
|
||||
*/
|
||||
static async #modifyStrife(event, target) {
|
||||
const {isValid, actor} = await GmMonitor.#getActorValidated(target);
|
||||
if (!isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const strife = actor.system.strife.value;
|
||||
return actor.update({
|
||||
system: {
|
||||
strife: {
|
||||
value: GmMonitor.#newValue(strife, event.button),
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PointerEvent} event The originating click event
|
||||
* @param {HTMLElement} target The capturing HTML element which defined a [data-action]
|
||||
*/
|
||||
static async #modifyVoidPoint(event, target) {
|
||||
const {isValid, actor} = await GmMonitor.#getActorValidated(target);
|
||||
if (!isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const void_points = actor.system.void_points.value;
|
||||
const void_points_max = actor.system.void_points.max;
|
||||
return actor.update({
|
||||
system: {
|
||||
void_points: {
|
||||
value: Math.min(
|
||||
void_points_max,
|
||||
GmMonitor.#newValue(void_points, event.button)
|
||||
),
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PointerEvent} event The originating click event
|
||||
* @param {HTMLElement} target The capturing HTML element which defined a [data-action]
|
||||
*/
|
||||
static async #removeActor(event, target) {
|
||||
const uuid = $(target).data("actor-uuid");
|
||||
if (!uuid) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.context.actors = this.context.actors.filter((actor) => actor.uuid !== uuid);
|
||||
return this.saveActorsIds();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get armors information for this actor
|
||||
* @param {ActorL5r5e} actor
|
||||
* @return {string}
|
||||
* @private
|
||||
*/
|
||||
async #getTooltipArmors(actor) {
|
||||
// Equipped Armors
|
||||
const armors = actor.items
|
||||
.filter((item) => item.type === "armor" && item.system.equipped)
|
||||
.map(
|
||||
(item) =>
|
||||
item.name +
|
||||
` (<i class="fas fa-tint">${item.system.armor.physical}</i>` +
|
||||
` / <i class="fas fa-bolt">${item.system.armor.supernatural}</i>)`
|
||||
);
|
||||
|
||||
// *** Template ***
|
||||
return foundry.applications.handlebars.renderTemplate(`${CONFIG.l5r5e.paths.templates}gm/monitor/tooltips/armors.html`, {
|
||||
armors,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get weapons information for this actor
|
||||
* @param {ActorL5r5e} actor
|
||||
* @return {string}
|
||||
* @private
|
||||
*/
|
||||
async #getTooltipWeapons(actor) {
|
||||
const display = (weapon) => {
|
||||
return (
|
||||
weapon.name +
|
||||
` (<i class="fas fa-arrows-alt-h"> ${weapon.system.range}</i>` +
|
||||
` / <i class="fas fa-tint"> ${weapon.system.damage}</i>` +
|
||||
` / <i class="fas fa-skull"> ${weapon.system.deadliness}</i>)`
|
||||
);
|
||||
};
|
||||
|
||||
// Readied Weapons
|
||||
const equippedWeapons = actor.items.filter((item) => item.type === "weapon" && item.system.equipped);
|
||||
|
||||
const readied = equippedWeapons
|
||||
.filter((weapon) => !!weapon.system.readied)
|
||||
.map((weapon) => display(weapon));
|
||||
|
||||
// Equipped Weapons
|
||||
const sheathed = equippedWeapons
|
||||
.filter((weapon) => !weapon.system.readied)
|
||||
.map((weapon) => display(weapon));
|
||||
|
||||
// *** Template ***
|
||||
return foundry.applications.handlebars.renderTemplate(`${CONFIG.l5r5e.paths.templates}gm/monitor/tooltips/weapons.html`, {
|
||||
readied,
|
||||
sheathed,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tooltips information for this character
|
||||
* @param {ActorL5r5e} actor
|
||||
* @return {string}
|
||||
* @private
|
||||
*/
|
||||
async #getTooltipGlobal(actor) {
|
||||
const actorData = (await actor.sheet?.getData()?.data) || actor;
|
||||
|
||||
// Peculiarities
|
||||
const Peculiarities = actor.items.filter((e) => e.type === "peculiarity");
|
||||
const advantages = Peculiarities
|
||||
.filter((item) => ["distinction", "passion"].includes(item.system.peculiarity_type))
|
||||
.map((item) => item.name)
|
||||
.join(", ");
|
||||
const disadvantages = Peculiarities
|
||||
.filter((item) => ["adversity", "anxiety"].includes(item.system.peculiarity_type))
|
||||
.map((item) => item.name)
|
||||
.join(", ");
|
||||
|
||||
// *** Template ***
|
||||
return foundry.applications.handlebars.renderTemplate(`${CONFIG.l5r5e.paths.templates}gm/monitor/tooltips/global.html`, {
|
||||
actorData: actorData,
|
||||
advantages: advantages,
|
||||
disadvantages: disadvantages,
|
||||
suffix: actorData.system.template === "pow" ? "_pow" : "",
|
||||
actor_type: actor.type,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tooltips information for this army
|
||||
* @param {ActorL5r5e} actor
|
||||
* @return {string}
|
||||
* @private
|
||||
*/
|
||||
async #getTooltipArmiesGlobal(actor) {
|
||||
const actorData = (await actor.sheet?.getData()?.data) || actor;
|
||||
|
||||
// *** Template ***
|
||||
return foundry.applications.handlebars.renderTemplate(`${CONFIG.l5r5e.paths.templates}gm/monitor/tooltips/global-armies.html`, {
|
||||
actorData: actorData,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ActorL5r5e} actor The actor that is being updated
|
||||
*/
|
||||
#onUpdateActor(actor) {
|
||||
if (this.context.actors.includes(actor)) {
|
||||
this.render(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Setting} setting The setting that is being updated
|
||||
*/
|
||||
#onUpdateSetting(setting) {
|
||||
switch (setting.key) {
|
||||
case "l5r5e.gm-monitor-actors":
|
||||
this.render(false);
|
||||
break;
|
||||
case "l5r5e.initiative-prepared-character":
|
||||
case "l5r5e.initiative-prepared-adversary":
|
||||
case "l5r5e.initiative-prepared-minion":
|
||||
this.render(false);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
const HandlebarsApplicationMixin = foundry.applications.api.HandlebarsApplicationMixin;
|
||||
const ApplicationV2 = foundry.applications.api.ApplicationV2;
|
||||
|
||||
export class GmToolbox extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
/** @override ApplicationV2 */
|
||||
static get DEFAULT_OPTIONS() { return {
|
||||
id: "l5r5e-gm-toolbox",
|
||||
classes: ["faded-ui"],
|
||||
window: {
|
||||
contentClasses: ["l5r5e", "gm-toolbox"],
|
||||
title: "l5r5e.gm.toolbox.title",
|
||||
minimizable: false,
|
||||
},
|
||||
position: {
|
||||
width: "auto",
|
||||
height: "auto"
|
||||
},
|
||||
actions: {
|
||||
open_gm_monitor: GmToolbox.#openGmMonitor,
|
||||
toggle_hide_difficulty: GmToolbox.#onToggleHideDifficulty,
|
||||
// Buttons map (0: left, 1: middle, 2: right, 3: extra 1, 4: extra 2)
|
||||
// Foundry v13 use middle (1) for popup and currently not bind it for custom
|
||||
// See : https://github.com/foundryvtt/foundryvtt/issues/12531
|
||||
change_difficulty: {
|
||||
buttons: [0, 1, 2],
|
||||
handler: GmToolbox.#onChangeDifficulty
|
||||
},
|
||||
reset_void: {
|
||||
buttons: [0, 1, 2, 3, 4],
|
||||
handler: GmToolbox.#onResetVoid
|
||||
},
|
||||
sleep: {
|
||||
buttons: [0, 1, 2, 3, 4],
|
||||
handler: GmToolbox.#onSleep
|
||||
},
|
||||
scene_end: {
|
||||
buttons: [0, 1, 2, 3, 4],
|
||||
handler: GmToolbox.#onSceneEnd
|
||||
},
|
||||
}
|
||||
}};
|
||||
|
||||
/** @override HandlebarsApplicationMixin */
|
||||
static PARTS = {
|
||||
main: {
|
||||
id: "gm-tool-content",
|
||||
template: "systems/l5rx-chiaroscuro/templates/" + "gm/gm-toolbox.html"
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* hooks we act upon, saved since we need to remove them when this window is not open
|
||||
*/
|
||||
#hooks = [];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.#hooks.push({
|
||||
hook: "updateSetting",
|
||||
fn: Hooks.on("updateSetting", (setting) => this.#onUpdateSetting(setting))
|
||||
});
|
||||
}
|
||||
|
||||
/** @override ApplicationV2*/
|
||||
async _prepareContext() {
|
||||
return {
|
||||
difficulty: game.settings.get(CONFIG.l5r5e.namespace, "initiative-difficulty-value"),
|
||||
difficultyHidden: game.settings.get(CONFIG.l5r5e.namespace, "initiative-difficulty-hidden"),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The ApplicationV2 always adds the close button so just remove it when redering the frame
|
||||
* @override ApplicationV2
|
||||
*/
|
||||
async _renderFrame(options) {
|
||||
const frame = await super._renderFrame(options);
|
||||
$(frame).find('button[data-action="close"]').remove();
|
||||
return frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* The ApplicationV2 always adds the close button so just remove it when redering the frame
|
||||
* @override ApplicationV2
|
||||
*/
|
||||
_onFirstRender(context, options) {
|
||||
//const x = $(window).width();
|
||||
const y = $(window).height();
|
||||
options.position.top = y - 220;
|
||||
options.position.left = 220; //x - 630;
|
||||
}
|
||||
|
||||
async _onRender(context, options) {
|
||||
await super._onRender(context, options);
|
||||
|
||||
if (this.options.classes.includes("themed")) {
|
||||
return;
|
||||
}
|
||||
this.element.classList.remove("theme-light", "theme-dark");
|
||||
const {colorScheme} = game.settings.get("core", "uiConfig");
|
||||
if (colorScheme.interface) {
|
||||
this.element.classList.add("themed", `theme-${colorScheme.interface}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The GM Toolbox should not be removed when toggling the main menu with the esc key etc.
|
||||
* @override ApplicationV2
|
||||
*/
|
||||
async close(options) {
|
||||
return Promise.resolve(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh data (used from socket)
|
||||
*/
|
||||
async refresh() {
|
||||
if (!game.user.isGM) {
|
||||
return;
|
||||
}
|
||||
this.render(false);
|
||||
}
|
||||
|
||||
static #openGmMonitor() {
|
||||
const app = foundry.applications.instances.get("l5r5e-gm-monitor")
|
||||
if (app) {
|
||||
app.close();
|
||||
} else {
|
||||
new game.l5r5e.GmMonitor().render(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PointerEvent} event The originating click event
|
||||
*/
|
||||
static #onChangeDifficulty(event) {
|
||||
let difficulty = game.settings.get(CONFIG.l5r5e.namespace, "initiative-difficulty-value");
|
||||
switch (event.button) {
|
||||
case 0: // left click
|
||||
difficulty = Math.min(9, difficulty + 1);
|
||||
break;
|
||||
case 1: // middle click
|
||||
difficulty = 2;
|
||||
break;
|
||||
case 2: // right click
|
||||
difficulty = Math.max(0, difficulty - 1);
|
||||
break;
|
||||
}
|
||||
game.settings.set(CONFIG.l5r5e.namespace, "initiative-difficulty-value", difficulty);
|
||||
}
|
||||
|
||||
static #onToggleHideDifficulty() {
|
||||
const hiddenSetting = game.settings.get(CONFIG.l5r5e.namespace, "initiative-difficulty-hidden")
|
||||
game.settings.set(CONFIG.l5r5e.namespace, "initiative-difficulty-hidden", !hiddenSetting);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Boolean} allActors
|
||||
* @param {ActorL5r5e} actor
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
static #updatableCharacter(allActors, actor) {
|
||||
if (!actor.isCharacterType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (allActors) {
|
||||
return true;
|
||||
}
|
||||
return actor.isCharacter && actor.hasPlayerOwnerActive
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Boolean} allActors
|
||||
* @param {String} type
|
||||
*/
|
||||
static #uiNotification(allActors, type) {
|
||||
ui.notifications.info(
|
||||
` <i class="fas fa-user${allActors ? "s" : ""}"></i> ` + game.i18n.localize(`l5r5e.gm.toolbox.${type}_info`)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PointerEvent} event The originating click event
|
||||
*/
|
||||
static async #onResetVoid(event) {
|
||||
const allActors = event.button !== 0;
|
||||
for await (const actor of game.actors.contents) {
|
||||
if (!GmToolbox.#updatableCharacter(allActors, actor)) {
|
||||
continue;
|
||||
}
|
||||
await actor.update({
|
||||
system: {
|
||||
void_points: {
|
||||
value: Math.ceil(actor.system.void_points.max / 2),
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
GmToolbox.#uiNotification(allActors, "reset_void");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PointerEvent} event The originating click event
|
||||
*/
|
||||
static async #onSleep(event) {
|
||||
const allActors = event.button !== 0;
|
||||
for await (const actor of game.actors.contents) {
|
||||
if (!GmToolbox.#updatableCharacter(allActors, actor)) {
|
||||
continue;
|
||||
}
|
||||
await actor.update({
|
||||
system: {
|
||||
fatigue: {
|
||||
value: Math.max(0,
|
||||
actor.system.fatigue.value - Math.ceil(actor.system.rings.water * 2)
|
||||
),
|
||||
}
|
||||
},
|
||||
});
|
||||
await actor.removeConditions(new Set(["exhausted"]));
|
||||
}
|
||||
|
||||
GmToolbox.#uiNotification(allActors, "sleep");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PointerEvent} event The originating click event
|
||||
*/
|
||||
static async #onSceneEnd(event) {
|
||||
const allActors = event.button !== 0;
|
||||
for await (const actor of game.actors.contents) {
|
||||
if (!GmToolbox.#updatableCharacter(allActors, actor)
|
||||
|| actor.statuses.has("exhausted")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
await actor.update({
|
||||
system: {
|
||||
fatigue: {
|
||||
value: Math.min(
|
||||
actor.system.fatigue.value,
|
||||
Math.ceil(actor.system.fatigue.max / 2)
|
||||
)
|
||||
},
|
||||
strife: {
|
||||
value: Math.min(
|
||||
actor.system.strife.value,
|
||||
Math.ceil(actor.system.strife.max / 2)
|
||||
)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
GmToolbox.#uiNotification(allActors, "scene_end");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Setting} setting The setting that is being updated
|
||||
*/
|
||||
async #onUpdateSetting(setting) {
|
||||
switch (setting.key) {
|
||||
case "l5r5e.initiative-difficulty-value":
|
||||
case "l5r5e.initiative-difficulty-hidden":
|
||||
this.render(false);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,11 +57,6 @@ export default class HooksL5r5e {
|
||||
// For some reason, not always really ready, so wait a little
|
||||
await new Promise((r) => setTimeout(r, 2000));
|
||||
|
||||
// Settings TN and EncounterType
|
||||
if (game.user.isGM) {
|
||||
new game.l5r5e.GmToolbox().render(true);
|
||||
}
|
||||
|
||||
// ***** UI *****
|
||||
// If any disclaimer "not translated by Edge"
|
||||
const disclaimer = game.i18n.localize("l5r5e.global.edge_translation_disclaimer");
|
||||
|
||||
@@ -10,6 +10,8 @@ export class AdvancementSheetL5r5e extends ItemSheetL5r5e {
|
||||
static types = [
|
||||
{ id: "ring", label: "l5r5e.rings.label" },
|
||||
{ id: "skill", label: "l5r5e.skills.label" },
|
||||
{ id: "arcane", label: "l5r5e.chiaroscuro.arcane.label" },
|
||||
{ id: "mot_invocation", label: "l5r5e.chiaroscuro.technique.mot_invocation" },
|
||||
// others have theirs own xp count
|
||||
];
|
||||
|
||||
@@ -27,6 +29,12 @@ export class AdvancementSheetL5r5e extends ItemSheetL5r5e {
|
||||
sheetData.data.subTypesList = AdvancementSheetL5r5e.types;
|
||||
sheetData.data.skillsList = game.l5r5e.HelpersL5r5e.getSkillsList(true);
|
||||
|
||||
// Invocation sub-types (Général / Neutre / Précis)
|
||||
const invTypes = game.l5r5e.HelpersL5r5e.getLocalizedRawObject("l5r5e.chiaroscuro.technique.invocation_types") ?? {};
|
||||
sheetData.data.invocationTypesList = [{ id: "", label: "—" }].concat(
|
||||
Object.entries(invTypes).map(([id, label]) => ({ id, label }))
|
||||
);
|
||||
|
||||
return sheetData;
|
||||
}
|
||||
|
||||
@@ -49,15 +57,22 @@ export class AdvancementSheetL5r5e extends ItemSheetL5r5e {
|
||||
html.find("#advancement_type").on("change", (event) => {
|
||||
const targetEvt = $(event.target);
|
||||
targetEvt.prop("disabled", true);
|
||||
const val = targetEvt.val();
|
||||
|
||||
if (targetEvt.val() === "skill") {
|
||||
if (val === "skill") {
|
||||
this._updateChoice({ ring: currentRing }, { skill: currentSkill }).then(
|
||||
targetEvt.prop("disabled", false)
|
||||
);
|
||||
} else if (targetEvt.val() === "ring") {
|
||||
} else if (val === "ring") {
|
||||
this._updateChoice({ skill: currentSkill }, { ring: currentRing }).then(
|
||||
targetEvt.prop("disabled", false)
|
||||
);
|
||||
} else {
|
||||
// arcane or mot_invocation: just save the type, no auto-calc
|
||||
this.object.update({ system: { advancement_type: val } }).then(() => {
|
||||
targetEvt.prop("disabled", false);
|
||||
this.render(true);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -10,7 +10,8 @@ export class ArcaneSheetL5r5e extends BaseItemSheetL5r5e {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ["l5r5e", "sheet", "arcane"],
|
||||
template: CONFIG.l5r5e.paths.templates + "items/arcane/arcane-sheet.html",
|
||||
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "attributes" }],
|
||||
width: 500,
|
||||
height: 400,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
import { ItemSheetL5r5e } from "./item-sheet.js";
|
||||
|
||||
/**
|
||||
* @extends {ItemSheetL5r5e}
|
||||
*/
|
||||
export class ArmyCohortSheetL5r5e extends ItemSheetL5r5e {
|
||||
/** @override */
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ["l5r5e", "sheet", "army-cohort"],
|
||||
template: CONFIG.l5r5e.paths.templates + "items/army-cohort/army-cohort-sheet.html",
|
||||
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "infos" }],
|
||||
dragDrop: [{ dragSelector: ".item", dropSelector: null }],
|
||||
});
|
||||
}
|
||||
|
||||
/** @override */
|
||||
constructor(options = {}) {
|
||||
super(options);
|
||||
this._initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize once
|
||||
* @private
|
||||
*/
|
||||
_initialize() {
|
||||
const data = this.object.system;
|
||||
|
||||
// update linked actor datas
|
||||
if (data.leader_actor_id) {
|
||||
const actor = game.actors.get(data.leader_actor_id);
|
||||
if (actor) {
|
||||
this._updateLinkedActorData(actor);
|
||||
} else {
|
||||
this._removeLinkedActor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Object|Promise}
|
||||
*/
|
||||
async getData(options = {}) {
|
||||
const sheetData = await super.getData(options);
|
||||
|
||||
// Editors enrichment
|
||||
sheetData.data.enrichedHtml.abilities = await foundry.applications.ux.TextEditor.implementation.enrichHTML(sheetData.data.system.abilities, {
|
||||
async: true,
|
||||
});
|
||||
|
||||
return sheetData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate a named TinyMCE text editor
|
||||
* @param {string} name The named data field which the editor modifies.
|
||||
* @param {object} options TinyMCE initialization options passed to TextEditor.create
|
||||
* @param {string} initialContent Initial text content for the editor area.
|
||||
* @override
|
||||
*/
|
||||
activateEditor(name, options = {}, initialContent = "") {
|
||||
// Symbols Compatibility with old compendium modules (PRE l5r v1.7.2)
|
||||
if (name === "system.abilities" && initialContent) {
|
||||
initialContent = game.l5r5e.HelpersL5r5e.convertSymbols(initialContent, false);
|
||||
}
|
||||
return super.activateEditor(name, options, initialContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to events from the sheet.
|
||||
* @param {jQuery} html HTML content of the sheet.
|
||||
*/
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
|
||||
// *** Everything below here is only needed if the sheet is editable ***
|
||||
if (!this.isEditable) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete the linked Actor
|
||||
html.find(".actor-remove-control").on("click", (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this._removeLinkedActor();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle dropped Item data on the Actor sheet (cohort, fortification)
|
||||
* @param {DragEvent} event
|
||||
*/
|
||||
async _onDrop(event) {
|
||||
// *** Everything below here is only needed if the sheet is editable ***
|
||||
if (!this.isEditable) {
|
||||
return;
|
||||
}
|
||||
|
||||
const droppedActor = await game.l5r5e.HelpersL5r5e.getDragnDropTargetObject(event);
|
||||
return this._updateLinkedActorData(droppedActor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update actor datas for this army sheet
|
||||
* @param {ActorL5r5e} actor actor object
|
||||
* @return {Promise<abstract.Document>}
|
||||
* @private
|
||||
*/
|
||||
async _updateLinkedActorData(actor) {
|
||||
if (!actor || actor.documentName !== "Actor" || !actor.isCharacterType) {
|
||||
console.warn("L5R5E | Army Cohort | Wrong actor type", actor?.type, actor);
|
||||
return;
|
||||
}
|
||||
|
||||
return this.object.update({
|
||||
img: actor.img,
|
||||
system: {
|
||||
leader: actor.name,
|
||||
leader_actor_id: actor._id,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the linked actor (commander/warlord)
|
||||
* @return {Promise<void>}
|
||||
* @private
|
||||
*/
|
||||
async _removeLinkedActor() {
|
||||
return this.object.update({
|
||||
system: {
|
||||
leader_actor_id: null,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import { ItemSheetL5r5e } from "./item-sheet.js";
|
||||
|
||||
/**
|
||||
* @extends {ItemSheetL5r5e}
|
||||
*/
|
||||
export class ArmyFortificationSheetL5r5e extends ItemSheetL5r5e {
|
||||
/** @override */
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ["l5r5e", "sheet", "army-fortification"],
|
||||
template: CONFIG.l5r5e.paths.templates + "items/army-fortification/army-fortification-sheet.html",
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import { ItemSheetL5r5e } from "./item-sheet.js";
|
||||
|
||||
/**
|
||||
* @extends {ItemSheet}
|
||||
*/
|
||||
export class BondSheetL5r5e extends ItemSheetL5r5e {
|
||||
/** @override */
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ["l5r5e", "sheet", "bond"],
|
||||
template: CONFIG.l5r5e.paths.templates + "items/bond/bond-sheet.html",
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
import { ItemSheetL5r5e } from "./item-sheet.js";
|
||||
|
||||
/**
|
||||
* @extends {ItemSheet}
|
||||
*/
|
||||
export class ItemPatternSheetL5r5e extends ItemSheetL5r5e {
|
||||
/** @override */
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ["l5r5e", "sheet", "item-pattern"],
|
||||
template: CONFIG.l5r5e.paths.templates + "items/item-pattern/item-pattern-sheet.html",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Object|Promise}
|
||||
*/
|
||||
async getData(options = {}) {
|
||||
const sheetData = await super.getData(options);
|
||||
|
||||
// Linked Property
|
||||
sheetData.data.linkedProperty = await this.getLinkedProperty(sheetData);
|
||||
|
||||
return sheetData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the linked property name
|
||||
* @param sheetData
|
||||
* @return {Promise<null|{name, id}>}
|
||||
*/
|
||||
async getLinkedProperty(sheetData) {
|
||||
if (sheetData.data.system.linked_property_id) {
|
||||
const linkedProperty = await game.l5r5e.HelpersL5r5e.getObjectGameOrPack({
|
||||
id: sheetData.data.system.linked_property_id,
|
||||
type: "Item",
|
||||
});
|
||||
if (linkedProperty) {
|
||||
return {
|
||||
id: linkedProperty._id,
|
||||
name: linkedProperty.name,
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to events from the sheet.
|
||||
* @param {jQuery} html HTML content of the sheet.
|
||||
*/
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
|
||||
// Everything below here is only needed if the sheet is editable
|
||||
if (!this.isEditable) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete the linked property
|
||||
html.find(`.linked-property-delete`).on("click", this._deleteLinkedProperty.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback actions which occur when a dragged element is dropped on a target.
|
||||
* @param {DragEvent} event The originating DragEvent
|
||||
* @private
|
||||
*/
|
||||
async _onDrop(event) {
|
||||
// Everything below here is only needed if the sheet is editable
|
||||
if (!this.isEditable) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only property allowed here
|
||||
let item = await game.l5r5e.HelpersL5r5e.getDragnDropTargetObject(event);
|
||||
if (!item || item.documentName !== "Item" || item.type !== "property") {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the new property, and update
|
||||
this.document.system.linked_property_id = item.id;
|
||||
this.document.update({
|
||||
system: {
|
||||
linked_property_id: this.document.system.linked_property_id,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the link to a property for the current item
|
||||
* @param {Event} event
|
||||
* @return {Promise<void>}
|
||||
* @private
|
||||
*/
|
||||
async _deleteLinkedProperty(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
let name;
|
||||
const linkedProperty = await game.l5r5e.HelpersL5r5e.getObjectGameOrPack({
|
||||
id: this.document.system.linked_property_id,
|
||||
type: "Item",
|
||||
});
|
||||
if (linkedProperty) {
|
||||
name = linkedProperty.name;
|
||||
}
|
||||
|
||||
const callback = async () => {
|
||||
this.document.system.linked_property_id = null;
|
||||
this.document.update({
|
||||
system: {
|
||||
linked_property_id: this.document.system.linked_property_id,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Holing Ctrl = without confirm
|
||||
if (event.ctrlKey || !name) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
game.l5r5e.HelpersL5r5e.confirmDeleteDialog(
|
||||
game.i18n.format("l5r5e.global.delete_confirm", { name }),
|
||||
callback
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -108,19 +108,12 @@ export class ItemSheetL5r5e extends BaseItemSheetL5r5e {
|
||||
}
|
||||
|
||||
// If we are a property, the child id need to be different to parent
|
||||
// (property type removed — guard kept for legacy data safety)
|
||||
if (this.item.type === "property" && this.item.id === item._id) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Specific ItemPattern's drop, get the associated props instead
|
||||
if (item.type === "item_pattern" && item.system.linked_property_id) {
|
||||
item = await game.l5r5e.HelpersL5r5e.getObjectGameOrPack({
|
||||
id: item.system.linked_property_id,
|
||||
type: "Item",
|
||||
});
|
||||
}
|
||||
|
||||
// Final object has to be a property
|
||||
// Final object has to be a property (type removed — no more drops possible)
|
||||
if (item.type !== "property") {
|
||||
return;
|
||||
}
|
||||
|
||||
59
system/scripts/items/mot-invocation-sheet.js
Normal file
59
system/scripts/items/mot-invocation-sheet.js
Normal file
@@ -0,0 +1,59 @@
|
||||
import { BaseItemSheetL5r5e } from "./base-item-sheet.js";
|
||||
|
||||
/** Mode Invocation values per invocation type */
|
||||
const INVOCATION_MODE = {
|
||||
general: 3,
|
||||
neutre: 0,
|
||||
precis: -3,
|
||||
};
|
||||
|
||||
/**
|
||||
* Sheet for Mot d'Invocation items (Chiaroscuro).
|
||||
* @extends {BaseItemSheetL5r5e}
|
||||
*/
|
||||
export class MotInvocationSheetL5r5e extends BaseItemSheetL5r5e {
|
||||
/** @override */
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ["l5r5e", "sheet", "mot-invocation"],
|
||||
template: CONFIG.l5r5e.paths.templates + "items/mot_invocation/mot-invocation-sheet.html",
|
||||
width: 500,
|
||||
height: 360,
|
||||
});
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async getData(options = {}) {
|
||||
const sheetData = await super.getData(options);
|
||||
|
||||
// Build invocation types list from i18n
|
||||
const invTypes = game.l5r5e.HelpersL5r5e.getLocalizedRawObject("l5r5e.chiaroscuro.technique.invocation_types") ?? {};
|
||||
sheetData.data.invocationTypesList = [{ id: "", label: "—" }].concat(
|
||||
Object.entries(invTypes).map(([id, label]) => ({ id, label }))
|
||||
);
|
||||
|
||||
sheetData.data.enrichedHtml = {
|
||||
description: await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||
sheetData.data.system.description ?? "",
|
||||
{ async: true }
|
||||
),
|
||||
};
|
||||
|
||||
return sheetData;
|
||||
}
|
||||
|
||||
/** @override */
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
|
||||
if (!this.isEditable) return;
|
||||
|
||||
html.find("#mot_invocation_type").on("change", async (event) => {
|
||||
const type = event.target.value;
|
||||
const mode = INVOCATION_MODE[type] ?? 0;
|
||||
// Update stored value and refresh display
|
||||
await this.object.update({ system: { invocation_type: type, mode_invocation: mode } });
|
||||
this.render(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,8 @@ export class MystereSheetL5r5e extends BaseItemSheetL5r5e {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ["l5r5e", "sheet", "mystere"],
|
||||
template: CONFIG.l5r5e.paths.templates + "items/mystere/mystere-sheet.html",
|
||||
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "attributes" }],
|
||||
width: 500,
|
||||
height: 380,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import { ItemSheetL5r5e } from "./item-sheet.js";
|
||||
|
||||
/**
|
||||
* @extends {ItemSheet}
|
||||
*/
|
||||
export class PropertySheetL5r5e extends ItemSheetL5r5e {
|
||||
/** @override */
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ["l5r5e", "sheet", "property"],
|
||||
template: CONFIG.l5r5e.paths.templates + "items/property/property-sheet.html",
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import { ItemSheetL5r5e } from "./item-sheet.js";
|
||||
|
||||
/**
|
||||
* @extends {ItemSheet}
|
||||
*/
|
||||
export class SignatureScrollSheetL5r5e extends ItemSheetL5r5e {
|
||||
/** @override */
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ["l5r5e", "sheet", "signature-scroll"],
|
||||
template: CONFIG.l5r5e.paths.templates + "items/signature-scroll/signature-scroll-sheet.html",
|
||||
});
|
||||
}
|
||||
}
|
||||
31
system/scripts/items/technique-ecole-sheet.js
Normal file
31
system/scripts/items/technique-ecole-sheet.js
Normal file
@@ -0,0 +1,31 @@
|
||||
import { BaseItemSheetL5r5e } from "./base-item-sheet.js";
|
||||
|
||||
/**
|
||||
* Sheet for Technique École items (Chiaroscuro).
|
||||
* @extends {BaseItemSheetL5r5e}
|
||||
*/
|
||||
export class TechniqueEcoleSheetL5r5e extends BaseItemSheetL5r5e {
|
||||
/** @override */
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ["l5r5e", "sheet", "technique-ecole"],
|
||||
template: CONFIG.l5r5e.paths.templates + "items/technique_ecole/technique-ecole-sheet.html",
|
||||
width: 500,
|
||||
height: 380,
|
||||
});
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async getData(options = {}) {
|
||||
const sheetData = await super.getData(options);
|
||||
|
||||
sheetData.data.enrichedHtml = {
|
||||
description: await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||
sheetData.data.system.description ?? "",
|
||||
{ async: true }
|
||||
),
|
||||
};
|
||||
|
||||
return sheetData;
|
||||
}
|
||||
}
|
||||
@@ -1,195 +0,0 @@
|
||||
import { ItemSheetL5r5e } from "./item-sheet.js";
|
||||
|
||||
/**
|
||||
* @extends {ItemSheet}
|
||||
*/
|
||||
export class TechniqueSheetL5r5e extends ItemSheetL5r5e {
|
||||
/** @override */
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ["l5r5e", "sheet", "technique"],
|
||||
template: CONFIG.l5r5e.paths.templates + "items/technique/technique-sheet.html",
|
||||
});
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async getData(options = {}) {
|
||||
const sheetData = await super.getData(options);
|
||||
|
||||
// List all available techniques type
|
||||
const types = ["core", "school", "title", "chiaroscuro"];
|
||||
if (game.settings.get(CONFIG.l5r5e.namespace, "techniques-customs")) {
|
||||
types.push("custom");
|
||||
}
|
||||
sheetData.data.techniquesList = game.l5r5e.HelpersL5r5e.getTechniquesList({ types });
|
||||
|
||||
// Invocation sub-type fields (visible only for mot_invocation)
|
||||
sheetData.data.isMotInvocation = sheetData.data.system.technique_type === "mot_invocation";
|
||||
sheetData.data.invocationTypes = [
|
||||
{ id: "general", label: game.i18n.localize("chiaroscuro.technique.invocation_types.general") },
|
||||
{ id: "neutre", label: game.i18n.localize("chiaroscuro.technique.invocation_types.neutre") },
|
||||
{ id: "precis", label: game.i18n.localize("chiaroscuro.technique.invocation_types.precis") },
|
||||
];
|
||||
sheetData.data.modeInvocationValues = [
|
||||
{ id: "-3", label: "-3" },
|
||||
{ id: "0", label: "0" },
|
||||
{ id: "3", label: "+3" },
|
||||
];
|
||||
// Convert mode_invocation to string for selectOptions matching
|
||||
sheetData.data.system.mode_invocation_str = String(sheetData.data.system.mode_invocation ?? 0);
|
||||
|
||||
// Sanitize Difficulty and Skill list
|
||||
sheetData.data.system.difficulty = TechniqueSheetL5r5e.formatDifficulty(sheetData.data.system.difficulty);
|
||||
sheetData.data.system.skill = TechniqueSheetL5r5e.translateSkillsList(
|
||||
TechniqueSheetL5r5e.formatSkillList(sheetData.data.system.skill.split(",")),
|
||||
false
|
||||
).join(", ");
|
||||
|
||||
return sheetData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @override
|
||||
*/
|
||||
async _updateObject(event, formData) {
|
||||
// Change the image according to the type if this is already the case
|
||||
if (
|
||||
formData["system.technique_type"] &&
|
||||
formData.img === `${CONFIG.l5r5e.paths.assets}icons/techs/${this.object.system.technique_type}.svg`
|
||||
) {
|
||||
formData.img = `${CONFIG.l5r5e.paths.assets}icons/techs/${formData["system.technique_type"]}.svg`;
|
||||
}
|
||||
|
||||
// Sanitize Difficulty and Skill list
|
||||
formData["system.difficulty"] = TechniqueSheetL5r5e.formatDifficulty(formData["system.difficulty"]);
|
||||
formData["system.skill"] = TechniqueSheetL5r5e.formatSkillList(
|
||||
TechniqueSheetL5r5e.translateSkillsList(formData["system.skill"].split(","), true)
|
||||
).join(",");
|
||||
|
||||
// Convert mode_invocation_str back to number
|
||||
if ("system.mode_invocation_str" in formData) {
|
||||
formData["system.mode_invocation"] = parseInt(formData["system.mode_invocation_str"] ?? "0", 10);
|
||||
delete formData["system.mode_invocation_str"];
|
||||
}
|
||||
|
||||
return super._updateObject(event, formData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen to html elements
|
||||
* @param {jQuery} html HTML content of the sheet.
|
||||
* @override
|
||||
*/
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
|
||||
// *** Everything below here is only needed if the sheet is editable ***
|
||||
if (!this.isEditable) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Autocomplete
|
||||
game.l5r5e.HelpersL5r5e.autocomplete(
|
||||
html,
|
||||
"system.difficulty",
|
||||
[
|
||||
"@T:intrigueRank",
|
||||
"@T:focus",
|
||||
"@T:martialRank",
|
||||
"@T:statusRank|max",
|
||||
"@T:strife.value|max",
|
||||
"@T:vigilance",
|
||||
"@T:vigilance|max",
|
||||
"@T:vigilance|min",
|
||||
"@T:vigilance|max(@T:statusRank)",
|
||||
],
|
||||
","
|
||||
);
|
||||
game.l5r5e.HelpersL5r5e.autocomplete(
|
||||
html,
|
||||
"system.skill",
|
||||
Object.values(TechniqueSheetL5r5e.getSkillsTranslationMap(false)),
|
||||
","
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize the technique difficulty
|
||||
* @param {string} str
|
||||
* @return {string}
|
||||
*/
|
||||
static formatDifficulty(str) {
|
||||
if (str && !Number.isNumeric(str) && !CONFIG.l5r5e.regex.techniqueDifficulty.test(str)) {
|
||||
return "";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a flat map for skill translation
|
||||
* @param {boolean} bToSkillId if true flip props/values
|
||||
* @return {Object}
|
||||
*/
|
||||
static getSkillsTranslationMap(bToSkillId) {
|
||||
return Array.from(CONFIG.l5r5e.skills).reduce((acc, [id, cat]) => {
|
||||
if (bToSkillId) {
|
||||
acc[game.l5r5e.HelpersL5r5e.normalize(game.i18n.localize(`l5r5e.skills.${cat}.${id}`))] = id;
|
||||
acc[game.l5r5e.HelpersL5r5e.normalize(game.i18n.localize(`l5r5e.skills.${cat}.title`))] = cat;
|
||||
} else {
|
||||
acc[id] = game.i18n.localize(`l5r5e.skills.${cat}.${id}`);
|
||||
acc[cat] = game.i18n.localize(`l5r5e.skills.${cat}.title`);
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate a list of skill and category
|
||||
* @param {string[]} aIn
|
||||
* @param {boolean} bToSkillId
|
||||
* @return {string[]}
|
||||
*/
|
||||
static translateSkillsList(aIn, bToSkillId) {
|
||||
const map = TechniqueSheetL5r5e.getSkillsTranslationMap(bToSkillId);
|
||||
return aIn.map((skill) => map[game.l5r5e.HelpersL5r5e.normalize(skill)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize the technique skill list
|
||||
* @param {string[]} skillList
|
||||
* @return {string[]}
|
||||
*/
|
||||
static formatSkillList(skillList) {
|
||||
if (!skillList) {
|
||||
return "";
|
||||
}
|
||||
const categories = game.l5r5e.HelpersL5r5e.getCategoriesSkillsList();
|
||||
|
||||
// List categories
|
||||
const unqCatList = new Set();
|
||||
skillList.forEach((s) => {
|
||||
s = s?.trim();
|
||||
if (!!s && categories.has(s)) {
|
||||
unqCatList.add(s);
|
||||
}
|
||||
});
|
||||
|
||||
// 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];
|
||||
}
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
import { ItemSheetL5r5e } from "./item-sheet.js";
|
||||
|
||||
/**
|
||||
* @extends {ItemSheet}
|
||||
*/
|
||||
export class TitleSheetL5r5e extends ItemSheetL5r5e {
|
||||
/** @override */
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ["l5r5e", "sheet", "title"],
|
||||
template: CONFIG.l5r5e.paths.templates + "items/title/title-sheet.html",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Object|Promise}
|
||||
*/
|
||||
async getData(options = {}) {
|
||||
const sheetData = await super.getData(options);
|
||||
|
||||
// Prepare OwnedItems
|
||||
sheetData.data.embedItemsList = this._prepareEmbedItems(sheetData.data.system.items);
|
||||
|
||||
// Automatically compute the total xp cost (full price) and XP in title (cursus, some halved prices)
|
||||
const { xp_used_total, xp_used } = game.l5r5e.HelpersL5r5e.getItemsXpCost(sheetData.data.embedItemsList);
|
||||
sheetData.data.system.xp_used_total = xp_used_total;
|
||||
sheetData.data.system.xp_used = xp_used;
|
||||
|
||||
return sheetData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare Embed items
|
||||
* @param {[]|Map} itemsMap
|
||||
* @return {[]}
|
||||
* @private
|
||||
*/
|
||||
_prepareEmbedItems(itemsMap) {
|
||||
let itemsList = itemsMap;
|
||||
if (itemsMap instanceof Map) {
|
||||
itemsList = Array.from(itemsMap).map(([id, item]) => item);
|
||||
}
|
||||
|
||||
// Sort by rank desc
|
||||
itemsList.sort((a, b) => (b.system.rank || 0) - (a.system.rank || 0));
|
||||
|
||||
return itemsList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback actions which occur when a dragged element is dropped on a target.
|
||||
* @param {DragEvent} event The originating DragEvent
|
||||
* @private
|
||||
*/
|
||||
async _onDrop(event) {
|
||||
// Everything below here is only needed if the sheet is editable
|
||||
if (!this.isEditable) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check item type and subtype
|
||||
let item = await game.l5r5e.HelpersL5r5e.getDragnDropTargetObject(event);
|
||||
if (!item || item.documentName !== "Item" || !["technique", "advancement"].includes(item.type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = item.toObject(false);
|
||||
|
||||
// Check xp for techs
|
||||
if (item.type === "technique") {
|
||||
data.system.xp_cost = data.system.xp_cost > 0 ? data.system.xp_cost : CONFIG.l5r5e.xp.techniqueCost;
|
||||
data.system.xp_used = data.system.xp_cost;
|
||||
}
|
||||
|
||||
this.document.addEmbedItem(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to events from the sheet.
|
||||
* @param {jQuery} html HTML content of the sheet.
|
||||
*/
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
|
||||
// Everything below here is only needed if the sheet is editable
|
||||
if (!this.isEditable) {
|
||||
return;
|
||||
}
|
||||
|
||||
// *** Sub-Items management ***
|
||||
html.find(".item-add").on("click", this._addSubItem.bind(this));
|
||||
html.find(`.item-edit`).on("click", this._editSubItem.bind(this));
|
||||
html.find(`.item-delete`).on("click", this._deleteSubItem.bind(this));
|
||||
html.find(`.item-curriculum`).on("click", this._switchSubItemCurriculum.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a dialog to choose what Item to add, and add it on this Item
|
||||
* @param {Event} event
|
||||
* @return {Promise<void>}
|
||||
* @private
|
||||
*/
|
||||
async _addSubItem(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
// Show Dialog
|
||||
const selectedType = await game.l5r5e.HelpersL5r5e.showSubItemDialog(["advancement", "technique"]);
|
||||
if (!selectedType) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the new Item
|
||||
const itemId = await this.document.addEmbedItem(
|
||||
new game.l5r5e.ItemL5r5e({
|
||||
name: game.i18n.localize(`TYPES.Item.${selectedType.toLowerCase()}`),
|
||||
type: selectedType,
|
||||
img: `${CONFIG.l5r5e.paths.assets}icons/items/${selectedType}.svg`,
|
||||
})
|
||||
);
|
||||
|
||||
// Get the store object and display it
|
||||
const item = this.document.items.get(itemId);
|
||||
if (item) {
|
||||
item.sheet.render(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toogle the curriculum for this embed item
|
||||
* @param {Event} event
|
||||
* @return {Promise<void>}
|
||||
* @private
|
||||
*/
|
||||
async _switchSubItemCurriculum(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const itemId = $(event.currentTarget).data("item-id");
|
||||
const item = this.document.getEmbedItem(itemId);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Switch the state and update
|
||||
item.system.in_curriculum = !item.system.in_curriculum;
|
||||
return this.document.updateEmbedItem(item);
|
||||
}
|
||||
}
|
||||
@@ -15,18 +15,21 @@ export class WeaponSheetL5r5e extends ItemSheetL5r5e {
|
||||
async getData(options = {}) {
|
||||
const sheetData = await super.getData(options);
|
||||
|
||||
// Martial skills only
|
||||
// Only these four skills are relevant for weapons
|
||||
const allowedSkills = ["archery", "unarmed", "melee", "invocation"];
|
||||
sheetData.data.skills = Array.from(CONFIG.l5r5e.skills)
|
||||
.filter(([id, cat]) => cat === "martial")
|
||||
.filter(([id]) => allowedSkills.includes(id))
|
||||
.map(([id, cat]) => ({
|
||||
id,
|
||||
label: "l5r5e.skills." + cat.toLowerCase() + "." + id.toLowerCase(),
|
||||
}));
|
||||
|
||||
// Weapon categories (Chiaroscuro)
|
||||
// Weapon categories (Chiaroscuro) — sorted alphabetically
|
||||
const catObj = game.l5r5e.HelpersL5r5e.getLocalizedRawObject("chiaroscuro.weapon.categories") ?? {};
|
||||
sheetData.data.weaponCategories = [{ id: "", label: "—" }].concat(
|
||||
Object.entries(catObj).map(([id, label]) => ({ id, label }))
|
||||
Object.entries(catObj)
|
||||
.map(([id, label]) => ({ id, label }))
|
||||
.sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: "base" }))
|
||||
);
|
||||
|
||||
return sheetData;
|
||||
|
||||
@@ -10,7 +10,6 @@ import HooksL5r5e from "./hooks.js";
|
||||
import { ActorL5r5e } from "./actor.js";
|
||||
import { CharacterSheetL5r5e } from "./actors/character-sheet.js";
|
||||
import { NpcSheetL5r5e } from "./actors/npc-sheet.js";
|
||||
import { ArmySheetL5r5e } from "./actors/army-sheet.js";
|
||||
import { RulerL5r5e, TokenRulerL5r5e } from "./tatical-grid-rulers.js";
|
||||
// Dice and rolls
|
||||
import { L5rBaseDie } from "./dice/dietype/l5r-base-die.js";
|
||||
@@ -26,19 +25,13 @@ import { ItemL5r5e } from "./item.js";
|
||||
import { ItemSheetL5r5e } from "./items/item-sheet.js";
|
||||
import { ArmorSheetL5r5e } from "./items/armor-sheet.js";
|
||||
import { WeaponSheetL5r5e } from "./items/weapon-sheet.js";
|
||||
import { TechniqueSheetL5r5e } from "./items/technique-sheet.js";
|
||||
import { PropertySheetL5r5e } from "./items/property-sheet.js";
|
||||
import { AdvancementSheetL5r5e } from "./items/advancement-sheet.js";
|
||||
import { PeculiaritySheetL5r5e } from "./items/peculiarity-sheet.js";
|
||||
import { TitleSheetL5r5e } from "./items/title-sheet.js";
|
||||
import { BondSheetL5r5e } from "./items/bond-sheet.js";
|
||||
import { SignatureScrollSheetL5r5e } from "./items/signature-scroll-sheet.js";
|
||||
import { ItemPatternSheetL5r5e } from "./items/item-pattern-sheet.js";
|
||||
import { ArcaneSheetL5r5e } from "./items/arcane-sheet.js";
|
||||
import { EtatSheetL5r5e } from "./items/etat-sheet.js";
|
||||
import { MystereSheetL5r5e } from "./items/mystere-sheet.js";
|
||||
import { ArmyCohortSheetL5r5e } from "./items/army-cohort-sheet.js";
|
||||
import { ArmyFortificationSheetL5r5e } from "./items/army-fortification-sheet.js";
|
||||
import { TechniqueEcoleSheetL5r5e } from "./items/technique-ecole-sheet.js";
|
||||
import { MotInvocationSheetL5r5e } from "./items/mot-invocation-sheet.js";
|
||||
// JournalEntry
|
||||
import { JournalL5r5e } from "./journal.js";
|
||||
import { BaseJournalSheetL5r5e } from "./journals/base-journal-sheet.js";
|
||||
@@ -46,8 +39,6 @@ import { BaseJournalSheetL5r5e } from "./journals/base-journal-sheet.js";
|
||||
import { CompendiumDirectoryL5r5e } from "./compendium/l5r5e-compendium-directory.js";
|
||||
// Specific
|
||||
import { MigrationL5r5e } from "./migration.js";
|
||||
import { GmToolbox } from "./gm/gm-toolbox.js";
|
||||
import { GmMonitor } from "./gm/gm-monitor.js";
|
||||
import { Storage } from "./storage.js";
|
||||
// Misc
|
||||
import { L5r5eHtmlMultiSelectElement } from "./misc/l5r5e-multiselect.js";
|
||||
@@ -123,8 +114,6 @@ Hooks.once("init", async () => {
|
||||
DicePickerDialog,
|
||||
RollnKeepDialog,
|
||||
ChiaroscuroDiceDialog,
|
||||
GmToolbox,
|
||||
GmMonitor,
|
||||
storage: new Storage(),
|
||||
sockets: new SocketHandlerL5r5e(),
|
||||
migrations: MigrationL5r5e,
|
||||
@@ -155,11 +144,6 @@ Hooks.once("init", async () => {
|
||||
label: "TYPES.Actor.npc",
|
||||
makeDefault: true,
|
||||
});
|
||||
fdc.Actors.registerSheet(L5R5E.namespace, ArmySheetL5r5e, {
|
||||
types: ["army"],
|
||||
label: "TYPES.Actor.army",
|
||||
makeDefault: true,
|
||||
});
|
||||
|
||||
// Items
|
||||
fdc.Items.unregisterSheet("core", fav1s.ItemSheet);
|
||||
@@ -178,16 +162,6 @@ Hooks.once("init", async () => {
|
||||
label: "TYPES.Item.weapon",
|
||||
makeDefault: true,
|
||||
});
|
||||
fdc.Items.registerSheet(L5R5E.namespace, TechniqueSheetL5r5e, {
|
||||
types: ["technique"],
|
||||
label: "TYPES.Item.technique",
|
||||
makeDefault: true,
|
||||
});
|
||||
fdc.Items.registerSheet(L5R5E.namespace, PropertySheetL5r5e, {
|
||||
types: ["property"],
|
||||
label: "TYPES.Item.property",
|
||||
makeDefault: true,
|
||||
});
|
||||
fdc.Items.registerSheet(L5R5E.namespace, PeculiaritySheetL5r5e, {
|
||||
types: ["peculiarity"],
|
||||
label: "TYPES.Item.peculiarity",
|
||||
@@ -198,36 +172,6 @@ Hooks.once("init", async () => {
|
||||
label: "TYPES.Item.advancement",
|
||||
makeDefault: true,
|
||||
});
|
||||
fdc.Items.registerSheet(L5R5E.namespace, TitleSheetL5r5e, {
|
||||
types: ["title"],
|
||||
label: "TYPES.Item.title",
|
||||
makeDefault: true,
|
||||
});
|
||||
fdc.Items.registerSheet(L5R5E.namespace, BondSheetL5r5e, {
|
||||
types: ["bond"],
|
||||
label: "TYPES.Item.bond",
|
||||
makeDefault: true,
|
||||
});
|
||||
fdc.Items.registerSheet(L5R5E.namespace, SignatureScrollSheetL5r5e, {
|
||||
types: ["signature_scroll"],
|
||||
label: "TYPES.Item.signature_scroll",
|
||||
makeDefault: true,
|
||||
});
|
||||
fdc.Items.registerSheet(L5R5E.namespace, ItemPatternSheetL5r5e, {
|
||||
types: ["item_pattern"],
|
||||
label: "TYPES.Item.item_pattern",
|
||||
makeDefault: true,
|
||||
});
|
||||
fdc.Items.registerSheet(L5R5E.namespace, ArmyCohortSheetL5r5e, {
|
||||
types: ["army_cohort"],
|
||||
label: "TYPES.Item.army_cohort",
|
||||
makeDefault: true,
|
||||
});
|
||||
fdc.Items.registerSheet(L5R5E.namespace, ArmyFortificationSheetL5r5e, {
|
||||
types: ["army_fortification"],
|
||||
label: "TYPES.Item.army_fortification",
|
||||
makeDefault: true,
|
||||
});
|
||||
fdc.Items.registerSheet(L5R5E.namespace, ArcaneSheetL5r5e, {
|
||||
types: ["arcane"],
|
||||
label: "TYPES.Item.arcane",
|
||||
@@ -243,6 +187,16 @@ Hooks.once("init", async () => {
|
||||
label: "TYPES.Item.mystere",
|
||||
makeDefault: true,
|
||||
});
|
||||
fdc.Items.registerSheet(L5R5E.namespace, TechniqueEcoleSheetL5r5e, {
|
||||
types: ["technique_ecole"],
|
||||
label: "TYPES.Item.technique_ecole",
|
||||
makeDefault: true,
|
||||
});
|
||||
fdc.Items.registerSheet(L5R5E.namespace, MotInvocationSheetL5r5e, {
|
||||
types: ["mot_invocation"],
|
||||
label: "TYPES.Item.mot_invocation",
|
||||
makeDefault: true,
|
||||
});
|
||||
|
||||
// Journal
|
||||
fdc.Journal.unregisterSheet("core", fav1s.JournalSheet);
|
||||
|
||||
@@ -8,10 +8,10 @@ export const PreloadTemplates = async function () {
|
||||
`${tpl}actors/character/aspects.html`,
|
||||
`${tpl}actors/character/attributes.html`,
|
||||
`${tpl}actors/character/category.html`,
|
||||
`${tpl}actors/character/conflict.html`,
|
||||
`${tpl}actors/character/experience.html`,
|
||||
`${tpl}actors/character/identity.html`,
|
||||
`${tpl}actors/character/identity-text.html`,
|
||||
`${tpl}actors/character/notes.html`,
|
||||
`${tpl}actors/character/inventory.html`,
|
||||
`${tpl}actors/character/invocations.html`,
|
||||
`${tpl}actors/character/narrative.html`,
|
||||
@@ -20,6 +20,7 @@ export const PreloadTemplates = async function () {
|
||||
`${tpl}actors/character/skill.html`,
|
||||
`${tpl}actors/character/social.html`,
|
||||
`${tpl}actors/character/stance.html`,
|
||||
`${tpl}actors/character/stance-simple.html`,
|
||||
`${tpl}actors/character/techniques.html`,
|
||||
`${tpl}actors/character/twenty-questions-item.html`,
|
||||
// *** Actors : Npc ***
|
||||
@@ -33,20 +34,12 @@ export const PreloadTemplates = async function () {
|
||||
`${tpl}actors/npc/social.html`,
|
||||
`${tpl}actors/npc/skill.html`,
|
||||
`${tpl}actors/npc/techniques.html`,
|
||||
// *** Actors : Army ***
|
||||
`${tpl}actors/army/army.html`,
|
||||
`${tpl}actors/army/cohort.html`,
|
||||
`${tpl}actors/army/fortification.html`,
|
||||
`${tpl}actors/army/others.html`,
|
||||
// *** Items ***
|
||||
`${tpl}items/advancement/advancement-entry.html`,
|
||||
`${tpl}items/advancement/advancement-sheet.html`,
|
||||
`${tpl}items/armor/armors.html`,
|
||||
`${tpl}items/armor/armor-entry.html`,
|
||||
`${tpl}items/armor/armor-sheet.html`,
|
||||
`${tpl}items/bond/bond-entry.html`,
|
||||
`${tpl}items/bond/bond-sheet.html`,
|
||||
`${tpl}items/bond/bond-text.html`,
|
||||
`${tpl}items/item/items.html`,
|
||||
`${tpl}items/item/item-entry.html`,
|
||||
`${tpl}items/item/item-value.html`,
|
||||
@@ -54,29 +47,14 @@ export const PreloadTemplates = async function () {
|
||||
`${tpl}items/item/item-infos.html`,
|
||||
`${tpl}items/item/item-text-partial-reference.html`,
|
||||
`${tpl}items/item/item-text.html`,
|
||||
`${tpl}items/item-pattern/item-pattern-entry.html`,
|
||||
`${tpl}items/item-pattern/item-pattern-sheet.html`,
|
||||
`${tpl}items/item-pattern/item-pattern-text.html`,
|
||||
`${tpl}items/peculiarity/peculiarity-entry.html`,
|
||||
`${tpl}items/peculiarity/peculiarity-sheet.html`,
|
||||
`${tpl}items/peculiarity/peculiarity-text.html`,
|
||||
`${tpl}items/property/properties.html`,
|
||||
`${tpl}items/property/property-entry.html`,
|
||||
`${tpl}items/property/property-sheet.html`,
|
||||
`${tpl}items/signature-scroll/signature-scroll-entry.html`,
|
||||
`${tpl}items/signature-scroll/signature-scroll-sheet.html`,
|
||||
`${tpl}items/signature-scroll/signature-scroll-text.html`,
|
||||
`${tpl}items/technique/technique-entry.html`,
|
||||
`${tpl}items/technique/technique-sheet.html`,
|
||||
`${tpl}items/technique/technique-text.html`,
|
||||
`${tpl}items/title/title-entry.html`,
|
||||
`${tpl}items/title/title-sheet.html`,
|
||||
`${tpl}items/title/title-text.html`,
|
||||
`${tpl}items/weapon/weapons.html`,
|
||||
`${tpl}items/weapon/weapon-entry.html`,
|
||||
`${tpl}items/weapon/weapon-sheet.html`,
|
||||
`${tpl}items/army-cohort/army-cohort-entry.html`,
|
||||
`${tpl}items/army-fortification/army-fortification-entry.html`,
|
||||
`${tpl}items/technique_ecole/technique-ecole-sheet.html`,
|
||||
`${tpl}items/mot_invocation/mot-invocation-sheet.html`,
|
||||
`${tpl}dice/chiaroscuro-chat-roll.html`,
|
||||
]);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user