From 4360ef6da20baca5568b189ed2d2f21bec2aef17 Mon Sep 17 00:00:00 2001 From: otivm Date: Sat, 25 Apr 2026 20:17:11 +0000 Subject: [PATCH] Add events log, active_dispatch tracking, and galleyProgress to gameState --- src/gameState.js | 66 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 10 deletions(-) diff --git a/src/gameState.js b/src/gameState.js index b9687f3..631bcd0 100644 --- a/src/gameState.js +++ b/src/gameState.js @@ -37,7 +37,52 @@ export function isRouteUnlocked(state, routeId) { return state.den >= r.unlock_den && state.aut >= r.unlock_aut } -// Apply a completed dispatch — returns new state +// Returns a 0–1 progress float for a dispatched galley along its route. +// 0 = just departed, 1 = returned. Returns null if no active dispatch. +// This function is pure — it takes a snapshot of active_dispatch and a +// current timestamp. OTIVM-IV will feed real duration_ms values. +// OTIVM-VII will map the progress float onto H3 waypoints along the route. +export function galleyProgress(active_dispatch, now_ms) { + if (!active_dispatch) return null + const { started_utc, duration_ms } = active_dispatch + const elapsed = now_ms - new Date(started_utc).getTime() + return Math.min(elapsed / duration_ms, 1) +} + +// Append an event to state — returns new state. +// Internal helper used by apply* functions below. +function appendEvent(state, type, route_id = null) { + const event = { + type, + route_id, + timestamp_utc: new Date().toISOString(), + } + return { + ...state, + events: [...(state.events || []), event], + } +} + +// Apply cost of launching a dispatch — returns new state or null if insufficient funds. +// Sets active_dispatch so galleyProgress can track position. +export function applyDispatchCost(state, routeId) { + const r = ROUTES.find((x) => x.id === routeId) + if (!r) return null + if (state.den < r.cost) return null + const withCost = { ...state, den: state.den - r.cost } + const withDispatch = { + ...withCost, + active_dispatch: { + route_id: routeId, + started_utc: new Date().toISOString(), + duration_ms: r.duration_ms, + }, + } + return appendEvent(withDispatch, 'dispatch_start', routeId) +} + +// Apply a completed dispatch — returns new state. +// Clears active_dispatch on completion. export function applyDispatch(state, routeId) { const r = ROUTES.find((x) => x.id === routeId) if (!r) return state @@ -49,6 +94,7 @@ export function applyDispatch(state, routeId) { const den = state.den + r.profit const dispatches = state.dispatches + 1 + const prevChapter = state.chapter const chapter = getChapter(den, state.aut) // Check for new journal entry @@ -59,22 +105,21 @@ export function applyDispatch(state, routeId) { ? [...state.journal_seen, { routeId, dispatch: dispatchCount }] : state.journal_seen - return { + let next = { ...state, den, dispatches, chapter, route_dispatches, journal_seen, + active_dispatch: null, } -} -// Apply cost of launching a dispatch — returns new state or null if insufficient funds -export function applyDispatchCost(state, routeId) { - const r = ROUTES.find((x) => x.id === routeId) - if (!r) return null - if (state.den < r.cost) return null - return { ...state, den: state.den - r.cost } + next = appendEvent(next, 'dispatch_complete', routeId) + if (entry) next = appendEvent(next, 'journal_unlock', routeId) + if (chapter > prevChapter) next = appendEvent(next, 'chapter_advance', null) + + return next } // Apply completed otium — returns new state @@ -82,7 +127,8 @@ export function applyOtium(state) { const gain = OTIUM_BASE_AUT + Math.floor(state.dispatches / 3) const aut = state.aut + gain const chapter = getChapter(state.den, aut) - return { ...state, aut, chapter } + const next = { ...state, aut, chapter } + return appendEvent(next, 'otium', null) } // Returns the journal entry to show for a completed dispatch, or null