Get the delve tour incrementer changes working and affecting fate as well

This commit is contained in:
Oliver-Akins 2025-03-01 23:40:39 -07:00
parent 7639962130
commit 110823a26b
9 changed files with 174 additions and 14 deletions

View file

@ -118,6 +118,20 @@
"condensedRange": { "condensedRange": {
"name": "Condense Weapon Range Input", "name": "Condense Weapon Range Input",
"hint": "With this enabled, the weapon range will be displayed as \"X / Y\" when editing a weapon. While disabled it will be as displayed as two different rows, one for Short Range and one for Long Range" "hint": "With this enabled, the weapon range will be displayed as \"X / Y\" when editing a weapon. While disabled it will be as displayed as two different rows, one for Short Range and one for Long Range"
},
"sandsOfFateInitial": {
"name": "Sands of Fate Initial",
"hint": "What value should The Hourglass reset to when a Cryptic Event occurs"
},
"onCrypticEvent": {
"name": "Cryptic Event Alert",
"hint": "What happens when a cryptic event occurs by clicking the \"Next Delve Tour\" button in the HUD",
"options": {
"notif": "Notification",
"pause": "Pause Game",
"both": "Notification and Pause Game",
"nothing": "Do Nothing"
}
} }
}, },
"Apps": { "Apps": {
@ -150,6 +164,9 @@
}, },
"warn": { "warn": {
"cannot-go-negative": "\"{name}\" is unable to be a negative number." "cannot-go-negative": "\"{name}\" is unable to be a negative number."
},
"info": {
"cryptic-event-alert": "A Cryptic Event Has Occured!"
} }
}, },
"tooltips": { "tooltips": {

View file

@ -1,6 +1,7 @@
import { distanceBetweenFates } from "../utils/distanceBetweenFates.mjs"; import { distanceBetweenFates, nextFate, previousFate } from "../utils/fates.mjs";
import { filePath } from "../consts.mjs"; import { filePath } from "../consts.mjs";
import { gameTerms } from "../gameTerms.mjs"; import { gameTerms } from "../gameTerms.mjs";
import { localizer } from "../utils/Localizer.mjs";
import { Logger } from "../utils/Logger.mjs"; import { Logger } from "../utils/Logger.mjs";
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
@ -128,35 +129,96 @@ export class DelveDiceHUD extends HandlebarsApplicationMixin(ApplicationV2) {
this.#animateCompassTo(); this.#animateCompassTo();
}; };
if (parts.includes(`sandsOfFate`)) {}; if (parts.includes(`sandsOfFate`)) {
this.#animateSandsTo();
};
}; };
#animateCompassTo(newFate) { #animateCompassTo(newFate) {
if (newFate === this._currentFate) { return };
/** @type {HTMLElement|undefined} */ /** @type {HTMLElement|undefined} */
const pointer = this.element.querySelector(`.compass-pointer`); const pointer = this.element.querySelector(`.compass-pointer`);
if (!pointer) { return }; if (!pointer) { return };
newFate ??= game.settings.get(`ripcrypt`, `currentFate`);
let distance = distanceBetweenFates(this._currentFate, newFate); let distance = distanceBetweenFates(this._currentFate, newFate);
Logger.table({ newFate, fate: this._currentFate, distance, _rotation: this._rotation });
if (distance === 3) { distance = -1 }; if (distance === 3) { distance = -1 };
this._rotation += distance * 90; this._rotation += distance * 90;
pointer.style.setProperty(`transform`, `rotate(${this._rotation}deg)`); pointer.style.setProperty(`transform`, `rotate(${this._rotation}deg)`);
this._currentFate = newFate;
};
#animateSandsTo(newSands) {
/** @type {HTMLElement|undefined} */
const sands = this.element.querySelector(`.sands-value`);
if (!sands) { return };
newSands ??= game.settings.get(`ripcrypt`, `sandsOfFate`);
sands.innerHTML = newSands;
this._sandsOfFate = newSands;
}; };
// #endregion // #endregion
// #region Actions // #region Actions
static async #tourDelta() {}; /** @this {DelveDiceHUD} */
static async #tourDelta(_event, element) {
const delta = parseInt(element.dataset.delta);
const initial = game.settings.get(`ripcrypt`, `sandsOfFateInitial`);
let newSands = this._sandsOfFate + delta;
if (newSands > initial) {
Logger.info(`Cannot go to a previous Delve Tour above the initial value`);
return;
};
if (newSands === 0) {
newSands = initial;
await this.alertCrypticEvent();
};
switch (Math.sign(delta)) {
case -1: {
game.settings.set(`ripcrypt`, `currentFate`, nextFate(this._currentFate));
break;
}
case 1: {
game.settings.set(`ripcrypt`, `currentFate`, previousFate(this._currentFate));
break;
}
};
this.#animateSandsTo(newSands);
game.settings.set(`ripcrypt`, `sandsOfFate`, newSands);
};
/** @this {DelveDiceHUD} */ /** @this {DelveDiceHUD} */
static async #setFate(_event, element) { static async #setFate(_event, element) {
const fate = element.dataset.toFate; const fate = element.dataset.toFate;
this.#animateCompassTo(fate); this.#animateCompassTo(fate);
// must be done after animate, otherwise it won't change the rotation
this._currentFate = fate;
game.settings.set(`ripcrypt`, `currentFate`, fate); game.settings.set(`ripcrypt`, `currentFate`, fate);
}; };
// #endregion // #endregion
// #region Public API
async alertCrypticEvent() {
const alertType = game.settings.get(`ripcrypt`, `onCrypticEvent`);
if (alertType === `nothing`) { return };
if ([`both`, `notif`].includes(alertType)) {
ui.notifications.info(
localizer(`RipCrypt.notifs.info.cryptic-event-alert`),
{ console: false },
);
};
if ([`both`, `pause`].includes(alertType) && game.user.isGM) {
game.togglePause(true, { broadcast: true });
};
};
// #endregion
}; };

