Added "Gm Monitor" & reset void point to gm toolbox
This commit is contained in:
315
system/scripts/gm/gm-monitor.js
Normal file
315
system/scripts/gm/gm-monitor.js
Normal file
@@ -0,0 +1,315 @@
|
||||
/**
|
||||
* L5R GM Monitor Windows
|
||||
* @extends {FormApplication}
|
||||
*/
|
||||
export class GmMonitor extends FormApplication {
|
||||
/**
|
||||
* Settings
|
||||
*/
|
||||
object = {
|
||||
actors: [],
|
||||
};
|
||||
|
||||
/**
|
||||
* Assign the default options
|
||||
* @override
|
||||
*/
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
id: "l5r5e-gm-monitor",
|
||||
classes: ["l5r5e", "gm-monitor"],
|
||||
template: CONFIG.l5r5e.paths.templates + "gm/gm-monitor.html",
|
||||
title: game.i18n.localize("l5r5e.gm_monitor.title"),
|
||||
width: 640,
|
||||
height: 300,
|
||||
resizable: true,
|
||||
closeOnSubmit: false,
|
||||
submitOnClose: false,
|
||||
submitOnChange: true,
|
||||
dragDrop: [{ dragSelector: null, dropSelector: null }],
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the Refresh button on top of sheet
|
||||
* @override
|
||||
*/
|
||||
_getHeaderButtons() {
|
||||
let buttons = super._getHeaderButtons();
|
||||
|
||||
// Send To Chat
|
||||
buttons.unshift({
|
||||
label: game.i18n.localize("l5r5e.global.refresh"),
|
||||
class: "refresh",
|
||||
icon: "fas fa-sync-alt",
|
||||
onclick: async () => this.refresh(),
|
||||
});
|
||||
|
||||
return buttons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {ApplicationOptions} options
|
||||
*/
|
||||
constructor(options = {}) {
|
||||
super(options);
|
||||
this._initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh data (used from socket)
|
||||
*/
|
||||
async refresh() {
|
||||
if (!game.user.isGM) {
|
||||
return;
|
||||
}
|
||||
this._initialize();
|
||||
this.render(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the values
|
||||
* @private
|
||||
*/
|
||||
_initialize() {
|
||||
let actors;
|
||||
const ids = game.settings.get("l5r5e", "gm-monitor-actors");
|
||||
|
||||
if (ids.length > 0) {
|
||||
// get actors with stored ids
|
||||
actors = game.actors.filter((e) => ids.includes(e.id));
|
||||
} else {
|
||||
// If empty add pc with owner
|
||||
actors = game.actors.filter((actor) => actor.data.type === "character" && actor.hasPlayerOwner);
|
||||
this._saveActorsIds();
|
||||
}
|
||||
|
||||
// Sort by name asc
|
||||
actors.sort((a, b) => {
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
|
||||
this.object = {
|
||||
actors,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent non GM to render this windows
|
||||
* @override
|
||||
*/
|
||||
render(force = false, options = {}) {
|
||||
if (!game.user.isGM) {
|
||||
return false;
|
||||
}
|
||||
// this.position.width = "auto";
|
||||
// this.position.height = "auto";
|
||||
return super.render(force, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct and return the data object used to render the HTML template for this form application.
|
||||
* @param options
|
||||
* @return {Object}
|
||||
* @override
|
||||
*/
|
||||
getData(options = null) {
|
||||
return {
|
||||
...super.getData(options),
|
||||
data: this.object,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen to html elements
|
||||
* @param {jQuery} html HTML content of the sheet.
|
||||
* @override
|
||||
*/
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
|
||||
if (!game.user.isGM) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Open sheet
|
||||
html.find(`.actor-sheet-control`).on("click", this._openActorSheet.bind(this));
|
||||
|
||||
// Delete
|
||||
html.find(`.actor-remove-control`).on("click", this._removeActor.bind(this));
|
||||
|
||||
// Tooltips
|
||||
html.find(".actor-infos-control")
|
||||
.on("mouseenter", async (event) => {
|
||||
$(document.body).find("#l5r5e-tooltip-ct").remove();
|
||||
|
||||
const id = $(event.currentTarget).data("actor-id");
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const actor = this.object.actors.find((e) => e.id === id);
|
||||
if (!actor) {
|
||||
return;
|
||||
}
|
||||
const tpl = await this._getTooltipForActor(actor);
|
||||
|
||||
$(document.body).append(
|
||||
`<div id="l5r5e-tooltip-ct" class="l5r5e-tooltip l5r5e-tooltip-ct">${tpl}</div>`
|
||||
);
|
||||
})
|
||||
.on("mousemove", (event) => {
|
||||
const popup = $(document.body).find("#l5r5e-tooltip-ct");
|
||||
if (popup) {
|
||||
popup.css(game.l5r5e.HelpersL5r5e.popupPosition(event, popup));
|
||||
}
|
||||
})
|
||||
.on("mouseleave", () => {
|
||||
$(document.body).find("#l5r5e-tooltip-ct").remove();
|
||||
}); // tooltips
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called upon form submission after form data is validated
|
||||
* @param event The initial triggering submission event
|
||||
* @param formData The object of validated form data with which to update the object
|
||||
* @returns A Promise which resolves once the update operation has completed
|
||||
* @override
|
||||
*/
|
||||
async _updateObject(event, formData) {
|
||||
this.render(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle dropped data on the Actor sheet
|
||||
* @param {DragEvent} event
|
||||
*/
|
||||
async _onDrop(event) {
|
||||
// *** Everything below here is only needed if the sheet is editable ***
|
||||
if (!this.isEditable) {
|
||||
return;
|
||||
}
|
||||
|
||||
const json = event.dataTransfer.getData("text/plain");
|
||||
if (!json) {
|
||||
return;
|
||||
}
|
||||
const data = JSON.parse(json);
|
||||
if (!data || data.type !== "Actor" || !data.id || !!this.object.actors.find((e) => e.id === data.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const actor = game.actors.filter((e) => e.id === data.id);
|
||||
if (!actor) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.object.actors.push(actor[0]);
|
||||
|
||||
await this._saveActorsIds();
|
||||
return this.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the actors ids in settings
|
||||
* @return {Promise<*>}
|
||||
* @private
|
||||
*/
|
||||
async _saveActorsIds() {
|
||||
return game.settings.set(
|
||||
"l5r5e",
|
||||
"gm-monitor-actors",
|
||||
this.object.actors.map((e) => e.id)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the Sheet for this actor
|
||||
* @param {Event} event
|
||||
* @return {Promise<void>}
|
||||
* @private
|
||||
*/
|
||||
async _openActorSheet(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const id = $(event.currentTarget).data("actor-id");
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.object.actors.find((e) => e.id === id)?.sheet?.render(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the link to a property for the current item
|
||||
* @param {Event} event
|
||||
* @return {Promise<void>}
|
||||
* @private
|
||||
*/
|
||||
async _removeActor(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const id = $(event.currentTarget).data("actor-id");
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.object.actors = this.object.actors.filter((e) => e.id !== id);
|
||||
await this._saveActorsIds();
|
||||
|
||||
return this.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tooltips informations for this actor
|
||||
* @param {BaseSheetL5r5e} actor
|
||||
* @return {string}
|
||||
* @private
|
||||
*/
|
||||
async _getTooltipForActor(actor) {
|
||||
const data = actor.data.data;
|
||||
|
||||
// Peculiarities
|
||||
const pec = actor.items.filter((e) => e.type === "peculiarity");
|
||||
const adv = pec
|
||||
.filter((e) => ["distinction", "passion"].includes(e.data.data.peculiarity_type))
|
||||
.map((e) => e.name)
|
||||
.join(", ");
|
||||
const dis = pec
|
||||
.filter((e) => ["adversity", "anxiety"].includes(e.data.data.peculiarity_type))
|
||||
.map((e) => e.name)
|
||||
.join(", ");
|
||||
|
||||
// Equipped Armors & Weapons
|
||||
const arm = actor.items
|
||||
.filter((e) => e.type === "armor" && e.data.data.equipped)
|
||||
.map(
|
||||
(e) =>
|
||||
e.name +
|
||||
`(<i class="fas fa-tint">${e.data.data.armor.physical}</i> / <i class="fas fa-bolt">${e.data.data.armor.supernatural}</i>)`
|
||||
)
|
||||
.join(", ");
|
||||
const wea = actor.items
|
||||
.filter((e) => e.type === "weapon" && e.data.data.equipped)
|
||||
.map(
|
||||
(e) =>
|
||||
e.name +
|
||||
`(<i class="fas fa-arrows-alt-h"> ${e.data.data.range || 0}</i> / <i class="fas fa-tint"> ${
|
||||
e.data.data.damage
|
||||
}</i> / <i class="fas fa-skull"> ${e.data.data.deadliness}</i>)`
|
||||
)
|
||||
.join(", ");
|
||||
|
||||
// *** Template ***
|
||||
return renderTemplate(`${CONFIG.l5r5e.paths.templates}gm/gm-monitor-tooltip.html`, {
|
||||
actorData: data,
|
||||
advantages: adv,
|
||||
disadvantages: dis,
|
||||
armors: arm,
|
||||
weapons: wea,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
* L5R GM Toolbox dialog
|
||||
* @extends {FormApplication}
|
||||
*/
|
||||
export class GmToolsDialog extends FormApplication {
|
||||
export class GmToolbox extends FormApplication {
|
||||
/**
|
||||
* Settings
|
||||
*/
|
||||
@@ -16,11 +16,11 @@ export class GmToolsDialog extends FormApplication {
|
||||
const x = $(window).width();
|
||||
const y = $(window).height();
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
id: "l5r5e-gm-tools-dialog",
|
||||
classes: ["l5r5e", "gm-tools-dialog"],
|
||||
template: CONFIG.l5r5e.paths.templates + "dialogs/gm-tools-dialog.html",
|
||||
id: "l5r5e-gm-toolbox",
|
||||
classes: ["l5r5e", "gm-toolbox"],
|
||||
template: CONFIG.l5r5e.paths.templates + "gm/gm-toolbox.html",
|
||||
title: game.i18n.localize("l5r5e.gm_toolbox.title"),
|
||||
left: x - 512,
|
||||
left: x - 605,
|
||||
top: y - 98,
|
||||
closeOnSubmit: false,
|
||||
submitOnClose: false,
|
||||
@@ -152,12 +152,25 @@ export class GmToolsDialog extends FormApplication {
|
||||
game.settings.set("l5r5e", "initiative-difficulty-value", this.object.difficulty).then(() => this.submit());
|
||||
});
|
||||
|
||||
// Scene End & Sleep
|
||||
// Scene End, Sleep, void pt
|
||||
html.find(`.gm_actor_updates`).on("click", (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this._updatesActors($(event.currentTarget).data("type"));
|
||||
});
|
||||
|
||||
// GM Monitor
|
||||
html.find(`.gm_monitor`).on("click", (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const app = Object.values(ui.windows).find((e) => e.id === "l5r5e-gm-monitor");
|
||||
if (app) {
|
||||
app.close();
|
||||
} else {
|
||||
new game.l5r5e.GmMonitor().render(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -202,6 +215,14 @@ export class GmToolsDialog extends FormApplication {
|
||||
Math.ceil(actor.data.data.strife.max / 2)
|
||||
);
|
||||
break;
|
||||
|
||||
case "reset_void":
|
||||
// only pc
|
||||
if (actor.data.type !== "character" || !actor.hasPlayerOwner) {
|
||||
return;
|
||||
}
|
||||
actor.data.data.void_points.value = Math.ceil(actor.data.data.void_points.max / 2);
|
||||
break;
|
||||
}
|
||||
|
||||
actor.update({
|
||||
@@ -212,6 +233,9 @@ export class GmToolsDialog extends FormApplication {
|
||||
strife: {
|
||||
value: actor.data.data.strife.value,
|
||||
},
|
||||
void_points: {
|
||||
value: actor.data.data.void_points.value,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -253,33 +253,34 @@ export class HelpersL5r5e {
|
||||
static getPackNameForCoreItem(documentId) {
|
||||
const core = new Map();
|
||||
|
||||
// Items
|
||||
core.set("Arm", "l5r5e.core-armors");
|
||||
core.set("Bon", "l5r5e.core-bonds");
|
||||
core.set("Itp", "l5r5e.core-item-patterns");
|
||||
core.set("Ite", "l5r5e.core-items");
|
||||
core.set("Pro", "l5r5e.core-properties");
|
||||
core.set("Kat", "l5r5e.core-techniques-kata");
|
||||
core.set("Kih", "l5r5e.core-techniques-kiho");
|
||||
core.set("Tit", "l5r5e.core-titles");
|
||||
core.set("Sig", "l5r5e.core-signature-scrolls");
|
||||
core.set("Wea", "l5r5e.core-weapons");
|
||||
|
||||
core.set("Ins", "l5r5e.core-techniques-inversion");
|
||||
core.set("Inv", "l5r5e.core-techniques-invocations");
|
||||
core.set("Kat", "l5r5e.core-techniques-kata");
|
||||
core.set("Kih", "l5r5e.core-techniques-kiho");
|
||||
core.set("Mah", "l5r5e.core-techniques-maho");
|
||||
core.set("Mas", "l5r5e.core-techniques-mastery");
|
||||
core.set("Nin", "l5r5e.core-techniques-ninjutsu");
|
||||
core.set("Rit", "l5r5e.core-techniques-rituals");
|
||||
core.set("Shu", "l5r5e.core-techniques-shuji");
|
||||
core.set("Mah", "l5r5e.core-techniques-maho");
|
||||
core.set("Nin", "l5r5e.core-techniques-ninjutsu");
|
||||
core.set("Sch", "l5r5e.core-techniques-school");
|
||||
core.set("Mas", "l5r5e.core-techniques-mastery");
|
||||
core.set("Ite", "l5r5e.core-items");
|
||||
core.set("Arm", "l5r5e.core-armors");
|
||||
core.set("Wea", "l5r5e.core-weapons");
|
||||
core.set("Bon", "l5r5e.core-bonds");
|
||||
core.set("Tit", "l5r5e.core-titles");
|
||||
core.set("Itp", "l5r5e.core-item-patterns");
|
||||
core.set("Sig", "l5r5e.core-signature-scrolls");
|
||||
core.set("Dis", "l5r5e.core-peculiarities-distinctions");
|
||||
core.set("Pas", "l5r5e.core-peculiarities-passions");
|
||||
|
||||
core.set("Adv", "l5r5e.core-peculiarities-adversities");
|
||||
core.set("Anx", "l5r5e.core-peculiarities-anxieties");
|
||||
core.set("Dis", "l5r5e.core-peculiarities-distinctions");
|
||||
core.set("Pas", "l5r5e.core-peculiarities-passions");
|
||||
|
||||
// Journal
|
||||
core.set("Csc", "l5r5e.core-journal-school-curriculum");
|
||||
core.set("Con", "l5r5e.core-journal-conditions");
|
||||
core.set("Opp", "l5r5e.core-journal-opportunities");
|
||||
core.set("Csc", "l5r5e.core-journal-school-curriculum");
|
||||
core.set("Ter", "l5r5e.core-journal-terrain-qualities");
|
||||
|
||||
return core.get(documentId.replace(/L5RCore(\w{3})\d+/gi, "$1"));
|
||||
@@ -344,7 +345,7 @@ export class HelpersL5r5e {
|
||||
* Notify Applications using Difficulty settings that the values was changed
|
||||
*/
|
||||
static notifyDifficultyChange() {
|
||||
["l5r5e-dice-picker-dialog", "l5r5e-gm-tools-dialog"].forEach((appId) => {
|
||||
["l5r5e-dice-picker-dialog", "l5r5e-gm-toolbox"].forEach((appId) => {
|
||||
const app = Object.values(ui.windows).find((e) => e.id === appId);
|
||||
if (app && typeof app.refresh === "function") {
|
||||
app.refresh();
|
||||
@@ -407,21 +408,6 @@ export class HelpersL5r5e {
|
||||
});
|
||||
|
||||
// Item detail tooltips
|
||||
const fctPos = (event, popup) => {
|
||||
let left = +event.clientX + 60,
|
||||
top = +event.clientY;
|
||||
|
||||
let maxY = window.innerHeight - popup.outerHeight();
|
||||
if (top > maxY) {
|
||||
top = maxY - 10;
|
||||
}
|
||||
|
||||
let maxX = window.innerWidth - popup.outerWidth();
|
||||
if (left > maxX) {
|
||||
left -= popup.outerWidth() + 100;
|
||||
}
|
||||
return { left: left + "px", top: top + "px", visibility: "visible" };
|
||||
};
|
||||
html.find(".l5r5e-tooltip")
|
||||
.on("mouseenter", async (event) => {
|
||||
$(document.body).find("#l5r5e-tooltip-ct").remove();
|
||||
@@ -436,15 +422,14 @@ export class HelpersL5r5e {
|
||||
return;
|
||||
}
|
||||
|
||||
const popup = $(document.body).append(
|
||||
$(document.body).append(
|
||||
`<div id="l5r5e-tooltip-ct" class="l5r5e-tooltip l5r5e-tooltip-ct">${tpl}</div>`
|
||||
);
|
||||
popup.css(fctPos(event, popup));
|
||||
})
|
||||
.on("mousemove", (event) => {
|
||||
const popup = $(document.body).find("#l5r5e-tooltip-ct");
|
||||
if (popup) {
|
||||
popup.css(fctPos(event, popup));
|
||||
popup.css(HelpersL5r5e.popupPosition(event, popup));
|
||||
}
|
||||
})
|
||||
.on("mouseleave", () => {
|
||||
@@ -452,6 +437,28 @@ export class HelpersL5r5e {
|
||||
}); // tooltips
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Popup position avoiding screen borders
|
||||
* @param {Event} event HTML Event
|
||||
* @param popup
|
||||
* @return {{top: string, visibility: string, left: string}}
|
||||
*/
|
||||
static popupPosition(event, popup) {
|
||||
let left = +event.clientX + 60,
|
||||
top = +event.clientY;
|
||||
|
||||
let maxY = window.innerHeight - popup.outerHeight();
|
||||
if (top > maxY) {
|
||||
top = maxY - 10;
|
||||
}
|
||||
|
||||
let maxX = window.innerWidth - popup.outerWidth();
|
||||
if (left > maxX) {
|
||||
left -= popup.outerWidth() + 100;
|
||||
}
|
||||
return { left: left + "px", top: top + "px", visibility: "visible" };
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Item from a Actor Sheet
|
||||
* @param {Event} event HTML Event
|
||||
|
||||
@@ -31,7 +31,7 @@ export default class HooksL5r5e {
|
||||
|
||||
// Settings TN and EncounterType
|
||||
if (game.user.isGM) {
|
||||
new game.l5r5e.GmToolsDialog().render(true);
|
||||
new game.l5r5e.GmToolbox().render(true);
|
||||
}
|
||||
|
||||
// ***** UI *****
|
||||
|
||||
@@ -19,7 +19,6 @@ import { RollL5r5e } from "./dice/roll.js";
|
||||
import { DicePickerDialog } from "./dice/dice-picker-dialog.js";
|
||||
import { RollnKeepDialog } from "./dice/roll-n-keep-dialog.js";
|
||||
import { CombatL5r5e } from "./combat.js";
|
||||
import { GmToolsDialog } from "./dialogs/gm-tools-dialog.js";
|
||||
// Items
|
||||
import { ItemL5r5e } from "./item.js";
|
||||
import { ItemSheetL5r5e } from "./items/item-sheet.js";
|
||||
@@ -38,6 +37,8 @@ import { JournalL5r5e } from "./journal.js";
|
||||
import { BaseJournalSheetL5r5e } from "./journals/base-journal-sheet.js";
|
||||
// Specific
|
||||
import { MigrationL5r5e } from "./migration.js";
|
||||
import { GmToolbox } from "./gm/gm-toolbox.js";
|
||||
import { GmMonitor } from "./gm/gm-monitor.js";
|
||||
|
||||
/* ------------------------------------ */
|
||||
/* Initialize system */
|
||||
@@ -84,7 +85,8 @@ Hooks.once("init", async () => {
|
||||
ActorL5r5e,
|
||||
DicePickerDialog,
|
||||
RollnKeepDialog,
|
||||
GmToolsDialog,
|
||||
GmToolbox,
|
||||
GmMonitor,
|
||||
HelpDialog,
|
||||
sockets: new SocketHandlerL5r5e(),
|
||||
migrations: MigrationL5r5e,
|
||||
|
||||
@@ -100,4 +100,15 @@ export const RegisterSettings = function () {
|
||||
type: String,
|
||||
default: "null",
|
||||
});
|
||||
|
||||
/* ------------------------------------ */
|
||||
/* GM Monitor windows (GM only) */
|
||||
/* ------------------------------------ */
|
||||
game.settings.register("l5r5e", "gm-monitor-actors", {
|
||||
name: "Gm Monitor",
|
||||
scope: "world", // for sync between gm
|
||||
config: false,
|
||||
type: Array,
|
||||
default: [],
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user