All game logic in The Beach Mystery lives in a single custom React hook:Documentation Index
Fetch the complete documentation index at: https://mintlify.com/apursley2012/the-beach-mystery/llms.txt
Use this file to discover all available pages before exploring further.
useGameState, defined in hooks/useGameState.js. Every piece of mutable data the application needs — which story node the player is on, which items they have collected, which endings they have unlocked, their accessibility settings, and any saved progress — is owned and managed here. Components never maintain their own game state; they destructure what they need from the hook and call the provided functions to drive transitions. This centralised design means the entire session can be serialised to a single JSON string, saved to localStorage, and restored exactly as it was left.
What the Hook Tracks
The hook manages three independent state slices, each persisted separately:gameState
achievements
resetGame().settings
sound, textSize, and highContrast. Persisted independently and applied globally via CSS data attributes.Initial State
When the hook is first mounted (beforestartGame is called), gameState is set to this default object:
state sub-object tracks the player’s inventory flags. These are mutated declaratively: narration and choice destination nodes carry an optional setState object that is merged in when the node is entered.
Settings Object
localStorage key beach-mystery-settings on hook initialisation and automatically persisted whenever updateSettings is called. They are never reset by resetGame().
localStorage Keys
| Key | Contents | Reset by resetGame? |
|---|---|---|
beach-mystery-save | Serialised gameState object | No — call clearSave() to delete the save file |
beach-mystery-achievements | JSON array of ending ID strings | No — persists across all sessions |
beach-mystery-settings | JSON settings object | No — persists across all sessions |
Hook API
Import the hook into any component that needs to interact with the game:Functions
startGame(variables) — begin a new session
startGame(variables) — begin a new session
- Captures the current time as
HH:MMand injects it intovariablesasformatted_time. - Sets
currentNodeto"beachOpening". - Resets
history,endingReached, and all inventory flags to their initial values. - Sets
startedtotrue.
makeChoice(option) — apply a choice node decision
makeChoice(option) — apply a choice node decision
choice-type node.- Verifies that
currentNodeis achoice-type node (no-ops otherwise). - Appends
{ nodeId: currentNode, choice: option.label }tohistory. - Resolves
STORY_NODES[option.next]and merges itssetStateflags intogameState.stateif present. - Sets
currentNodetooption.next.
node.options.advanceNode() — step past a narration node
advanceNode() — step past a narration node
narration-type node.- Reads
STORY_NODES[currentNode].next. - Merges the destination node’s
setStateflags intogameState.stateif present. - Sets
currentNodetonode.next.
node.next is undefined (an ending node), the function is a no-op — the session is already complete.solveRiddle() — advance past a riddle node
solveRiddle() — advance past a riddle node
riddle-type node.- Verifies that
currentNodeis ariddle-type node (no-ops otherwise). - Appends
{ nodeId: currentNode, choice: 'echo' }tohistory. - Sets
currentNodetonode.next.
node.answer (case-insensitive comparison). Incorrect answers should not call this function — the UI is responsible for showing the hint after two failures.'echo' as the choice value regardless of how the player typed their answer, since echo is the only correct answer in the current riddle set.reachEnding(endingId) — record a completed storyline
reachEnding(endingId) — record a completed storyline
ending field.- Sets
gameState.endingReachedtoendingId. - Adds
endingIdto theachievementsarray if it is not already present. - The
achievementsarray is automatically persisted tobeach-mystery-achievementsvia auseEffect.
node.ending is defined. Pass node.ending as the argument. The renderer should call this once when the ending node is first entered.Valid ending IDs: islandTreasure, hiddenChamber, animalDen, peacefulWalk.resetGame() — reset the current session
resetGame() — reset the current session
gameState to its initial value, effectively starting a new session from the beginning.- Restores
gameStateto theinitialStateobject (all flags false,currentNodenull,startedfalse). - Does not clear
achievements— discovered endings are preserved. - Does not remove the save from localStorage — call
clearSave()separately if you want to discard the save file.
saveGame() — persist the current session
saveGame() — persist the current session
gameState to localStorage under the key beach-mystery-save.loadGame() → boolean — restore a saved session
loadGame() → boolean — restore a saved session
localStorage.true if a valid in-progress save was found and restored; false otherwise.What it does:- Reads and JSON-parses
beach-mystery-save. - If the parsed object has
started: true, replacesgameStatewith the saved object and returnstrue. - Returns
false(without modifying state) if no save exists, the JSON is malformed, orstartedis false.
hasSave() → boolean — check for a valid save
hasSave() → boolean — check for a valid save
true if beach-mystery-save in localStorage contains a valid object where started is true and currentNode is not null.When to call: To conditionally render a “Continue” button on the home page.clearSave() — delete the save file
clearSave() — delete the save file
beach-mystery-save key from localStorage.resetGame() when you want a fully clean slate.updateSettings(partial) — change a setting
updateSettings(partial) — change a setting
- Shallow-merges
partialinto the existingsettingsobject. - The updated settings are automatically persisted to
beach-mystery-settingsvia auseEffect.