iv: fix route selection and otium den deduction in Forum.jsx
This commit is contained in:
@@ -2,6 +2,10 @@
|
|||||||
// FORUM context screen. Replaces Ledger.jsx.
|
// FORUM context screen. Replaces Ledger.jsx.
|
||||||
// Renders two divs directly into Shell's two-col layout grid.
|
// Renders two divs directly into Shell's two-col layout grid.
|
||||||
// All game logic migrated from Ledger.jsx.
|
// All game logic migrated from Ledger.jsx.
|
||||||
|
//
|
||||||
|
// Fixes from initial version:
|
||||||
|
// - isRouteUnlocked called with route.id (string), not route (object)
|
||||||
|
// - applyOtium result has 8 dn deducted to match server-side debit
|
||||||
|
|
||||||
import { useState, useEffect, useRef } from 'react'
|
import { useState, useEffect, useRef } from 'react'
|
||||||
import Section from '../components/Section.jsx'
|
import Section from '../components/Section.jsx'
|
||||||
@@ -27,7 +31,6 @@ export default function Forum({ state, onStateChange, onNewGame }) {
|
|||||||
const [otium, setOtium] = useState(null)
|
const [otium, setOtium] = useState(null)
|
||||||
const [message, setMessage] = useState('')
|
const [message, setMessage] = useState('')
|
||||||
const [journal, setJournal] = useState(getSeenJournalEntries(state))
|
const [journal, setJournal] = useState(getSeenJournalEntries(state))
|
||||||
const [newEntryKey, setNewEntryKey] = useState(null)
|
|
||||||
const tickRef = useRef(null)
|
const tickRef = useRef(null)
|
||||||
const msgRef = useRef(null)
|
const msgRef = useRef(null)
|
||||||
|
|
||||||
@@ -48,11 +51,7 @@ export default function Forum({ state, onStateChange, onNewGame }) {
|
|||||||
const newState = applyDispatch(state, dispatch.routeId)
|
const newState = applyDispatch(state, dispatch.routeId)
|
||||||
const route = ROUTES.find(r => r.id === dispatch.routeId)
|
const route = ROUTES.find(r => r.id === dispatch.routeId)
|
||||||
showMessage(`Galley returned. +${route.profit} denarii.`)
|
showMessage(`Galley returned. +${route.profit} denarii.`)
|
||||||
if (entry) {
|
if (entry) setJournal(j => [entry, ...j])
|
||||||
setJournal(j => [entry, ...j])
|
|
||||||
setNewEntryKey(`${dispatch.routeId}-${newState.route_dispatches[dispatch.routeId]}`)
|
|
||||||
setTimeout(() => setNewEntryKey(null), 5000)
|
|
||||||
}
|
|
||||||
setSelectedRoute(null)
|
setSelectedRoute(null)
|
||||||
onStateChange(newState)
|
onStateChange(newState)
|
||||||
}
|
}
|
||||||
@@ -61,8 +60,10 @@ export default function Forum({ state, onStateChange, onNewGame }) {
|
|||||||
const elapsed = now - otium.startMs
|
const elapsed = now - otium.startMs
|
||||||
if (elapsed >= OTIUM_DURATION_MS) {
|
if (elapsed >= OTIUM_DURATION_MS) {
|
||||||
setOtium(null)
|
setOtium(null)
|
||||||
const newState = applyOtium(state)
|
// Apply otium aut gain, then deduct 8 dn to match server-side debit
|
||||||
showMessage('Otium complete. Auctoritas recorded.')
|
const withAut = applyOtium(state)
|
||||||
|
const newState = { ...withAut, den: Math.max(0, withAut.den - OTIUM_CYCLE_TOTAL_DN) }
|
||||||
|
showMessage(`Otium complete. −${OTIUM_CYCLE_TOTAL_DN} dn. Auctoritas recorded.`)
|
||||||
onStateChange(newState)
|
onStateChange(newState)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,6 +75,7 @@ export default function Forum({ state, onStateChange, onNewGame }) {
|
|||||||
|
|
||||||
function handleSelectRoute(routeId) {
|
function handleSelectRoute(routeId) {
|
||||||
if (busy) return
|
if (busy) return
|
||||||
|
// Fix: pass route.id string to isRouteUnlocked, not the route object
|
||||||
if (!isRouteUnlocked(state, routeId)) return
|
if (!isRouteUnlocked(state, routeId)) return
|
||||||
setSelectedRoute(prev => prev === routeId ? null : routeId)
|
setSelectedRoute(prev => prev === routeId ? null : routeId)
|
||||||
}
|
}
|
||||||
@@ -103,19 +105,14 @@ export default function Forum({ state, onStateChange, onNewGame }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Progress
|
// Progress
|
||||||
let progressPct = 0, progressLabel = '', progressSub = ''
|
let progressPct = 0
|
||||||
if (dispatch) {
|
if (dispatch) {
|
||||||
progressPct = Math.min(((Date.now()-dispatch.startMs)/dispatch.durationMs)*100, 100)
|
progressPct = Math.min(((Date.now()-dispatch.startMs)/dispatch.durationMs)*100, 100)
|
||||||
const route = ROUTES.find(r=>r.id===dispatch.routeId)
|
|
||||||
progressLabel = `${WAYPOINTS[route.from].name} → ${WAYPOINTS[route.to].name}`
|
|
||||||
progressSub = `${Math.round(progressPct)}%`
|
|
||||||
} else if (otium) {
|
} else if (otium) {
|
||||||
progressPct = Math.min(((Date.now()-otium.startMs)/OTIUM_DURATION_MS)*100, 100)
|
progressPct = Math.min(((Date.now()-otium.startMs)/OTIUM_DURATION_MS)*100, 100)
|
||||||
progressLabel = 'resting...'
|
|
||||||
progressSub = `${Math.round(progressPct)}%`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section data
|
// Section data — active venture
|
||||||
const activeVentureItems = dispatch ? (() => {
|
const activeVentureItems = dispatch ? (() => {
|
||||||
const route = ROUTES.find(r => r.id === dispatch.routeId)
|
const route = ROUTES.find(r => r.id === dispatch.routeId)
|
||||||
return [
|
return [
|
||||||
@@ -130,8 +127,9 @@ export default function Forum({ state, onStateChange, onNewGame }) {
|
|||||||
{ key: 'Cost', value: `−${OTIUM_CYCLE_TOTAL_DN} dn`, cls: 'bad' },
|
{ key: 'Cost', value: `−${OTIUM_CYCLE_TOTAL_DN} dn`, cls: 'bad' },
|
||||||
] : [{ key: 'Status', value: 'No galley at sea', cls: '' }]
|
] : [{ key: 'Status', value: 'No galley at sea', cls: '' }]
|
||||||
|
|
||||||
|
// Route list — fix: pass route.id to isRouteUnlocked
|
||||||
const routeItems = ROUTES.map(route => {
|
const routeItems = ROUTES.map(route => {
|
||||||
const unlocked = isRouteUnlocked(state, route)
|
const unlocked = isRouteUnlocked(state, route.id)
|
||||||
const vectura = Math.round(route.cost * 0.60 * 10) / 10
|
const vectura = Math.round(route.cost * 0.60 * 10) / 10
|
||||||
const portoria = Math.round(route.cost * 0.25 * 10) / 10
|
const portoria = Math.round(route.cost * 0.25 * 10) / 10
|
||||||
const other = Math.round((route.cost - vectura - portoria) * 10) / 10
|
const other = Math.round((route.cost - vectura - portoria) * 10) / 10
|
||||||
@@ -145,7 +143,11 @@ export default function Forum({ state, onStateChange, onNewGame }) {
|
|||||||
profit: route.profit,
|
profit: route.profit,
|
||||||
vectura, portoria, other,
|
vectura, portoria, other,
|
||||||
locked: !unlocked,
|
locked: !unlocked,
|
||||||
lock_reason: !unlocked ? (state.den<route.unlock_den ? `${route.unlock_den.toLocaleString()} dn` : `Auctoritas ${route.unlock_aut}`) : null,
|
lock_reason: !unlocked
|
||||||
|
? (state.den < route.unlock_den
|
||||||
|
? `${route.unlock_den.toLocaleString()} dn`
|
||||||
|
: `Auctoritas ${route.unlock_aut}`)
|
||||||
|
: null,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -181,7 +183,6 @@ export default function Forum({ state, onStateChange, onNewGame }) {
|
|||||||
background: 'var(--otivm-ink)', color: 'var(--otivm-parch)',
|
background: 'var(--otivm-ink)', color: 'var(--otivm-parch)',
|
||||||
padding: '10px 18px', borderRadius: '3px',
|
padding: '10px 18px', borderRadius: '3px',
|
||||||
fontSize: '0.8rem', fontStyle: 'italic', zIndex: 300,
|
fontSize: '0.8rem', fontStyle: 'italic', zIndex: 300,
|
||||||
gridColumn:'1/-1',
|
|
||||||
}}>
|
}}>
|
||||||
{message}
|
{message}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user