diff --git a/src/gameState.js b/src/gameState.js new file mode 100644 index 0000000..b9687f3 --- /dev/null +++ b/src/gameState.js @@ -0,0 +1,101 @@ +import { + ROUTES, + JOURNAL, + INITIAL_STATE, + OTIUM_BASE_AUT, +} from './constants.js' + +// Returns a fresh game state with a generated token +export function createState(token) { + return { + ...INITIAL_STATE, + token, + created_at: new Date().toISOString(), + } +} + +// Returns current chapter based on den and aut thresholds +export function getChapter(den, aut) { + if (aut >= 30 && den >= 800) return 5 + if (aut >= 15 && den >= 350) return 4 + if (aut >= 5 && den >= 120) return 3 + if (den >= 40) return 2 + return 1 +} + +// Returns routes available to this state (unlocked, not locked) +export function getUnlockedRoutes(state) { + return ROUTES.filter( + (r) => state.den >= r.unlock_den && state.aut >= r.unlock_aut + ) +} + +// Returns true if a route is unlocked +export function isRouteUnlocked(state, routeId) { + const r = ROUTES.find((x) => x.id === routeId) + if (!r) return false + return state.den >= r.unlock_den && state.aut >= r.unlock_aut +} + +// Apply a completed dispatch — returns new state +export function applyDispatch(state, routeId) { + const r = ROUTES.find((x) => x.id === routeId) + if (!r) return state + + const route_dispatches = { + ...state.route_dispatches, + [routeId]: (state.route_dispatches[routeId] || 0) + 1, + } + + const den = state.den + r.profit + const dispatches = state.dispatches + 1 + const chapter = getChapter(den, state.aut) + + // Check for new journal entry + const dispatchCount = route_dispatches[routeId] + const entries = JOURNAL[routeId] || [] + const entry = entries.find((e) => e.dispatch === dispatchCount) + const journal_seen = entry + ? [...state.journal_seen, { routeId, dispatch: dispatchCount }] + : state.journal_seen + + return { + ...state, + den, + dispatches, + chapter, + route_dispatches, + journal_seen, + } +} + +// 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 } +} + +// Apply completed otium — returns new state +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 } +} + +// Returns the journal entry to show for a completed dispatch, or null +export function getNewJournalEntry(state, routeId) { + const dispatchCount = (state.route_dispatches[routeId] || 0) + const entries = JOURNAL[routeId] || [] + return entries.find((e) => e.dispatch === dispatchCount) || null +} + +// Returns all journal entries seen so far, in order seen +export function getSeenJournalEntries(state) { + return state.journal_seen.map(({ routeId, dispatch }) => { + const entries = JOURNAL[routeId] || [] + return entries.find((e) => e.dispatch === dispatch) + }).filter(Boolean) +}