mirror of
https://github.com/migatu/vtt_work.git
synced 2026-07-14 13:34:42 +00:00
Final final final
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "gk-greyknight",
|
"id": "gk-greyknight",
|
||||||
"title": "Grey Knights — Compendium (Wrath & Glory)",
|
"title": "Grey Knights — Compendium (Wrath & Glory)",
|
||||||
"version": "0.6.4.6",
|
"version": "0.6.5.2",
|
||||||
"compatibility": {
|
"compatibility": {
|
||||||
"minimum": "11",
|
"minimum": "11",
|
||||||
"verified": "13"
|
"verified": "13"
|
||||||
@@ -26,7 +26,8 @@
|
|||||||
],
|
],
|
||||||
"esmodules": [
|
"esmodules": [
|
||||||
"scripts/auto-loadout.js",
|
"scripts/auto-loadout.js",
|
||||||
"scripts/helpers.js"
|
"scripts/helpers.js",
|
||||||
|
"scripts/ui-gkpanel.js"
|
||||||
],
|
],
|
||||||
"readme": "README.txt"
|
"readme": "README.txt"
|
||||||
}
|
}
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,201 @@
|
|||||||
|
// gk-greyknight — ui-gkpanel.js (0.6.5.2)
|
||||||
|
|
||||||
|
const GK_NS = "gk-greyknight";
|
||||||
|
const GK_DEBUG = false;
|
||||||
|
|
||||||
|
/* ---------------- helpers ---------------- */
|
||||||
|
function gkActorHasBrotherhood(actor) {
|
||||||
|
return !!actor?.items?.some(i => i.type === "ability" && i.name === "Brotherhood of Psykers");
|
||||||
|
}
|
||||||
|
function gkAlliesAndBonus(actor) {
|
||||||
|
const tokens = canvas?.tokens?.placeables || [];
|
||||||
|
const ids = new Set();
|
||||||
|
for (const t of tokens) {
|
||||||
|
const a = t.actor; if (!a) continue;
|
||||||
|
if (gkActorHasBrotherhood(a)) ids.add(a.id);
|
||||||
|
}
|
||||||
|
const total = ids.size;
|
||||||
|
const bonus = ids.has(actor?.id) ? Math.max(0, total - 1) : total;
|
||||||
|
return { total, bonus };
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------- settings (per-user) ---------------- */
|
||||||
|
Hooks.once("init", () => {
|
||||||
|
game.settings.register(GK_NS, "panelVisible", {
|
||||||
|
name: "Brotherhood panel visible",
|
||||||
|
hint: "Show the Brotherhood Focus panel by default.",
|
||||||
|
scope: "client", config: true, type: Boolean, default: true,
|
||||||
|
onChange: (v) => { v ? GKFocusPanel.show(true) : GKFocusPanel.hide(); }
|
||||||
|
});
|
||||||
|
game.settings.register(GK_NS, "panelAutoOpen", {
|
||||||
|
name: "Auto-open when eligible",
|
||||||
|
hint: "Auto-open the panel when your selected actor (or controlled token) has Brotherhood of Psykers.",
|
||||||
|
scope: "client", config: true, type: Boolean, default: true,
|
||||||
|
onChange: () => GKFocusPanel.render()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// v13: dodatkowy hook na zmiany ustawień po stronie klienta
|
||||||
|
Hooks.on?.("clientSettingChanged", (key, value) => {
|
||||||
|
if (key === `${GK_NS}.panelVisible`) return value ? GKFocusPanel.show(true) : GKFocusPanel.hide();
|
||||||
|
if (key === `${GK_NS}.panelAutoOpen`) return GKFocusPanel.render();
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ---------------- panel ---------------- */
|
||||||
|
class GKFocusPanel {
|
||||||
|
static async getState() {
|
||||||
|
return (await game.user.getFlag(GK_NS, "focusPanel")) || {};
|
||||||
|
}
|
||||||
|
static async saveState(patch) {
|
||||||
|
const cur = await this.getState();
|
||||||
|
return game.user.setFlag(GK_NS, "focusPanel", { ...cur, ...patch });
|
||||||
|
}
|
||||||
|
static async isVisible() {
|
||||||
|
const v = game.settings.get(GK_NS, "panelVisible");
|
||||||
|
const st = await this.getState();
|
||||||
|
return v && !st.closed;
|
||||||
|
}
|
||||||
|
static async hide() {
|
||||||
|
await this.saveState({ closed: true, forced: false });
|
||||||
|
$("#gk-focus-panel").remove();
|
||||||
|
}
|
||||||
|
static async show(force = false) {
|
||||||
|
await this.saveState({ closed: false, forced: !!force });
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
static async toggle() {
|
||||||
|
const vis = await this.isVisible();
|
||||||
|
if (vis) return this.hide();
|
||||||
|
return this.show(true); // force-open nawet bez aktora GK
|
||||||
|
}
|
||||||
|
|
||||||
|
static currentActor() {
|
||||||
|
return game.user?.character || canvas.tokens?.controlled?.[0]?.actor || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async render() {
|
||||||
|
const st = await this.getState();
|
||||||
|
if (st.closed || !game.settings.get(GK_NS, "panelVisible")) { $("#gk-focus-panel").remove(); return; }
|
||||||
|
|
||||||
|
const actor = this.currentActor();
|
||||||
|
if (!st.forced) {
|
||||||
|
if (!actor) { $("#gk-focus-panel").remove(); return; }
|
||||||
|
if (!gkActorHasBrotherhood(actor) && !game.settings.get(GK_NS, "panelAutoOpen")) {
|
||||||
|
$("#gk-focus-panel").remove(); return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this._renderInternal(actor);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async _renderInternal(actor) {
|
||||||
|
const st = await this.getState();
|
||||||
|
|
||||||
|
const { total, bonus } = gkAlliesAndBonus(actor);
|
||||||
|
const x = Number.isFinite(st.x) ? st.x : (window.innerWidth - 260);
|
||||||
|
const y = Number.isFinite(st.y) ? st.y : (window.innerHeight - 120);
|
||||||
|
const w = Number.isFinite(st.w) ? st.w : 260;
|
||||||
|
const h = Number.isFinite(st.h) ? st.h : 110;
|
||||||
|
|
||||||
|
const root = $("#gk-focus-panel");
|
||||||
|
const html = $(`
|
||||||
|
<section id="gk-focus-panel" class="window-app"
|
||||||
|
style="position:fixed; left:${x}px; top:${y}px;
|
||||||
|
width:${w}px; height:${h}px; min-width:200px; min-height:80px;
|
||||||
|
resize: both; overflow:auto; background:#111A; color:#fff;
|
||||||
|
border:1px solid #444; border-radius:6px; z-index:70;">
|
||||||
|
<header class="window-header" style="cursor:move; display:flex; align-items:center; gap:.5rem; padding:.25rem .5rem; border-bottom:1px solid #444;">
|
||||||
|
<h4 class="window-title" style="margin:0; font-size:.95rem;">Brotherhood Focus</h4>
|
||||||
|
<a class="gk-close header-button" style="margin-left:auto;" title="Close"><i class="fas fa-times"></i></a>
|
||||||
|
</header>
|
||||||
|
<section class="window-content" style="padding:.4rem .6rem;">
|
||||||
|
<div><span style="opacity:.9">Allies</span>: <b>${total}</b> · <span style="opacity:.9">Bonus</span>: <b>+${bonus}</b></div>
|
||||||
|
<div style="opacity:.85; font-size:.9em; margin-top:.25rem;">Only one linked GK should use a psychic power as their <em>Action</em> per round. Subsequent powers add +1 <em>Wrath</em> die.</div>
|
||||||
|
</section>
|
||||||
|
</section>`);
|
||||||
|
|
||||||
|
if (root.length) root.replaceWith(html); else $("body").append(html);
|
||||||
|
|
||||||
|
html.find(".gk-close").off("click").on("click", async () => {
|
||||||
|
const left = parseInt(html.css("left")) || x;
|
||||||
|
const top = parseInt(html.css("top")) || y;
|
||||||
|
const ww = parseInt(html.outerWidth()) || w;
|
||||||
|
const hh = parseInt(html.outerHeight()) || h;
|
||||||
|
await this.saveState({ closed: true, forced: false, x:left, y:top, w:ww, h:hh });
|
||||||
|
html.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
const handle = html.find(".window-header");
|
||||||
|
let dragging = false, ox = 0, oy = 0;
|
||||||
|
function onMove(ev) {
|
||||||
|
if (!dragging) return;
|
||||||
|
const nx = Math.max(0, Math.min(window.innerWidth - html.outerWidth(), ev.clientX - ox));
|
||||||
|
const ny = Math.max(0, Math.min(window.innerHeight - html.outerHeight(), ev.clientY - oy));
|
||||||
|
html.css({ left: nx, top: ny });
|
||||||
|
}
|
||||||
|
async function onUp() {
|
||||||
|
if (!dragging) return;
|
||||||
|
dragging = false;
|
||||||
|
$(window).off("pointermove.gkfp").off("pointerup.gkfp");
|
||||||
|
const left = parseInt(html.css("left")) || 0;
|
||||||
|
const top = parseInt(html.css("top")) || 0;
|
||||||
|
const ww = parseInt(html.outerWidth()) || w;
|
||||||
|
const hh = parseInt(html.outerHeight()) || h;
|
||||||
|
await GKFocusPanel.saveState({ x:left, y:top, w:ww, h:hh });
|
||||||
|
}
|
||||||
|
handle.off("pointerdown.gkfp").on("pointerdown.gkfp", (ev) => {
|
||||||
|
dragging = true;
|
||||||
|
const r = html[0].getBoundingClientRect();
|
||||||
|
ox = ev.clientX - r.left; oy = ev.clientY - r.top;
|
||||||
|
$(window).on("pointermove.gkfp", onMove).on("pointerup.gkfp", onUp);
|
||||||
|
});
|
||||||
|
|
||||||
|
$(window).off("pointerup.gkfp-size").on("pointerup.gkfp-size", async () => {
|
||||||
|
if (!document.body.contains(html[0])) return;
|
||||||
|
const ww = parseInt(html.outerWidth()) || w;
|
||||||
|
const hh = parseInt(html.outerHeight()) || h;
|
||||||
|
await GKFocusPanel.saveState({ w: ww, h: hh });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------- left toolbar button (v12 & v13) ---------------- */
|
||||||
|
Hooks.on("getSceneControlButtons", (controls) => {
|
||||||
|
// v12: controls = SceneControl[]
|
||||||
|
if (Array.isArray(controls)) {
|
||||||
|
const token = controls.find(c => c?.name === "token");
|
||||||
|
if (!token) return;
|
||||||
|
if (token.tools?.some?.(t => t?.name === "gk-toggle-focus")) return;
|
||||||
|
token.tools.push({
|
||||||
|
name: "gk-toggle-focus",
|
||||||
|
title: "Toggle Brotherhood Focus Panel",
|
||||||
|
icon: "fas fa-bolt",
|
||||||
|
button: true,
|
||||||
|
visible: true,
|
||||||
|
order: 999,
|
||||||
|
onClick: () => GKFocusPanel.toggle()
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// v13: controls = Record<string, SceneControl>; tools = Record<string, SceneControlTool>
|
||||||
|
const token = controls?.token;
|
||||||
|
if (!token) return;
|
||||||
|
token.tools ??= {};
|
||||||
|
if (!token.tools["gk-toggle-focus"]) {
|
||||||
|
token.tools["gk-toggle-focus"] = {
|
||||||
|
name: "gk-toggle-focus",
|
||||||
|
title: "Toggle Brotherhood Focus Panel",
|
||||||
|
icon: "fas fa-bolt",
|
||||||
|
button: true,
|
||||||
|
visible: true,
|
||||||
|
order: 999,
|
||||||
|
onClick: () => GKFocusPanel.toggle()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ---------------- reactive hooks ---------------- */
|
||||||
|
Hooks.on("canvasReady", () => GKFocusPanel.render());
|
||||||
|
Hooks.on("controlToken", () => GKFocusPanel.render());
|
||||||
|
Hooks.on("updateCombat", () => GKFocusPanel.render());
|
||||||
|
Hooks.on("createToken", () => GKFocusPanel.render());
|
||||||
|
Hooks.on("deleteToken", () => GKFocusPanel.render());
|
||||||
Reference in New Issue
Block a user