From fc93efd245867e9fe0b02953bc732fd9350d8aaa Mon Sep 17 00:00:00 2001
From: Hikari
Date: Mon, 9 Mar 2026 22:02:52 -0700
Subject: [PATCH 1/5] feat: persist adventure multiplier selection in
localStorage
Reads the saved batch-size preference on mount and writes it back to
localStorage on every selection change, so the chosen multiplier
survives page refreshes. Falls back to 1 when no stored value is found
or the value is unrecognisable.
Closes #35
---
.../src/components/game/adventurerPanel.tsx | 30 ++++++++++++++++++-
1 file changed, 29 insertions(+), 1 deletion(-)
diff --git a/apps/web/src/components/game/adventurerPanel.tsx b/apps/web/src/components/game/adventurerPanel.tsx
index efa7790..c996374 100644
--- a/apps/web/src/components/game/adventurerPanel.tsx
+++ b/apps/web/src/components/game/adventurerPanel.tsx
@@ -16,6 +16,31 @@ import type { Adventurer } from "@elysium/types";
type BatchSize = 1 | 5 | 10 | 25 | 100 | "max";
const batchOptions: Array = [ 1, 5, 10, 25, 100, "max" ];
+/**
+ * Parses a localStorage string back into a valid BatchSize, defaulting to 1.
+ * @param stored - The raw string from localStorage (or null if absent).
+ * @returns A valid BatchSize value.
+ */
+const parseBatchSize = (stored: string | null): BatchSize => {
+ if (stored === "max") {
+ return "max";
+ }
+ const numeric = Number(stored);
+ if (numeric === 5) {
+ return 5;
+ }
+ if (numeric === 10) {
+ return 10;
+ }
+ if (numeric === 25) {
+ return 25;
+ }
+ if (numeric === 100) {
+ return 100;
+ }
+ return 1;
+};
+
/**
* Computes the total cost to buy a batch of adventurers.
* @param adventurer - The adventurer to buy.
@@ -148,7 +173,9 @@ const AdventurerCard = ({
const AdventurerPanel = (): JSX.Element => {
const { state, formatNumber } = useGame();
const [ showLocked, setShowLocked ] = useState(true);
- const [ batchSize, setBatchSize ] = useState(1);
+ const [ batchSize, setBatchSize ] = useState(() => {
+ return parseBatchSize(localStorage.getItem("elysium_batch_size"));
+ });
if (state === null) {
return (
@@ -196,6 +223,7 @@ const AdventurerPanel = (): JSX.Element => {
{batchOptions.map((option) => {
function handleBatchSelect(): void {
setBatchSize(option);
+ localStorage.setItem("elysium_batch_size", String(option));
}
return (
}
- {quest.rewards.map((reward) => {
+ {quest.rewards.map((reward, rewardIndex) => {
return (
-
+
{reward.type === "gold"
&& `🪙 ${formatNumber(reward.amount ?? 0)}`}
{reward.type === "essence"
--
2.52.0
From 2c16736bdf3dad6241ac02cfabd8c50b7967f047 Mon Sep 17 00:00:00 2001
From: Hikari
Date: Mon, 9 Mar 2026 22:12:42 -0700
Subject: [PATCH 4/5] fix: use item name as key for equipped items list
item.type only has three possible values (weapon/armour/trinket).
Using it as a React key is safe in practice because the equipment system
enforces one item per slot, but item.name is a stable, semantically
correct unique identifier that does not rely on that invariant.
---
apps/web/src/components/game/characterPage.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/web/src/components/game/characterPage.tsx b/apps/web/src/components/game/characterPage.tsx
index b498534..25fca25 100644
--- a/apps/web/src/components/game/characterPage.tsx
+++ b/apps/web/src/components/game/characterPage.tsx
@@ -243,7 +243,7 @@ const CharacterPage = ({ discordId }: CharacterPageProperties): JSX.Element => {
return (
--
2.52.0
From c3e85c37687943252d48a6f021a397338b794870 Mon Sep 17 00:00:00 2001
From: Hikari
Date: Mon, 9 Mar 2026 22:15:38 -0700
Subject: [PATCH 5/5] feat: persist exploration zone selection in
sessionStorage
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Applies the same sticky-zone pattern as the boss and quest panels.
The handleZoneSelect wrapper already existed — it just needed to write
to sessionStorage alongside updating state, and the useState initialiser
needed to read from sessionStorage on mount.
---
apps/web/src/components/game/explorationPanel.tsx | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/apps/web/src/components/game/explorationPanel.tsx b/apps/web/src/components/game/explorationPanel.tsx
index 03ce4c0..b32fac4 100644
--- a/apps/web/src/components/game/explorationPanel.tsx
+++ b/apps/web/src/components/game/explorationPanel.tsx
@@ -67,7 +67,9 @@ interface CollectResult {
const ExplorationPanel = (): JSX.Element => {
const { state, startExploration, collectExploration, formatNumber }
= useGame();
- const [ activeZoneId, setActiveZoneId ] = useState("verdant_vale");
+ const [ activeZoneId, setActiveZoneId ] = useState(() => {
+ return sessionStorage.getItem("elysium_explore_zone") ?? "verdant_vale";
+ });
const [ pendingAreaId, setPendingAreaId ] = useState(null);
const [ lastResult, setLastResult ] = useState(null);
@@ -116,6 +118,7 @@ const ExplorationPanel = (): JSX.Element => {
function handleZoneSelect(id: string): void {
setActiveZoneId(id);
setLastResult(null);
+ sessionStorage.setItem("elysium_explore_zone", id);
}
const goldChange = lastResult?.response.event?.goldChange ?? 0;
--
2.52.0