diff --git a/src/services/authService.ts b/src/services/authService.ts index e1cd6a9..137f5f4 100644 --- a/src/services/authService.ts +++ b/src/services/authService.ts @@ -156,6 +156,20 @@ export class AuthService { return await this._client.getTokenSilently(); } catch (error) { log.error('Error getting access token:', error); + + // If refresh token is missing/invalid, clear stale auth state + // User will appear logged out and can log in fresh + if (error instanceof Error && error.message.includes('Missing Refresh Token')) { + log.warn('[AuthService] Clearing stale auth session'); + this._user = null; + // Clear Auth0 local storage cache without redirecting + try { + await this._client.logout({ openUrl: false }); + } catch { + // Ignore logout errors - just clear local state + } + } + return undefined; } } diff --git a/src/services/gameResultsService.ts b/src/services/gameResultsService.ts index 36a304f..b8ad844 100644 --- a/src/services/gameResultsService.ts +++ b/src/services/gameResultsService.ts @@ -144,8 +144,7 @@ export class GameResultsService { levelName: string, gameStats: GameStats, totalAsteroids: number, - endReason: 'victory' | 'death' | 'stranded', - parTime: number + endReason: 'victory' | 'death' | 'stranded' ): GameResult { // Get player name from auth service const authService = AuthService.getInstance(); diff --git a/src/ship/ship.ts b/src/ship/ship.ts index 7842214..698fc0c 100644 --- a/src/ship/ship.ts +++ b/src/ship/ship.ts @@ -609,14 +609,20 @@ export class Ship { rightStick: Vector2.Zero(), }; - // Merge inputs with smooth deadzone scaling (controller takes priority if active, keyboard disabled in VR) - // Deadzone: 0.1-0.15 range with linear scaling (avoids abrupt cliff effect) + // Merge inputs with non-linear curve (controller takes priority if active, keyboard disabled in VR) + // Deadzone: 0.1, Max input: 0.9, Power curve for slow start, fast finish + const DEADZONE = 0.1; + const MAX_INPUT = 0.9; + const CURVE_EXPONENT = 2.5; + const leftMagnitude = controllerState.leftStick.length(); const rightMagnitude = controllerState.rightStick.length(); - // Scale factor: 0% at 0.1, 100% at 0.15, linear interpolation between - const leftScale = Math.max(0, Math.min(1, (leftMagnitude - 0.1) / 0.05)); - const rightScale = Math.max(0, Math.min(1, (rightMagnitude - 0.1) / 0.05)); + // Calculate curved scale: slow at low deflection, ramps up toward max at 0.9 + const leftScale = leftMagnitude <= DEADZONE ? 0 : + Math.pow(Math.min(1, (leftMagnitude - DEADZONE) / (MAX_INPUT - DEADZONE)), CURVE_EXPONENT); + const rightScale = rightMagnitude <= DEADZONE ? 0 : + Math.pow(Math.min(1, (rightMagnitude - DEADZONE) / (MAX_INPUT - DEADZONE)), CURVE_EXPONENT); const combinedInput = { leftStick: diff --git a/src/ship/weaponSystem.ts b/src/ship/weaponSystem.ts index 8911415..a1c50bd 100644 --- a/src/ship/weaponSystem.ts +++ b/src/ship/weaponSystem.ts @@ -20,6 +20,7 @@ import { GameStats } from "../game/gameStats"; import { Projectile } from "./projectile"; import { RockFactory } from "../environment/asteroids/rockFactory"; import log from "../core/logger"; +import { ScoreEvent } from "../ui/hud/scoreboard"; /** * Handles weapon firing and projectile lifecycle using shape casting @@ -39,7 +40,7 @@ export class WeaponSystem { private _havokPlugin: HavokPlugin | null = null; // Observable for score updates when asteroids are destroyed - private _scoreObservable: Observable<{score: number, remaining: number, message: string}> | null = null; + private _scoreObservable: Observable | null = null; // Ship body to ignore in shape casts private _shipBody: PhysicsBody | null = null; diff --git a/src/ui/hud/statusScreen.ts b/src/ui/hud/statusScreen.ts index 7f127c7..eb31422 100644 --- a/src/ui/hud/statusScreen.ts +++ b/src/ui/hud/statusScreen.ts @@ -671,8 +671,7 @@ export class StatusScreen { this._currentLevelName, this._gameStats, this._totalAsteroids, - endReason, - this._parTime + endReason ); log.info('[StatusScreen] Built result:', result);