Compare commits
5 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 566a9ad845 | |||
| 0eb8ed5430 | |||
| 737690f42a | |||
| 318498489b | |||
| 7a20b05c76 |
4 changed files with 685 additions and 12 deletions
14
README.md
14
README.md
|
|
@ -21,6 +21,18 @@ Reduces screen clutter by optimizing the Countdown Tracker widget:
|
||||||
* **For Players**: Hides the countdown widget completely if there are no active/visible countdowns.
|
* **For Players**: Hides the countdown widget completely if there are no active/visible countdowns.
|
||||||
* **For GMs**: Minimizes the countdown widget to a tiny square featuring a `+` icon when empty. Clicking the `+` icon opens the system's native Countdown Edit dialog.
|
* **For GMs**: Minimizes the countdown widget to a tiny square featuring a `+` icon when empty. Clicking the `+` icon opens the system's native Countdown Edit dialog.
|
||||||
|
|
||||||
|
### 4. Fear Tracker Customizations (On by default)
|
||||||
|
Enhances the Daggerheart Fear Tracker with improved layout options, sizing, and a convenient context menu:
|
||||||
|
* **Clean Borderless Layout**: Hides the Fear Tracker's title bar for a cleaner look. When the title bar is hidden, you can still move the tracker by clicking and dragging any empty space on it.
|
||||||
|
* **Right-Click Context Menu**: Right-clicking the Fear Tracker opens a quick-access menu to lock/unlock position, reset position or scale, and scale the UI.
|
||||||
|
* **Position Locking**: Lock the Fear Tracker's position to prevent accidental dragging or resizing.
|
||||||
|
* **Flexible Sizing & Scaling**: Scale the Fear Tracker and all of its elements (70%, 85%, 100%, 115%, or 130%) to fit your screen.
|
||||||
|
* **Perfect-Fit Auto-Resizing**: Dynamically resizes the tracker window to perfectly fit the current number of Fear tokens/rows without leaving empty borders or margins.
|
||||||
|
* **Position & Scale Resets**: Settings and buttons to instantly restore the tracker back to its default position and scale.
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
All features can be toggled on or off independently under **Configure Settings** > **Cosmo's Daggerheart Tweaks**. The *Spotlight Request Canvas Indicator* is grouped together with the HUD button option.
|
All features can be toggled on or off independently under **Configure Settings** > **Cosmo's Daggerheart Tweaks**.
|
||||||
|
* The *Spotlight Request Canvas Indicator* is grouped together with the HUD button option.
|
||||||
|
* The Fear Tracker settings allow you to hide the title bar, scale the tracker, or reset its scale and position. Position locking can also be toggled directly from the tracker's right-click context menu.
|
||||||
|
|
||||||
|
|
|
||||||
19
lang/en.json
19
lang/en.json
|
|
@ -9,5 +9,22 @@
|
||||||
"COSMOS_DH_TWEAKS.Spotlight.cancel": "Cancel Spotlight Request",
|
"COSMOS_DH_TWEAKS.Spotlight.cancel": "Cancel Spotlight Request",
|
||||||
"COSMOS_DH_TWEAKS.Spotlight.grant": "Grant Spotlight",
|
"COSMOS_DH_TWEAKS.Spotlight.grant": "Grant Spotlight",
|
||||||
"COSMOS_DH_TWEAKS.Spotlight.take": "Take Spotlight",
|
"COSMOS_DH_TWEAKS.Spotlight.take": "Take Spotlight",
|
||||||
"COSMOS_DH_TWEAKS.Countdown.add": "Add Countdown"
|
"COSMOS_DH_TWEAKS.Countdown.add": "Add Countdown",
|
||||||
|
"COSMOS_DH_TWEAKS.Settings.RemoveFearTitleBar.Name": "Remove Fear Tracker Title Bar",
|
||||||
|
"COSMOS_DH_TWEAKS.Settings.RemoveFearTitleBar.Hint": "Hides the title bar of the Fear Tracker window by default, allowing you to drag it by clicking on empty space.",
|
||||||
|
"COSMOS_DH_TWEAKS.Settings.ResetFearPosition.Name": "Reset Fear Tracker Position",
|
||||||
|
"COSMOS_DH_TWEAKS.Settings.ResetFearPosition.Hint": "Resets the Fear Tracker window back to its default position and size.",
|
||||||
|
"COSMOS_DH_TWEAKS.Settings.ResetFearPosition.ButtonText": "Reset Position",
|
||||||
|
"COSMOS_DH_TWEAKS.Settings.ResetFearPosition.Success": "Fear tracker position has been reset.",
|
||||||
|
"COSMOS_DH_TWEAKS.ContextMenu.LockPosition": "Lock Position",
|
||||||
|
"COSMOS_DH_TWEAKS.ContextMenu.UnlockPosition": "Unlock Position",
|
||||||
|
"COSMOS_DH_TWEAKS.Settings.FearTrackerScale.Name": "Fear Tracker Scale",
|
||||||
|
"COSMOS_DH_TWEAKS.Settings.FearTrackerScale.Hint": "Scale the size of the Fear Tracker window and its icons.",
|
||||||
|
"COSMOS_DH_TWEAKS.Settings.ResetFearScale.Name": "Reset Fear Tracker Scale",
|
||||||
|
"COSMOS_DH_TWEAKS.Settings.ResetFearScale.Hint": "Resets the Fear Tracker scale back to 100%.",
|
||||||
|
"COSMOS_DH_TWEAKS.Settings.ResetFearScale.ButtonText": "Reset Scale",
|
||||||
|
"COSMOS_DH_TWEAKS.Settings.ResetFearScale.Success": "Fear tracker scale has been reset.",
|
||||||
|
"COSMOS_DH_TWEAKS.ContextMenu.ResetPosition": "Reset Position",
|
||||||
|
"COSMOS_DH_TWEAKS.ContextMenu.ResetScale": "Reset Scale",
|
||||||
|
"COSMOS_DH_TWEAKS.ContextMenu.ScaleHeader": "Scale"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,397 @@ Hooks.once("init", () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
game.settings.register("cosmos-dh-tweaks", "removeFearTitleBar", {
|
||||||
|
name: "COSMOS_DH_TWEAKS.Settings.RemoveFearTitleBar.Name",
|
||||||
|
hint: "COSMOS_DH_TWEAKS.Settings.RemoveFearTitleBar.Hint",
|
||||||
|
scope: "client",
|
||||||
|
config: true,
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
onChange: () => {
|
||||||
|
if (ui.resources) {
|
||||||
|
ui.resources.updateTitleBarStatus();
|
||||||
|
ui.resources.adjustSizeToMaxFear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
game.settings.register("cosmos-dh-tweaks", "lockFearTrackerPosition", {
|
||||||
|
scope: "client",
|
||||||
|
config: false,
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
});
|
||||||
|
|
||||||
|
game.settings.register("cosmos-dh-tweaks", "resetFearPosition", {
|
||||||
|
name: "COSMOS_DH_TWEAKS.Settings.ResetFearPosition.Name",
|
||||||
|
hint: "COSMOS_DH_TWEAKS.Settings.ResetFearPosition.Hint",
|
||||||
|
scope: "client",
|
||||||
|
config: true,
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
});
|
||||||
|
|
||||||
|
game.settings.register("cosmos-dh-tweaks", "fearTrackerScale", {
|
||||||
|
name: "COSMOS_DH_TWEAKS.Settings.FearTrackerScale.Name",
|
||||||
|
hint: "COSMOS_DH_TWEAKS.Settings.FearTrackerScale.Hint",
|
||||||
|
scope: "client",
|
||||||
|
config: true,
|
||||||
|
type: Number,
|
||||||
|
range: {
|
||||||
|
min: 0.5,
|
||||||
|
max: 1.5,
|
||||||
|
step: 0.05
|
||||||
|
},
|
||||||
|
default: 1.0,
|
||||||
|
onChange: () => {
|
||||||
|
if (ui.resources && ui.resources.rendered) {
|
||||||
|
ui.resources.updateScale();
|
||||||
|
ui.resources.adjustSizeToMaxFear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
game.settings.register("cosmos-dh-tweaks", "resetFearScale", {
|
||||||
|
name: "COSMOS_DH_TWEAKS.Settings.ResetFearScale.Name",
|
||||||
|
hint: "COSMOS_DH_TWEAKS.Settings.ResetFearScale.Hint",
|
||||||
|
scope: "client",
|
||||||
|
config: true,
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
});
|
||||||
|
|
||||||
|
// Override the FearTracker class in CONFIG
|
||||||
|
const OriginalFearTracker = CONFIG.ui.resources;
|
||||||
|
if (OriginalFearTracker) {
|
||||||
|
class CosmoDhTweaksFearTracker extends OriginalFearTracker {
|
||||||
|
async _onRender(context, options) {
|
||||||
|
await super._onRender(context, options);
|
||||||
|
|
||||||
|
if (!this._eventsBound) {
|
||||||
|
this._eventsBound = true;
|
||||||
|
|
||||||
|
this.element.addEventListener('contextmenu', (e) => {
|
||||||
|
this._showContextMenu(e);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.element.addEventListener('mousedown', (e) => {
|
||||||
|
const isLocked = game.settings.get("cosmos-dh-tweaks", "lockFearTrackerPosition");
|
||||||
|
if (isLocked) {
|
||||||
|
if (e.target.closest('header, .window-resize-handle')) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeTitleBar = game.settings.get("cosmos-dh-tweaks", "removeFearTitleBar");
|
||||||
|
if (removeTitleBar && !isLocked) {
|
||||||
|
if (e.button !== 0) return;
|
||||||
|
if (e.target.closest('i, .controls, button, input, a, .window-resize-handle')) return;
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
this._startDrag(e);
|
||||||
|
}
|
||||||
|
}, { capture: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateTitleBarStatus();
|
||||||
|
this.updateLockStatus();
|
||||||
|
this.updateScale();
|
||||||
|
this.adjustSizeToMaxFear();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateScale() {
|
||||||
|
if (!this.element) return;
|
||||||
|
const scale = game.settings.get("cosmos-dh-tweaks", "fearTrackerScale") ?? 1.0;
|
||||||
|
this.element.style.setProperty('--cosmo-fear-scale', scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTitleBarStatus() {
|
||||||
|
if (!this.element) return;
|
||||||
|
const removeTitleBar = game.settings.get("cosmos-dh-tweaks", "removeFearTitleBar");
|
||||||
|
if (removeTitleBar) {
|
||||||
|
this.element.classList.add("cosmo-no-title-bar");
|
||||||
|
} else {
|
||||||
|
this.element.classList.remove("cosmo-no-title-bar");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateLockStatus() {
|
||||||
|
if (!this.element) return;
|
||||||
|
const isLocked = game.settings.get("cosmos-dh-tweaks", "lockFearTrackerPosition");
|
||||||
|
if (isLocked) {
|
||||||
|
this.element.classList.add("cosmo-position-locked");
|
||||||
|
} else {
|
||||||
|
this.element.classList.remove("cosmo-position-locked");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getLayoutMeasurements() {
|
||||||
|
const content = this.element.querySelector('.window-content');
|
||||||
|
const grid = this.element.querySelector('#resource-fear');
|
||||||
|
const skulls = grid ? grid.querySelectorAll('i') : [];
|
||||||
|
|
||||||
|
let skullW = 48;
|
||||||
|
let skullH = 48;
|
||||||
|
let gapX = 4;
|
||||||
|
let gapY = 8;
|
||||||
|
let padX = 16;
|
||||||
|
let padY = 16;
|
||||||
|
let headerH = 0;
|
||||||
|
|
||||||
|
if (content) {
|
||||||
|
const style = window.getComputedStyle(content);
|
||||||
|
const parsedPadX = parseFloat(style.paddingLeft) + parseFloat(style.paddingRight);
|
||||||
|
const parsedPadY = parseFloat(style.paddingTop) + parseFloat(style.paddingBottom);
|
||||||
|
if (!isNaN(parsedPadX)) padX = parsedPadX;
|
||||||
|
if (!isNaN(parsedPadY)) padY = parsedPadY;
|
||||||
|
}
|
||||||
|
|
||||||
|
const header = this.element.querySelector('header.window-header');
|
||||||
|
if (header && window.getComputedStyle(header).display !== 'none') {
|
||||||
|
headerH = header.offsetHeight || 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skulls.length > 0) {
|
||||||
|
const first = skulls[0];
|
||||||
|
const firstRect = first.getBoundingClientRect();
|
||||||
|
if (firstRect.width > 0) skullW = firstRect.width;
|
||||||
|
if (firstRect.height > 0) skullH = firstRect.height;
|
||||||
|
|
||||||
|
if (skulls.length > 1) {
|
||||||
|
let second = null;
|
||||||
|
for (let i = 1; i < skulls.length; i++) {
|
||||||
|
const rect = skulls[i].getBoundingClientRect();
|
||||||
|
if (Math.abs(rect.top - firstRect.top) < 2 && rect.width > 0) {
|
||||||
|
second = rect;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (second) {
|
||||||
|
gapX = second.left - firstRect.right;
|
||||||
|
}
|
||||||
|
|
||||||
|
let nextRow = null;
|
||||||
|
for (let i = 1; i < skulls.length; i++) {
|
||||||
|
const rect = skulls[i].getBoundingClientRect();
|
||||||
|
if (rect.top - firstRect.bottom > 2 && rect.width > 0) {
|
||||||
|
nextRow = rect;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nextRow) {
|
||||||
|
gapY = nextRow.top - firstRect.bottom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { skullW, skullH, gapX, gapY, padX, padY, headerH };
|
||||||
|
}
|
||||||
|
|
||||||
|
calculatePerfectSize(targetWidth, measurements) {
|
||||||
|
const scale = game.settings.get("cosmos-dh-tweaks", "fearTrackerScale") ?? 1.0;
|
||||||
|
|
||||||
|
// Divide measurements by scale to work with unscaled base measurements
|
||||||
|
const skullW = measurements.skullW / scale;
|
||||||
|
const skullH = measurements.skullH / scale;
|
||||||
|
const gapX = measurements.gapX / scale;
|
||||||
|
const gapY = measurements.gapY / scale;
|
||||||
|
const padX = measurements.padX / scale;
|
||||||
|
const padY = measurements.padY / scale;
|
||||||
|
const headerH = measurements.headerH / scale;
|
||||||
|
|
||||||
|
const display = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance).displayFear;
|
||||||
|
const maxFear = this.maxFear;
|
||||||
|
|
||||||
|
// Subtract the fixed border buffer from targetWidth before unscaling to get exact content size
|
||||||
|
const targetWUnscaled = targetWidth ? Math.max(0, (targetWidth - 8) / scale) : 222;
|
||||||
|
|
||||||
|
if (display === 'bar') {
|
||||||
|
const barElement = this.element.querySelector('#resource-fear');
|
||||||
|
const barH = barElement ? (barElement.getBoundingClientRect().height / scale) : 30;
|
||||||
|
const perfectH = barH + padY + headerH;
|
||||||
|
const minW = 200;
|
||||||
|
const perfectW = Math.max(minW, targetWUnscaled);
|
||||||
|
|
||||||
|
return {
|
||||||
|
width: Math.ceil(perfectW * scale) + 8,
|
||||||
|
height: Math.ceil(perfectH * scale) + 6
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
let cols = Math.round((targetWUnscaled - padX + gapX) / (skullW + gapX));
|
||||||
|
cols = Math.max(1, Math.min(maxFear, cols));
|
||||||
|
|
||||||
|
const perfectW = cols * skullW + (cols - 1) * gapX + padX;
|
||||||
|
const rows = Math.ceil(maxFear / cols);
|
||||||
|
const perfectH = rows * skullH + (rows - 1) * gapY + padY + headerH;
|
||||||
|
|
||||||
|
return {
|
||||||
|
width: Math.ceil(perfectW * scale) + 8,
|
||||||
|
height: Math.ceil(perfectH * scale) + 6
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
adjustSizeToMaxFear() {
|
||||||
|
if (!this.element) return;
|
||||||
|
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
if (!this.element) return;
|
||||||
|
const measurements = this.getLayoutMeasurements();
|
||||||
|
const pos = this.position;
|
||||||
|
|
||||||
|
const perfect = this.calculatePerfectSize(pos.width, measurements);
|
||||||
|
|
||||||
|
if (Math.abs(pos.width - perfect.width) > 1 || Math.abs(pos.height - perfect.height) > 1) {
|
||||||
|
this.setPosition(perfect);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setPosition(position = {}) {
|
||||||
|
if (!this.element) return super.setPosition(position);
|
||||||
|
|
||||||
|
const measurements = this.getLayoutMeasurements();
|
||||||
|
if (position.width !== undefined || position.height !== undefined) {
|
||||||
|
const currentW = position.width !== undefined ? position.width : this.position.width;
|
||||||
|
const perfect = this.calculatePerfectSize(currentW, measurements);
|
||||||
|
|
||||||
|
position.width = perfect.width;
|
||||||
|
position.height = perfect.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.setPosition(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
_startDrag(e) {
|
||||||
|
let isDragging = true;
|
||||||
|
const startX = e.clientX;
|
||||||
|
const startY = e.clientY;
|
||||||
|
const pos = this.position;
|
||||||
|
const startLeft = pos.left;
|
||||||
|
const startTop = pos.top;
|
||||||
|
|
||||||
|
const onMouseMove = (event) => {
|
||||||
|
if (!isDragging) return;
|
||||||
|
const dx = event.clientX - startX;
|
||||||
|
const dy = event.clientY - startY;
|
||||||
|
|
||||||
|
this.setPosition({
|
||||||
|
left: startLeft + dx,
|
||||||
|
top: startTop + dy
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMouseUp = () => {
|
||||||
|
isDragging = false;
|
||||||
|
document.removeEventListener('mousemove', onMouseMove);
|
||||||
|
document.removeEventListener('mouseup', onMouseUp);
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('mousemove', onMouseMove);
|
||||||
|
document.addEventListener('mouseup', onMouseUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
_showContextMenu(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const existing = document.querySelector('.cosmo-fear-context-menu');
|
||||||
|
if (existing) existing.remove();
|
||||||
|
|
||||||
|
const isLocked = game.settings.get("cosmos-dh-tweaks", "lockFearTrackerPosition");
|
||||||
|
const currentScale = game.settings.get("cosmos-dh-tweaks", "fearTrackerScale") ?? 1.0;
|
||||||
|
const menu = document.createElement('div');
|
||||||
|
menu.className = 'cosmo-fear-context-menu';
|
||||||
|
|
||||||
|
const lockText = isLocked
|
||||||
|
? `<i class="fa-solid fa-lock-open"></i> ${game.i18n.localize("COSMOS_DH_TWEAKS.ContextMenu.UnlockPosition")}`
|
||||||
|
: `<i class="fa-solid fa-lock"></i> ${game.i18n.localize("COSMOS_DH_TWEAKS.ContextMenu.LockPosition")}`;
|
||||||
|
|
||||||
|
menu.innerHTML = `
|
||||||
|
<ul class="cosmo-context-menu-list">
|
||||||
|
<li class="cosmo-context-menu-item lock-toggle" data-action="toggle-lock">
|
||||||
|
${lockText}
|
||||||
|
</li>
|
||||||
|
<li class="cosmo-context-menu-item reset-position" data-action="reset-position">
|
||||||
|
<i class="fa-solid fa-arrows-rotate"></i> ${game.i18n.localize("COSMOS_DH_TWEAKS.ContextMenu.ResetPosition")}
|
||||||
|
</li>
|
||||||
|
<li class="cosmo-context-menu-item reset-scale" data-action="reset-scale">
|
||||||
|
<i class="fa-solid fa-expand"></i> ${game.i18n.localize("COSMOS_DH_TWEAKS.ContextMenu.ResetScale")}
|
||||||
|
</li>
|
||||||
|
<li class="cosmo-context-menu-header">${game.i18n.localize("COSMOS_DH_TWEAKS.ContextMenu.ScaleHeader")}</li>
|
||||||
|
<li class="cosmo-context-menu-scale-controls">
|
||||||
|
<button class="scale-btn ${Math.abs(currentScale - 0.7) < 0.01 ? 'active' : ''}" data-scale="0.7">70%</button>
|
||||||
|
<button class="scale-btn ${Math.abs(currentScale - 0.85) < 0.01 ? 'active' : ''}" data-scale="0.85">85%</button>
|
||||||
|
<button class="scale-btn ${Math.abs(currentScale - 1.0) < 0.01 ? 'active' : ''}" data-scale="1.0">100%</button>
|
||||||
|
<button class="scale-btn ${Math.abs(currentScale - 1.15) < 0.01 ? 'active' : ''}" data-scale="1.15">115%</button>
|
||||||
|
<button class="scale-btn ${Math.abs(currentScale - 1.3) < 0.01 ? 'active' : ''}" data-scale="1.3">130%</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
`;
|
||||||
|
|
||||||
|
menu.style.position = 'fixed';
|
||||||
|
menu.style.left = `${e.clientX}px`;
|
||||||
|
menu.style.top = `${e.clientY}px`;
|
||||||
|
menu.style.zIndex = 100000;
|
||||||
|
|
||||||
|
document.body.appendChild(menu);
|
||||||
|
|
||||||
|
menu.querySelector('[data-action="toggle-lock"]').addEventListener('click', async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
const currentLock = game.settings.get("cosmos-dh-tweaks", "lockFearTrackerPosition");
|
||||||
|
await game.settings.set("cosmos-dh-tweaks", "lockFearTrackerPosition", !currentLock);
|
||||||
|
this.updateLockStatus();
|
||||||
|
menu.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
menu.querySelector('[data-action="reset-position"]').addEventListener('click', async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
await game.user.unsetFlag(CONFIG.DH.id, 'app.resources.position');
|
||||||
|
if (this.rendered) {
|
||||||
|
const defaultPos = CONFIG.ui.resources.DEFAULT_OPTIONS.position;
|
||||||
|
await this.setPosition(defaultPos);
|
||||||
|
}
|
||||||
|
ui.notifications.info(game.i18n.localize("COSMOS_DH_TWEAKS.Settings.ResetFearPosition.Success"));
|
||||||
|
menu.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
menu.querySelector('[data-action="reset-scale"]').addEventListener('click', async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
await game.settings.set("cosmos-dh-tweaks", "fearTrackerScale", 1.0);
|
||||||
|
this.updateScale();
|
||||||
|
this.adjustSizeToMaxFear();
|
||||||
|
ui.notifications.info(game.i18n.localize("COSMOS_DH_TWEAKS.Settings.ResetFearScale.Success"));
|
||||||
|
menu.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
menu.querySelectorAll('.scale-btn').forEach(btn => {
|
||||||
|
btn.addEventListener('click', async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
const newScale = parseFloat(btn.dataset.scale);
|
||||||
|
await game.settings.set("cosmos-dh-tweaks", "fearTrackerScale", newScale);
|
||||||
|
this.updateScale();
|
||||||
|
this.adjustSizeToMaxFear();
|
||||||
|
menu.remove();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const closeMenu = (event) => {
|
||||||
|
if (!menu.contains(event.target)) {
|
||||||
|
menu.remove();
|
||||||
|
document.removeEventListener('click', closeMenu);
|
||||||
|
document.removeEventListener('contextmenu', closeMenu);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
setTimeout(() => {
|
||||||
|
document.addEventListener('click', closeMenu);
|
||||||
|
document.addEventListener('contextmenu', closeMenu);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CONFIG.ui.resources = CosmoDhTweaksFearTracker;
|
||||||
|
}
|
||||||
|
|
||||||
// Override the Countdown class in CONFIG
|
// Override the Countdown class in CONFIG
|
||||||
const OriginalCountdowns = CONFIG.ui.countdowns;
|
const OriginalCountdowns = CONFIG.ui.countdowns;
|
||||||
if (OriginalCountdowns) {
|
if (OriginalCountdowns) {
|
||||||
|
|
@ -295,16 +686,20 @@ Hooks.on("refreshToken", (token, options) => {
|
||||||
bg.endFill();
|
bg.endFill();
|
||||||
container.addChild(bg);
|
container.addChild(bg);
|
||||||
|
|
||||||
// Draw FontAwesome solid e05d hand sparkles icon
|
// FontAwesome hand-sparkles SVG data URI
|
||||||
const style = new PIXI.TextStyle({
|
const handSparklesSvg = `
|
||||||
fontFamily: '"Font Awesome 6 Free"',
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" width="640" height="512">
|
||||||
fontWeight: "900",
|
<path fill="#ffffff" d="M320 0c17.7 0 32 14.3 32 32l0 208c0 8.8 7.2 16 16 16s16-7.2 16-16l0-176c0-17.7 14.3-32 32-32s32 14.3 32 32l0 176c0 8.8 7.2 16 16 16s16-7.2 16-16l0-112c0-17.7 14.3-32 32-32s32 14.3 32 32l0 178.2c-19.2 5.4-34.7 20.4-40.4 40.3l-6.5 22.7-22.7 6.5c-25.2 7.2-42.5 30.2-42.5 56.4 0 22.1 12.4 42 31.4 51.9-27.5 17.8-60.2 28.1-95.4 28.1l-19.2 0c-59.6 0-116.9-22.9-160-64L76.4 341c-16-15.2-16.6-40.6-1.4-56.6s40.6-16.6 56.6-1.4l60.5 57.6c0-1.5-.1-3.1-.1-4.6l0-272c0-17.7 14.3-32 32-32s32 14.3 32 32l0 176c0 8.8 7.2 16 16 16s16-7.2 16-16l0-208c0-17.7 14.3-32 32-32zm-7.3 326.6c-1.1-3.9-4.7-6.6-8.7-6.6s-7.6 2.7-8.7 6.6l-7.3 25.4-25.4 7.3c-3.9 1.1-6.6 4.7-6.6 8.7s2.7 7.6 6.6 8.7l25.4 7.3 7.3 25.4c1.1 3.9 4.7 6.6 8.7 6.6s7.6-2.7 8.7-6.6l7.3-25.4 25.4-7.3c3.9-1.1 6.6-4.7 6.6-8.7s-2.7-7.6-6.6-8.7l-25.4-7.3-7.3-25.4zM104 120l48.3 13.8c4.6 1.3 7.7 5.5 7.7 10.2s-3.1 8.9-7.7 10.2L104 168 90.2 216.3c-1.3 4.6-5.5 7.7-10.2 7.7s-8.9-3.1-10.2-7.7L56 168 7.7 154.2C3.1 152.9 0 148.7 0 144s3.1-8.9 7.7-10.2L56 120 69.8 71.7C71.1 67.1 75.3 64 80 64s8.9 3.1 10.2 7.7L104 120zM584 408l48.3 13.8c4.6 1.3 7.7 5.5 7.7 10.2s-3.1 8.9-7.7 10.2L584 456 570.2 504.3c-1.3 4.6-5.5 7.7-10.2 7.7s-8.9-3.1-10.2-7.7L536 456 487.7 442.2c-4.6-1.3-7.7-5.5-7.7-10.2s3.1-8.9 7.7-10.2L536 408 549.8 359.7c1.3-4.6 5.5-7.7 10.2-7.7s8.9 3.1 10.2 7.7L584 408z"/>
|
||||||
fontSize: 13,
|
</svg>
|
||||||
fill: "#ffffff"
|
`;
|
||||||
});
|
const svgDataUri = "data:image/svg+xml;base64," + btoa(unescape(encodeURIComponent(handSparklesSvg)));
|
||||||
const text = new PIXI.Text("\ue05d", style);
|
const handTexture = PIXI.Texture.from(svgDataUri);
|
||||||
text.anchor.set(0.5);
|
const handSprite = new PIXI.Sprite(handTexture);
|
||||||
container.addChild(text);
|
handSprite.anchor.set(0.5);
|
||||||
|
handSprite.width = 13;
|
||||||
|
handSprite.height = 13 / (640 / 512);
|
||||||
|
handSprite.position.set(0, -0.5);
|
||||||
|
container.addChild(handSprite);
|
||||||
|
|
||||||
token.cosmoSpotlightIndicator = token.addChild(container);
|
token.cosmoSpotlightIndicator = token.addChild(container);
|
||||||
}
|
}
|
||||||
|
|
@ -325,3 +720,63 @@ Hooks.on("destroyToken", (token) => {
|
||||||
token.cosmoSpotlightIndicator = null;
|
token.cosmoSpotlightIndicator = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Hooks.on("renderSettingsConfig", (app, html, data) => {
|
||||||
|
const posInput = html.querySelector
|
||||||
|
? html.querySelector('[name="cosmos-dh-tweaks.resetFearPosition"]')
|
||||||
|
: html.find('[name="cosmos-dh-tweaks.resetFearPosition"]')[0];
|
||||||
|
|
||||||
|
if (posInput) {
|
||||||
|
const formGroup = posInput.closest('.form-group');
|
||||||
|
if (formGroup) {
|
||||||
|
const formFields = formGroup.querySelector('.form-fields');
|
||||||
|
if (formFields) {
|
||||||
|
const button = document.createElement('button');
|
||||||
|
button.type = 'button';
|
||||||
|
button.className = 'cosmo-reset-fear-pos-btn';
|
||||||
|
button.innerHTML = `<i class="fa-solid fa-arrows-rotate"></i> ${game.i18n.localize("COSMOS_DH_TWEAKS.Settings.ResetFearPosition.ButtonText")}`;
|
||||||
|
button.addEventListener('click', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
await game.user.unsetFlag(CONFIG.DH.id, 'app.resources.position');
|
||||||
|
if (ui.resources && ui.resources.rendered) {
|
||||||
|
const defaultPos = CONFIG.ui.resources.DEFAULT_OPTIONS.position;
|
||||||
|
await ui.resources.setPosition(defaultPos);
|
||||||
|
}
|
||||||
|
ui.notifications.info(game.i18n.localize("COSMOS_DH_TWEAKS.Settings.ResetFearPosition.Success"));
|
||||||
|
});
|
||||||
|
|
||||||
|
formFields.innerHTML = '';
|
||||||
|
formFields.appendChild(button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const scaleInput = html.querySelector
|
||||||
|
? html.querySelector('[name="cosmos-dh-tweaks.resetFearScale"]')
|
||||||
|
: html.find('[name="cosmos-dh-tweaks.resetFearScale"]')[0];
|
||||||
|
|
||||||
|
if (scaleInput) {
|
||||||
|
const formGroup = scaleInput.closest('.form-group');
|
||||||
|
if (formGroup) {
|
||||||
|
const formFields = formGroup.querySelector('.form-fields');
|
||||||
|
if (formFields) {
|
||||||
|
const button = document.createElement('button');
|
||||||
|
button.type = 'button';
|
||||||
|
button.className = 'cosmo-reset-fear-scale-btn';
|
||||||
|
button.innerHTML = `<i class="fa-solid fa-arrows-rotate"></i> ${game.i18n.localize("COSMOS_DH_TWEAKS.Settings.ResetFearScale.ButtonText")}`;
|
||||||
|
button.addEventListener('click', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
await game.settings.set("cosmos-dh-tweaks", "fearTrackerScale", 1.0);
|
||||||
|
if (ui.resources && ui.resources.rendered) {
|
||||||
|
ui.resources.updateScale();
|
||||||
|
ui.resources.adjustSizeToMaxFear();
|
||||||
|
}
|
||||||
|
ui.notifications.info(game.i18n.localize("COSMOS_DH_TWEAKS.Settings.ResetFearScale.Success"));
|
||||||
|
});
|
||||||
|
|
||||||
|
formFields.innerHTML = '';
|
||||||
|
formFields.appendChild(button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -103,3 +103,192 @@
|
||||||
border: 1px solid var(--golden, #c5a45e) !important;
|
border: 1px solid var(--golden, #c5a45e) !important;
|
||||||
box-shadow: 0 0 5px rgba(197, 164, 94, 0.5) !important;
|
box-shadow: 0 0 5px rgba(197, 164, 94, 0.5) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Fear Tracker Window Customizations */
|
||||||
|
#resources.cosmo-no-title-bar header.window-header {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#resources.cosmo-no-title-bar {
|
||||||
|
min-height: 4rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#resources.cosmo-position-locked .window-resize-handle {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Context menu styling */
|
||||||
|
.cosmo-fear-context-menu {
|
||||||
|
background: rgba(24, 22, 46, 0.95);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 2px solid rgb(153, 122, 79);
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.5);
|
||||||
|
padding: 4px 0;
|
||||||
|
min-width: 150px;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cosmo-context-menu-list {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cosmo-context-menu-item {
|
||||||
|
padding: 8px 12px;
|
||||||
|
color: #d3d3d3;
|
||||||
|
font-size: 13px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
transition: background-color 0.2s, color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cosmo-context-menu-item:hover {
|
||||||
|
background-color: rgba(197, 164, 94, 0.2);
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cosmo-context-menu-item i {
|
||||||
|
width: 16px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.cosmo-reset-fear-pos-btn,
|
||||||
|
button.cosmo-reset-fear-scale-btn {
|
||||||
|
background: rgba(197, 164, 94, 0.1);
|
||||||
|
border: 1px solid rgb(153, 122, 79);
|
||||||
|
color: #d3d3d3;
|
||||||
|
padding: 4px 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-family: inherit;
|
||||||
|
transition: background-color 0.2s, color 0.2s, border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.cosmo-reset-fear-pos-btn:hover,
|
||||||
|
button.cosmo-reset-fear-scale-btn:hover {
|
||||||
|
background: rgba(197, 164, 94, 0.25);
|
||||||
|
border-color: #ffffff;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fear Tracker Scaling */
|
||||||
|
#resources {
|
||||||
|
--cosmo-fear-scale: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scale the skull icons */
|
||||||
|
#resources #resource-fear i {
|
||||||
|
width: calc(3rem * var(--cosmo-fear-scale, 1)) !important;
|
||||||
|
height: calc(3rem * var(--cosmo-fear-scale, 1)) !important;
|
||||||
|
font-size: calc(1.125rem * var(--cosmo-fear-scale, 1)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scale hover state font-size for GMs */
|
||||||
|
#resources.isGM #resource-fear i:hover {
|
||||||
|
font-size: calc(1.25rem * var(--cosmo-fear-scale, 1)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scale the control buttons (+ and -) */
|
||||||
|
#resources #resource-fear .controls {
|
||||||
|
width: calc(30px * var(--cosmo-fear-scale, 1)) !important;
|
||||||
|
height: calc(30px * var(--cosmo-fear-scale, 1)) !important;
|
||||||
|
font-size: calc(1.25rem * var(--cosmo-fear-scale, 1)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#resources #resource-fear .controls:hover {
|
||||||
|
font-size: calc(1.5rem * var(--cosmo-fear-scale, 1)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scale the resource bar */
|
||||||
|
#resources #resource-fear .resource-bar {
|
||||||
|
font-size: calc(1.25rem * var(--cosmo-fear-scale, 1)) !important;
|
||||||
|
padding: calc(0.25rem * var(--cosmo-fear-scale, 1)) calc(0.5rem * var(--cosmo-fear-scale, 1)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scale the window padding and flex gap */
|
||||||
|
#resources .window-content {
|
||||||
|
padding: calc(0.5rem * var(--cosmo-fear-scale, 1)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#resources #resource-fear {
|
||||||
|
gap: calc(0.5rem * var(--cosmo-fear-scale, 1)) calc(0.25rem * var(--cosmo-fear-scale, 1)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scale the window header */
|
||||||
|
#resources header.window-header {
|
||||||
|
font-size: calc(14px * var(--cosmo-fear-scale, 1)) !important;
|
||||||
|
min-height: calc(30px * var(--cosmo-fear-scale, 1)) !important;
|
||||||
|
height: calc(30px * var(--cosmo-fear-scale, 1)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#resources header.window-header h1 {
|
||||||
|
font-size: calc(14px * var(--cosmo-fear-scale, 1)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#resources header.window-header button,
|
||||||
|
#resources header.window-header a,
|
||||||
|
#resources header.window-header i {
|
||||||
|
font-size: calc(14px * var(--cosmo-fear-scale, 1)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#resources header.window-header button,
|
||||||
|
#resources header.window-header a {
|
||||||
|
width: calc(30px * var(--cosmo-fear-scale, 1)) !important;
|
||||||
|
height: calc(30px * var(--cosmo-fear-scale, 1)) !important;
|
||||||
|
line-height: calc(30px * var(--cosmo-fear-scale, 1)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Context menu scale controls styling */
|
||||||
|
.cosmo-context-menu-header {
|
||||||
|
padding: 6px 12px 2px 12px;
|
||||||
|
font-size: 11px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #a49a85;
|
||||||
|
font-weight: bold;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
border-top: 1px solid rgba(153, 122, 79, 0.3);
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cosmo-context-menu-scale-controls {
|
||||||
|
display: flex;
|
||||||
|
padding: 4px 12px 8px 12px;
|
||||||
|
gap: 4px;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cosmo-context-menu-scale-controls button.scale-btn {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border: 1px solid rgba(153, 122, 79, 0.4);
|
||||||
|
color: #d3d3d3;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 11px;
|
||||||
|
font-family: inherit;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cosmo-context-menu-scale-controls button.scale-btn:hover {
|
||||||
|
background: rgba(197, 164, 94, 0.25);
|
||||||
|
border-color: #ffffff;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cosmo-context-menu-scale-controls button.scale-btn.active {
|
||||||
|
background: rgba(197, 164, 94, 0.4);
|
||||||
|
border-color: rgb(197, 164, 94);
|
||||||
|
color: #ffffff;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue