- Added Skills to character sheet - Added migration to old skills - Added icon on skills - Added actor.isNpc - Money out of system
436 lines
12 KiB
JavaScript
436 lines
12 KiB
JavaScript
/**
|
|
* L5R GM Monitor Windows
|
|
* @extends {FormApplication}
|
|
*/
|
|
export class GmMonitor extends FormApplication {
|
|
/**
|
|
* Settings
|
|
*/
|
|
object = {
|
|
view: "characters", // characters|armies
|
|
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: 800,
|
|
height: 300,
|
|
resizable: true,
|
|
closeOnSubmit: false,
|
|
submitOnClose: false,
|
|
submitOnChange: false,
|
|
dragDrop: [{ dragSelector: null, dropSelector: null }],
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Add the Switch View button on top of sheet
|
|
* @override
|
|
*/
|
|
_getHeaderButtons() {
|
|
let buttons = super._getHeaderButtons();
|
|
|
|
// Switch view Characters/Armies
|
|
buttons.unshift({
|
|
label: game.i18n.localize("l5r5e.gm.monitor.switch_view"),
|
|
class: "switch-view",
|
|
icon: "fas fa-users",
|
|
onclick: () =>
|
|
game.l5r5e.HelpersL5r5e.debounce(
|
|
"SwitchView-" + this.object.id,
|
|
() => {
|
|
this.object.view = this.object.view === "armies" ? "characters" : "armies";
|
|
this.render(false);
|
|
},
|
|
1000,
|
|
true
|
|
)(),
|
|
});
|
|
|
|
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(CONFIG.l5r5e.systemName, "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.isCharacter && actor.hasPlayerOwnerActive);
|
|
this._saveActorsIds();
|
|
}
|
|
|
|
// Sort by name asc
|
|
actors.sort((a, b) => {
|
|
return a.name.localeCompare(b.name);
|
|
});
|
|
|
|
this.object.actors = actors;
|
|
}
|
|
|
|
/**
|
|
* Prevent non GM to render this windows
|
|
* @override
|
|
*/
|
|
render(force = false, options = {}) {
|
|
if (!game.user.isGM) {
|
|
return false;
|
|
}
|
|
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
|
|
*/
|
|
async getData(options = null) {
|
|
return {
|
|
...(await super.getData(options)),
|
|
data: {
|
|
...this.object,
|
|
actors: this.object.actors.filter((a) => (this.object.view === "armies" ? a.isArmy : !a.isArmy)),
|
|
},
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Listen to html elements
|
|
* @param {jQuery} html HTML content of the sheet.
|
|
* @override
|
|
*/
|
|
activateListeners(html) {
|
|
super.activateListeners(html);
|
|
|
|
if (!game.user.isGM) {
|
|
return;
|
|
}
|
|
|
|
// Commons
|
|
game.l5r5e.HelpersL5r5e.commonListeners(html);
|
|
|
|
// Delete
|
|
html.find(`.actor-remove-control`).on("click", this._removeActor.bind(this));
|
|
|
|
// Add/Subtract
|
|
html.find(`.actor-modify-control`).on("mousedown", this._modifyActor.bind(this));
|
|
|
|
// Tooltips
|
|
game.l5r5e.HelpersL5r5e.popupManager(html.find(".actor-infos-control"), async (event) => {
|
|
const type = $(event.currentTarget).data("type");
|
|
if (!type) {
|
|
return;
|
|
}
|
|
if (type === "text") {
|
|
return $(event.currentTarget).data("text");
|
|
}
|
|
|
|
const id = $(event.currentTarget).data("actor-id");
|
|
if (!id) {
|
|
return;
|
|
}
|
|
const actor = this.object.actors.find((e) => e.id === id);
|
|
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);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 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.uuid || !!this.object.actors.find((a) => a.uuid === data.uuid)) {
|
|
return;
|
|
}
|
|
|
|
const actor = game.actors.find((a) => a.uuid === data.uuid);
|
|
if (!actor) {
|
|
return;
|
|
}
|
|
|
|
this.object.actors.push(actor);
|
|
|
|
return this._saveActorsIds();
|
|
}
|
|
|
|
/**
|
|
* Save the actors ids in settings
|
|
* @return {Promise<*>}
|
|
* @private
|
|
*/
|
|
async _saveActorsIds() {
|
|
return game.settings.set(
|
|
CONFIG.l5r5e.systemName,
|
|
"gm-monitor-actors",
|
|
this.object.actors.map((e) => e.id)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
|
|
return this._saveActorsIds();
|
|
}
|
|
|
|
/**
|
|
* Add or subtract fatigue/strife/void/casualties/panic
|
|
* @param event
|
|
* @return {Promise<void>}
|
|
* @private
|
|
*/
|
|
async _modifyActor(event) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
|
|
const type = $(event.currentTarget).data("type");
|
|
if (!type) {
|
|
console.warn("L5R5E | type not set", type);
|
|
return;
|
|
}
|
|
const id = $(event.currentTarget).data("actor-id");
|
|
if (!id) {
|
|
console.warn("L5R5E | actor id not set", type);
|
|
return;
|
|
}
|
|
const actor = game.actors.get(id);
|
|
if (!actor) {
|
|
console.warn("L5R5E | Actor not found", type);
|
|
return;
|
|
}
|
|
|
|
// Mouse bt : middle = 0, left +1, right -1
|
|
const add = event.which === 2 ? -999 : event.which === 1 ? 1 : -1;
|
|
|
|
// Stance
|
|
let stanceIdx = CONFIG.l5r5e.stances.findIndex((s) => s === actor.system.stance) + (event.which === 1 ? 1 : -1);
|
|
if (stanceIdx < 0) {
|
|
stanceIdx = CONFIG.l5r5e.stances.length - 1;
|
|
} else if (stanceIdx > CONFIG.l5r5e.stances.length - 1) {
|
|
stanceIdx = 0;
|
|
}
|
|
|
|
const updateData = {};
|
|
switch (type) {
|
|
// *** Characters ***
|
|
case "fatigue":
|
|
updateData["system.fatigue.value"] = Math.max(0, actor.system.fatigue.value + add);
|
|
break;
|
|
|
|
case "strife":
|
|
updateData["system.strife.value"] = Math.max(0, actor.system.strife.value + add);
|
|
break;
|
|
|
|
case "void_points":
|
|
updateData["system.void_points.value"] = Math.min(
|
|
actor.system.void_points.max,
|
|
Math.max(0, actor.system.void_points.value + add)
|
|
);
|
|
break;
|
|
|
|
case "stance":
|
|
updateData["system.stance"] = CONFIG.l5r5e.stances[stanceIdx];
|
|
break;
|
|
|
|
case "prepared":
|
|
updateData["system.prepared"] = !actor.system.prepared;
|
|
break;
|
|
|
|
// *** Armies ***
|
|
case "casualties":
|
|
updateData["system.battle_readiness.casualties_strength.value"] = Math.max(
|
|
0,
|
|
actor.system.battle_readiness.casualties_strength.value + add
|
|
);
|
|
break;
|
|
|
|
case "panic":
|
|
updateData["system.battle_readiness.panic_discipline.value"] = Math.max(
|
|
0,
|
|
actor.system.battle_readiness.panic_discipline.value + add
|
|
);
|
|
break;
|
|
|
|
default:
|
|
console.warn("L5R5E | Unsupported type", type);
|
|
break;
|
|
}
|
|
if (!foundry.utils.isEmpty(updateData)) {
|
|
await actor.update(updateData);
|
|
this.render(false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 pec = actor.items.filter((e) => e.type === "peculiarity");
|
|
const adv = pec
|
|
.filter((e) => ["distinction", "passion"].includes(e.system.peculiarity_type))
|
|
.map((e) => e.name)
|
|
.join(", ");
|
|
const dis = pec
|
|
.filter((e) => ["adversity", "anxiety"].includes(e.system.peculiarity_type))
|
|
.map((e) => e.name)
|
|
.join(", ");
|
|
|
|
// *** Template ***
|
|
return renderTemplate(`${CONFIG.l5r5e.paths.templates}gm/monitor-tooltips/global.html`, {
|
|
actorData: actorData,
|
|
advantages: adv,
|
|
disadvantages: dis,
|
|
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 renderTemplate(`${CONFIG.l5r5e.paths.templates}gm/monitor-tooltips/global-armies.html`, {
|
|
actorData: actorData,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get weapons information for this actor
|
|
* @param {ActorL5r5e} actor
|
|
* @return {string}
|
|
* @private
|
|
*/
|
|
async _getTooltipWeapons(actor) {
|
|
const display = (e) => {
|
|
return (
|
|
e.name +
|
|
` (<i class="fas fa-arrows-alt-h"> ${e.system.range}</i>` +
|
|
` / <i class="fas fa-tint"> ${e.system.damage}</i>` +
|
|
` / <i class="fas fa-skull"> ${e.system.deadliness}</i>)`
|
|
);
|
|
};
|
|
|
|
// Readied Weapons
|
|
const readied = actor.items
|
|
.filter((e) => e.type === "weapon" && e.system.equipped && !!e.system.readied)
|
|
.map((e) => display(e));
|
|
|
|
// Equipped Weapons
|
|
const sheathed = actor.items
|
|
.filter((e) => e.type === "weapon" && e.system.equipped && !e.system.readied)
|
|
.map((e) => display(e));
|
|
|
|
// *** Template ***
|
|
return renderTemplate(`${CONFIG.l5r5e.paths.templates}gm/monitor-tooltips/weapons.html`, {
|
|
readied,
|
|
sheathed,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get armors information for this actor
|
|
* @param {ActorL5r5e} actor
|
|
* @return {string}
|
|
* @private
|
|
*/
|
|
async _getTooltipArmors(actor) {
|
|
// Equipped Armors
|
|
const armors = actor.items
|
|
.filter((e) => e.type === "armor" && e.system.equipped)
|
|
.map(
|
|
(e) =>
|
|
e.name +
|
|
` (<i class="fas fa-tint">${e.system.armor.physical}</i>` +
|
|
` / <i class="fas fa-bolt">${e.system.armor.supernatural}</i>)`
|
|
);
|
|
|
|
// *** Template ***
|
|
return renderTemplate(`${CONFIG.l5r5e.paths.templates}gm/monitor-tooltips/armors.html`, {
|
|
armors,
|
|
});
|
|
}
|
|
}
|