Get the delve tour incrementer changes working and affecting fate as well
This commit is contained in:
parent
7639962130
commit
110823a26b
9 changed files with 174 additions and 14 deletions
|
|
@ -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": {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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`, {
|
||||||
|
|
|
||||||
|
|
@ -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`,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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];
|
||||||
|
};
|
||||||
|
|
@ -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)"
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue