From 6655abeeeca1ba850587a428603a506c9b4b88a8 Mon Sep 17 00:00:00 2001 From: Michael Mainguy Date: Mon, 1 Dec 2025 19:24:57 -0600 Subject: [PATCH] Add non-linear controller input curve and fix auth token error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace linear controller scaling with power curve (exp 2.5) for smoother VR controls - less sensitive at low deflections, full power at 90% stick travel - Fix Missing Refresh Token error by clearing stale auth state automatically instead of leaving users in broken state - Fix build errors: use ScoreEvent type in weaponSystem, remove unused parTime parameter from buildResult 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/services/authService.ts | 14 ++++++++++++++ src/services/gameResultsService.ts | 3 +-- src/ship/ship.ts | 16 +++++++++++----- src/ship/weaponSystem.ts | 3 ++- src/ui/hud/statusScreen.ts | 3 +-- 5 files changed, 29 insertions(+), 10 deletions(-) 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);