fix(complement-race): add pressure decay system and improve logging

**1. Smart Logging (event-based instead of frame-based)**
- Only logs on answer submission, not every frame
- Format: "🚂 Answer #X: momentum=Y pos=Z pressure=P streak=S"
- Prevents console overflow in real-time game

**2. Pressure Decay System**
- Added `pressure` field to PlayerState type
- Pressure now independent from momentum (was stuck at 100)
- Correct answer: +20 pressure (add steam)
- Wrong answer: +5 pressure (less steam)
- Decay: -8 pressure per answer (steam escapes over time)
- Range: 0-100 with min/max caps

**3. Implementation**
- types.ts: Added pressure field to PlayerState
- Validator.ts: Initialize pressure=60, update with decay
- Provider.tsx: Use actual pressure from server (not calculated)
- Route reset: Reset pressure to 60 on new routes

This fixes the pressure gauge being pinned at 100 constantly.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Thomas Hallock
2025-10-17 07:22:24 -05:00
parent 52019a24c2
commit 66992e8770
3 changed files with 40 additions and 6 deletions

View File

@@ -240,6 +240,9 @@ export function ComplementRaceProvider({ children }: { children: ReactNode }) {
})
}, [activePlayers, players])
// Debug logging ref (track last logged values)
const lastLogRef = useState({ key: '', count: 0 })[0]
// Transform multiplayer state to look like single-player state
const compatibleState = useMemo((): CompatibleGameState => {
const localPlayer = localPlayerId ? multiplayerState.players[localPlayerId] : null
@@ -306,7 +309,7 @@ export function ComplementRaceProvider({ children }: { children: ReactNode }) {
// Sprint mode specific
momentum: localPlayer?.momentum || 0,
trainPosition: localPlayer?.position || 0,
pressure: localPlayer?.momentum ? Math.min(100, localPlayer.momentum + 10) : 0,
pressure: localPlayer?.pressure || 0, // Use actual pressure from server (has decay)
elapsedTime: multiplayerState.gameStartTime ? Date.now() - multiplayerState.gameStartTime : 0,
lastCorrectAnswerTime: localPlayer?.lastAnswerTime || Date.now(),
currentRoute: multiplayerState.currentRoute,
@@ -330,9 +333,28 @@ export function ComplementRaceProvider({ children }: { children: ReactNode }) {
}
}, [multiplayerState, localPlayerId, localUIState])
console.log(
`🚂 Sprint: momentum=${compatibleState.momentum} pos=${compatibleState.trainPosition} pressure=${compatibleState.pressure}`
)
// Debug logging: only log on answer submission or significant events
useEffect(() => {
if (compatibleState.style === 'sprint' && compatibleState.isGameActive) {
const key = `${compatibleState.correctAnswers}`
// Only log on new answers (not every frame)
if (lastLogRef.key !== key) {
console.log(
`🚂 Answer #${compatibleState.correctAnswers}: momentum=${compatibleState.momentum} pos=${Math.floor(compatibleState.trainPosition)} pressure=${compatibleState.pressure} streak=${compatibleState.streak}`
)
lastLogRef.key = key
}
}
}, [
compatibleState.correctAnswers,
compatibleState.momentum,
compatibleState.trainPosition,
compatibleState.pressure,
compatibleState.streak,
compatibleState.style,
compatibleState.isGameActive,
])
// Action creators
const startGame = useCallback(() => {

View File

@@ -170,6 +170,7 @@ export class ComplementRaceValidator
totalQuestions: 0,
position: 0,
momentum: 50, // Start with some momentum in sprint mode
pressure: 60, // Start with initial pressure
isReady: false,
isActive: true,
currentAnswer: null,
@@ -317,13 +318,21 @@ export class ComplementRaceValidator
updatedPlayer.position = Math.min(100, player.position + 100 / state.config.raceGoal)
}
} else if (state.config.style === 'sprint') {
// Sprint: Update momentum AND position
// Sprint: Update momentum, pressure, AND position
if (correct) {
updatedPlayer.momentum = Math.min(100, player.momentum + 15)
// Add pressure on correct answer (add steam to boiler)
updatedPlayer.pressure = Math.min(100, player.pressure + 20)
} else {
updatedPlayer.momentum = Math.max(0, player.momentum - 10)
// Less pressure added on wrong answer
updatedPlayer.pressure = Math.min(100, player.pressure + 5)
}
// Pressure decay: Every answer causes some steam to escape
// Decay rate: 8 points per answer (pressure naturally decreases over time)
updatedPlayer.pressure = Math.max(0, updatedPlayer.pressure - 8)
// Move train based on momentum (momentum/20 = position change per answer)
// Higher momentum = faster movement
const moveDistance = updatedPlayer.momentum / 20
@@ -527,12 +536,14 @@ export class ComplementRaceValidator
return { valid: false, error: 'Routes only available in sprint mode' }
}
// Reset all player positions to 0
// Reset all player positions to 0 for new route
const resetPlayers: Record<string, PlayerState> = {}
for (const [playerId, player] of Object.entries(state.players)) {
resetPlayers[playerId] = {
...player,
position: 0,
momentum: 50, // Reset momentum to starting value
pressure: 60, // Reset pressure to starting value
passengers: [], // Clear any remaining passengers
}
}

View File

@@ -63,6 +63,7 @@ export interface PlayerState {
// Position & Progress
position: number // 0-100% for practice/sprint, lap count for survival
momentum: number // 0-100 (sprint mode only)
pressure: number // 0-100 (sprint mode only, decays over time)
// Current state
isReady: boolean