Add animation and editing support for the fate compass

This commit is contained in:
Oliver-Akins 2025-03-01 19:22:02 -07:00
parent 7c8d6a7208
commit 7639962130
2 changed files with 115 additions and 23 deletions

View file

@ -1,7 +1,17 @@
import { distanceBetweenFates } from "../utils/distanceBetweenFates.mjs";
import { filePath } from "../consts.mjs"; import { filePath } from "../consts.mjs";
import { gameTerms } from "../gameTerms.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;
const { FatePath } = gameTerms;
const CompassRotations = {
[FatePath.NORTH]: -90,
[FatePath.EAST]: 0,
[FatePath.SOUTH]: 90,
[FatePath.WEST]: 180,
};
const conditions = [ const conditions = [
{ label: `RipCrypt.common.difficulties.easy`, value: 4 }, { label: `RipCrypt.common.difficulties.easy`, value: 4 },
@ -17,7 +27,7 @@ export class DelveDiceHUD extends HandlebarsApplicationMixin(ApplicationV2) {
tag: `aside`, tag: `aside`,
classes: [ classes: [
`ripcrypt`, `ripcrypt`,
`ripcrypt--DelveDiceHUD` `ripcrypt--DelveDiceHUD`,
], ],
window: { window: {
frame: false, frame: false,
@ -25,6 +35,7 @@ export class DelveDiceHUD extends HandlebarsApplicationMixin(ApplicationV2) {
}, },
actions: { actions: {
tourDelta: this.#tourDelta, tourDelta: this.#tourDelta,
setFate: this.#setFate,
}, },
}; };
@ -38,7 +49,7 @@ export class DelveDiceHUD extends HandlebarsApplicationMixin(ApplicationV2) {
fateCompass: { fateCompass: {
template: filePath(`templates/Apps/DelveDiceHUD/fateCompass.hbs`), template: filePath(`templates/Apps/DelveDiceHUD/fateCompass.hbs`),
}, },
currentTour: { sandsOfFate: {
template: filePath(`templates/Apps/DelveDiceHUD/tour/current.hbs`), template: filePath(`templates/Apps/DelveDiceHUD/tour/current.hbs`),
}, },
nextTour: { nextTour: {
@ -47,6 +58,24 @@ export class DelveDiceHUD extends HandlebarsApplicationMixin(ApplicationV2) {
}; };
// #endregion // #endregion
// #region Instance Data
/**
* The current number of degrees the compass pointer should be rotated, this
* is not stored in the DB since we only care about the initial rotation on
* reload, which is derived from the current fate.
* @type {Number}
*/
_rotation;
constructor(...args) {
super(...args);
this._sandsOfFate = game.settings.get(`ripcrypt`, `sandsOfFate`);
this._currentFate = game.settings.get(`ripcrypt`, `currentFate`);
this._rotation = CompassRotations[this._currentFate];
this._difficulty = game.settings.get(`ripcrypt`, `dc`);
};
// #endregion
// #region Lifecycle // #region Lifecycle
/** /**
* Injects the element into the Foundry UI in the top middle * Injects the element into the Foundry UI in the top middle
@ -75,16 +104,17 @@ export class DelveDiceHUD extends HandlebarsApplicationMixin(ApplicationV2) {
ctx.meta.editable = game.user.isGM; ctx.meta.editable = game.user.isGM;
switch (partId) { switch (partId) {
case `currentTour`: { case `sandsOfFate`: {
await this._prepareTourContext(ctx); ctx.sandsOfFate = this._sandsOfFate;
break; break;
}; };
case `difficulty`: { case `difficulty`: {
await this._prepareDifficultyContext(ctx); ctx.dc = this._difficulty;
break; break;
}; };
case `fateCompass`: { case `fateCompass`: {
await this._prepareFateCompassContext(ctx); ctx.fate = this._currentFate;
ctx.rotation = `${this._rotation}deg`;
break; break;
}; };
}; };
@ -93,26 +123,40 @@ export class DelveDiceHUD extends HandlebarsApplicationMixin(ApplicationV2) {
return ctx; return ctx;
}; };
async _prepareTourContext(ctx) { async animate({ parts = [] } = {}) {
ctx.tour = game.settings.get(`ripcrypt`, `sandsOfFate`); if (parts.includes(`fateCompass`)) {
this.#animateCompassTo();
};
if (parts.includes(`sandsOfFate`)) {};
}; };
async _prepareDifficultyContext(ctx) { #animateCompassTo(newFate) {
ctx.dc = game.settings.get(`ripcrypt`, `dc`); /** @type {HTMLElement|undefined} */
}; const pointer = this.element.querySelector(`.compass-pointer`);
if (!pointer) { return };
async _prepareFateCompassContext(ctx) { let distance = distanceBetweenFates(this._currentFate, newFate);
ctx.direction = game.settings.get(`ripcrypt`, `currentFate`); Logger.table({ newFate, fate: this._currentFate, distance, _rotation: this._rotation });
} if (distance === 3) { distance = -1 };
this._rotation += distance * 90;
pointer.style.setProperty(`transform`, `rotate(${this._rotation}deg)`);
};
// #endregion // #endregion
// #region Actions // #region Actions
static async #tourDelta() { static async #tourDelta() {};
ui.notifications.info(`Delve Tour Changed`, { console: false });
};
static async #setFate() { /** @this {DelveDiceHUD} */
ui.notifications.info(`Fate Set!`, { console: false }); static async #setFate(_event, element) {
const fate = element.dataset.toFate;
this.#animateCompassTo(fate);
// must be done after animate, otherwise it won't change the rotation
this._currentFate = fate;
game.settings.set(`ripcrypt`, `currentFate`, fate);
}; };
// #endregion // #endregion
}; };

View file

@ -1,16 +1,64 @@
<div id="fate-compass"> <div id="fate-compass">
<div class="compass-container"> <div class="compass-container">
<div class="compass"> <div class="compass">
<button type="button" class="transparent" style="grid-area: N;">N</button>
<button type="button" class="transparent" style="grid-area: W;">W</button>
<rc-icon <rc-icon
class="compass-pointer" class="compass-pointer"
name="icons/arrow-compass" name="icons/arrow-compass"
var:fill="var(--accent-3)" var:fill="var(--accent-3)"
var:size="1rem" var:size="1rem"
style="transform: rotate({{rotation}})"
></rc-icon> ></rc-icon>
<button type="button" class="transparent" style="grid-area: E;">E</button> {{#if meta.editable}}
<button type="button" class="transparent" style="grid-area: S;">S</button> <button
type="button"
class="transparent"
style="grid-area: N;"
data-action="setFate"
data-to-fate="North"
data-tooltip="Set Fate to North"
data-tooltip-direction="LEFT"
>
N
</button>
<button
type="button"
class="transparent"
style="grid-area: W;"
data-action="setFate"
data-to-fate="West"
data-tooltip="Set Fate to West"
data-tooltip-direction="LEFT"
>
W
</button>
<button
type="button"
class="transparent"
style="grid-area: E;"
data-action="setFate"
data-to-fate="East"
data-tooltip="Set Fate to East"
data-tooltip-direction="RIGHT"
>
E
</button>
<button
type="button"
class="transparent"
style="grid-area: S;"
data-action="setFate"
data-to-fate="South"
data-tooltip="Set Fate to South"
data-tooltip-direction="DOWN"
>
S
</button>
{{else}}
<span style="grid-area: N;">N</span>
<span style="grid-area: W;">W</span>
<span style="grid-area: E;">E</span>
<span style="grid-area: S;">S</span>
{{/if}}
</div> </div>
</div> </div>
</div> </div>