View file

@ -6,6 +6,7 @@ import { HeroSummaryCardV1 } from "./Apps/ActorSheets/HeroSummaryCardV1.mjs";
import { RichEditor } from "./Apps/RichEditor.mjs"; import { RichEditor } from "./Apps/RichEditor.mjs";
// Util imports // Util imports
import { distanceBetweenFates, nextFate, previousFate } from "./utils/fates.mjs";
import { documentSorter } from "./consts.mjs"; import { documentSorter } from "./consts.mjs";
const { deepFreeze } = foundry.utils; const { deepFreeze } = foundry.utils;
@ -24,6 +25,9 @@ Object.defineProperty(
}, },
utils: { utils: {
documentSorter, documentSorter,
distanceBetweenFates,
nextFate,
previousFate,
}, },
}), }),
writable: false, writable: false,

View file

@ -1,3 +1,8 @@
import { gameTerms } from "../gameTerms.mjs";
const { StringField } = foundry.data.fields;
const { FatePath } = gameTerms;
export function registerMetaSettings() { export function registerMetaSettings() {
game.settings.register(`ripcrypt`, `dc`, { game.settings.register(`ripcrypt`, `dc`, {
scope: `world`, scope: `world`,
@ -15,15 +20,23 @@ export function registerMetaSettings() {
initial: 8, initial: 8,
config: false, config: false,
requiresReload: false, requiresReload: false,
onChange: async () => {}, onChange: async () => {
ui.delveDice.animate({ parts: [`sandsOfFate`] });
},
}); });
game.settings.register(`ripcrypt`, `currentFate`, { game.settings.register(`ripcrypt`, `currentFate`, {
scope: `world`, scope: `world`,
type: String, type: new StringField({
blank: false,
nullable: false,
initial: FatePath.NORTH,
}),
config: false, config: false,
requiresReload: false, requiresReload: false,
onChange: async () => {}, onChange: async () => {
ui.delveDice.animate({ parts: [`fateCompass`] });
},
}); });
game.settings.register(`ripcrypt`, `whoFirst`, { game.settings.register(`ripcrypt`, `whoFirst`, {

View file

@ -1,10 +1,51 @@
const { NumberField, StringField } = foundry.data.fields;
export function registerWorldSettings() { export function registerWorldSettings() {
game.settings.register(`ripcrypt`, `showDelveTour`, { game.settings.register(`ripcrypt`, `showDelveTour`, {
name: `Delve Tour Popup`, name: `Delve Tour Popup`,
scope: `world`, scope: `world`,
type: Boolean, type: Boolean,
config: true, config: false,
default: true, default: true,
requiresReload: false, requiresReload: false,
}); });
game.settings.register(`ripcrypt`, `sandsOfFateInitial`, {
name: `RipCrypt.setting.sandsOfFateInitial.name`,
hint: `RipCrypt.setting.sandsOfFateInitial.hint`,
scope: `world`,
config: true,
requiresReload: false,
type: new NumberField({
required: true,
min: 1,
step: 1,
max: 10,
initial: 8,
}),
onChange: async (newInitialSands) => {
const currentSands = game.settings.get(`ripcrypt`, `sandsOfFate`);
if (newInitialSands <= currentSands) {
game.settings.set(`ripcrypt`, `sandsOfFate`, newInitialSands);
};
},
});
game.settings.register(`ripcrypt`, `onCrypticEvent`, {
name: `RipCrypt.setting.onCrypticEvent.name`,
hint: `RipCrypt.setting.onCrypticEvent.hint`,
scope: `world`,
config: true,
requiresReload: false,
type: new StringField({
required: true,
initial: `notif`,
choices: {
"notif": `RipCrypt.setting.onCrypticEvent.options.notif`,
"pause": `RipCrypt.setting.onCrypticEvent.options.pause`,
"both": `RipCrypt.setting.onCrypticEvent.options.both`,
"nothing": `RipCrypt.setting.onCrypticEvent.options.nothing`,
},
}),
});
}; };

View file

@ -31,3 +31,21 @@ export function distanceBetweenFates(start, end) {
}; };
return 3; return 3;
}; };
const fateOrder = [
FatePath.WEST, // to make the .find not integer overflow
FatePath.NORTH,
FatePath.EAST,
FatePath.SOUTH,
FatePath.WEST,
];
export function nextFate(fate) {
const fateIndex = fateOrder.findIndex(f => f === fate);
return fateOrder[fateIndex + 1];
};
export function previousFate(fate) {
const fateIndex = fateOrder.lastIndexOf(fate);
return fateOrder[fateIndex - 1];
};

View file

@ -1,12 +1,13 @@
<div id="the-hourglass"> <div id="the-hourglass">
<div <div
class="icon-container" class="icon-container"
data-tooltip="Current Delve Tour: 8" data-tooltip="Current Delve Tour"
> >
<span> <span class="sands-value">
8 {{sandsOfFate}}
</span> </span>
<rc-svg <rc-svg
aria-hidden="true"
class="hourglass" class="hourglass"
name="icons/hourglass" name="icons/hourglass"
var:fill="var(--accent-2)" var:fill="var(--accent-2)"

View file

@ -7,7 +7,9 @@
type="button" type="button"
class="icon" class="icon"
data-action="tourDelta" data-action="tourDelta"
data-delta="-1"
data-tooltip="Next Delve Tour" data-tooltip="Next Delve Tour"
data-tooltip-direction="RIGHT"
> >
<rc-icon <rc-icon
name="icons/arrow-right" name="icons/arrow-right"

View file

@ -7,7 +7,9 @@
type="button" type="button"
class="icon" class="icon"
data-action="tourDelta" data-action="tourDelta"
data-delta="1"
data-tooltip="Previous Delve Tour" data-tooltip="Previous Delve Tour"
data-tooltip-direction="LEFT"
> >
<rc-icon <rc-icon
name="icons/arrow-left" name="icons/arrow-left"