Replace debugLog and console.* with loglevel logger

- Create centralized logger module (src/core/logger.ts)
- Replace all debugLog() calls with log.debug()
- Replace console.log() with log.info()
- Replace console.warn() with log.warn()
- Replace console.error() with log.error()
- Delete deprecated src/core/debug.ts
- Configure log levels: debug for dev, warn for production
- Add localStorage override for production debugging

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Michael Mainguy 2025-11-29 05:24:18 -06:00
parent e8ac3a8f0a
commit 123b341ed7
44 changed files with 575 additions and 572 deletions

View File

@ -1,5 +1,6 @@
import { AnalyticsAdapter, AnalyticsEvent } from './analyticsAdapter'; import { AnalyticsAdapter, AnalyticsEvent } from './analyticsAdapter';
import { BrowserAgent } from '@newrelic/browser-agent/loaders/browser-agent'; import { BrowserAgent } from '@newrelic/browser-agent/loaders/browser-agent';
import log from '../../core/logger';
interface NewRelicAdapterConfig { interface NewRelicAdapterConfig {
/** Maximum events to batch before auto-flush */ /** Maximum events to batch before auto-flush */
@ -137,7 +138,7 @@ export class NewRelicAdapter implements AnalyticsAdapter {
this.agent.addPageAction(event.name, payload); this.agent.addPageAction(event.name, payload);
this.log('Event sent:', event.name, payload); this.log('Event sent:', event.name, payload);
} catch (error) { } catch (error) {
console.error('Failed to send New Relic event:', error); log.error('Failed to send New Relic event:', error);
} }
} }
@ -163,7 +164,7 @@ export class NewRelicAdapter implements AnalyticsAdapter {
this.agent.addPageAction(`${eventName}_batch`, batchPayload); this.agent.addPageAction(`${eventName}_batch`, batchPayload);
this.log(`Batched ${events.length} events:`, eventName, batchPayload); this.log(`Batched ${events.length} events:`, eventName, batchPayload);
} catch (error) { } catch (error) {
console.error('Failed to send batched New Relic event:', error); log.error('Failed to send batched New Relic event:', error);
} }
} }
@ -237,7 +238,7 @@ export class NewRelicAdapter implements AnalyticsAdapter {
private log(message: string, ...args: any[]): void { private log(message: string, ...args: any[]): void {
if (this.config.debug) { if (this.config.debug) {
console.log(`[NewRelicAdapter] ${message}`, ...args); log.info(`[NewRelicAdapter] ${message}`, ...args);
} }
} }
} }

View File

@ -1,5 +1,6 @@
import { AnalyticsAdapter, AnalyticsConfig, EventMetadata, EventOptions } from './adapters/analyticsAdapter'; import { AnalyticsAdapter, AnalyticsConfig, EventMetadata, EventOptions } from './adapters/analyticsAdapter';
import { GameEventName, GameEventProperties } from './events/gameEvents'; import { GameEventName, GameEventProperties } from './events/gameEvents';
import log from '../core/logger';
/** /**
* Central analytics service with pluggable adapters * Central analytics service with pluggable adapters
@ -36,7 +37,7 @@ export class AnalyticsService {
static initialize(config?: AnalyticsConfig): AnalyticsService { static initialize(config?: AnalyticsConfig): AnalyticsService {
if (AnalyticsService.instance) { if (AnalyticsService.instance) {
console.warn('AnalyticsService already initialized'); log.warn('AnalyticsService already initialized');
return AnalyticsService.instance; return AnalyticsService.instance;
} }
@ -108,7 +109,7 @@ export class AnalyticsService {
try { try {
adapter.track(event); adapter.track(event);
} catch (error) { } catch (error) {
console.error(`Adapter ${adapter.name} failed to track event:`, error); log.error(`Adapter ${adapter.name} failed to track event:`, error);
} }
} }
} }
@ -140,7 +141,7 @@ export class AnalyticsService {
try { try {
adapter.track(event); adapter.track(event);
} catch (error) { } catch (error) {
console.error(`Adapter ${adapter.name} failed to track custom event:`, error); log.error(`Adapter ${adapter.name} failed to track custom event:`, error);
} }
} }
} }
@ -170,7 +171,7 @@ export class AnalyticsService {
try { try {
adapter.flush(); adapter.flush();
} catch (error) { } catch (error) {
console.error(`Adapter ${adapter.name} failed to flush:`, error); log.error(`Adapter ${adapter.name} failed to flush:`, error);
} }
} }
} }
@ -184,7 +185,7 @@ export class AnalyticsService {
try { try {
adapter.shutdown(); adapter.shutdown();
} catch (error) { } catch (error) {
console.error(`Adapter ${adapter.name} failed to shutdown:`, error); log.error(`Adapter ${adapter.name} failed to shutdown:`, error);
} }
} }
AnalyticsService.instance = null; AnalyticsService.instance = null;
@ -239,7 +240,7 @@ export class AnalyticsService {
private log(message: string, ...args: any[]): void { private log(message: string, ...args: any[]): void {
if (this.config.debug) { if (this.config.debug) {
console.log(`[AnalyticsService] ${message}`, ...args); log.info(`[AnalyticsService] ${message}`, ...args);
} }
} }
} }

View File

@ -5,12 +5,13 @@
import { progressionStore } from '../../stores/progression'; import { progressionStore } from '../../stores/progression';
import { gameConfigStore } from '../../stores/gameConfig'; import { gameConfigStore } from '../../stores/gameConfig';
import Button from '../shared/Button.svelte'; import Button from '../shared/Button.svelte';
import log from '../../core/logger';
export let levelId: string; export let levelId: string;
export let levelEntry: CloudLevelEntry; export let levelEntry: CloudLevelEntry;
async function handleLevelClick() { async function handleLevelClick() {
console.log('[LevelCard] Level clicked:', { log.info('[LevelCard] Level clicked:', {
levelId, levelId,
levelName: levelEntry.name, levelName: levelEntry.name,
isUnlocked, isUnlocked,
@ -20,24 +21,24 @@
// If level is locked and user not authenticated, prompt to sign in // If level is locked and user not authenticated, prompt to sign in
if (!isUnlocked && !$authStore.isAuthenticated) { if (!isUnlocked && !$authStore.isAuthenticated) {
console.log('[LevelCard] Locked level clicked - prompting for sign in'); log.info('[LevelCard] Locked level clicked - prompting for sign in');
try { try {
await authStore.login(); await authStore.login();
console.log('[LevelCard] Login completed'); log.info('[LevelCard] Login completed');
} catch (error) { } catch (error) {
console.error('[LevelCard] Login failed:', error); log.error('[LevelCard] Login failed:', error);
} }
return; return;
} }
// If level is still locked (progression), don't allow play // If level is still locked (progression), don't allow play
if (!isUnlocked) { if (!isUnlocked) {
console.log('[LevelCard] Level still locked after auth check (progression lock)'); log.info('[LevelCard] Level still locked after auth check (progression lock)');
return; return;
} }
// Navigate to level play route // Navigate to level play route
console.log('[LevelCard] Level unlocked, navigating to /play/' + levelId); log.info('[LevelCard] Level unlocked, navigating to /play/' + levelId);
navigate(`/play/${levelId}`); navigate(`/play/${levelId}`);
} }

View File

@ -4,7 +4,7 @@
import { Main } from '../../main'; import { Main } from '../../main';
import type { LevelConfig } from '../../levels/config/levelConfig'; import type { LevelConfig } from '../../levels/config/levelConfig';
import { LevelRegistry } from '../../levels/storage/levelRegistry'; import { LevelRegistry } from '../../levels/storage/levelRegistry';
import debugLog from '../../core/debug'; import log from '../../core/logger';
import { DefaultScene } from '../../core/defaultScene'; import { DefaultScene } from '../../core/defaultScene';
// svelte-routing passes params as an object with route params // svelte-routing passes params as an object with route params
@ -34,17 +34,17 @@
// Handle popstate (browser back/forward buttons) // Handle popstate (browser back/forward buttons)
function handlePopState() { function handlePopState() {
if (isInitialized && !isExiting && !window.location.pathname.startsWith('/play/')) { if (isInitialized && !isExiting && !window.location.pathname.startsWith('/play/')) {
debugLog('[PlayLevel] Navigation detected via popstate, starting cleanup'); log.debug('[PlayLevel] Navigation detected via popstate, starting cleanup');
isExiting = true; isExiting = true;
} }
} }
onMount(async () => { onMount(async () => {
console.log('[PlayLevel] Component mounted'); log.info('[PlayLevel] Component mounted');
console.log('[PlayLevel] params:', params); log.info('[PlayLevel] params:', params);
console.log('[PlayLevel] levelId prop:', levelId); log.info('[PlayLevel] levelId prop:', levelId);
console.log('[PlayLevel] actualLevelId:', actualLevelId); log.info('[PlayLevel] actualLevelId:', actualLevelId);
console.log('[PlayLevel] window.location.pathname:', window.location.pathname); log.info('[PlayLevel] window.location.pathname:', window.location.pathname);
// Try to extract levelId from URL if props don't have it // Try to extract levelId from URL if props don't have it
let extractedLevelId = actualLevelId; let extractedLevelId = actualLevelId;
@ -52,19 +52,19 @@
const match = window.location.pathname.match(/\/play\/(.+)/); const match = window.location.pathname.match(/\/play\/(.+)/);
if (match) { if (match) {
extractedLevelId = match[1]; extractedLevelId = match[1];
console.log('[PlayLevel] Extracted levelId from URL:', extractedLevelId); log.info('[PlayLevel] Extracted levelId from URL:', extractedLevelId);
} }
} }
levelName = extractedLevelId; levelName = extractedLevelId;
if (!levelName) { if (!levelName) {
console.error('[PlayLevel] No levelId found!'); log.error('[PlayLevel] No levelId found!');
error = 'No level specified'; error = 'No level specified';
return; return;
} }
console.log('[PlayLevel] Using levelName:', levelName); log.info('[PlayLevel] Using levelName:', levelName);
// Add event listeners // Add event listeners
window.addEventListener('beforeunload', handleBeforeUnload); window.addEventListener('beforeunload', handleBeforeUnload);
@ -75,7 +75,7 @@
const appElement = document.getElementById('app'); const appElement = document.getElementById('app');
if (appElement) { if (appElement) {
appElement.style.display = 'none'; appElement.style.display = 'none';
debugLog('[PlayLevel] App UI hidden'); log.debug('[PlayLevel] App UI hidden');
} }
// Get the main instance (should already exist from app initialization) // Get the main instance (should already exist from app initialization)
@ -93,7 +93,7 @@
throw new Error(`Level "${levelName}" not found`); throw new Error(`Level "${levelName}" not found`);
} }
debugLog('[PlayLevel] Level config loaded:', levelEntry); log.debug('[PlayLevel] Level config loaded:', levelEntry);
// Dispatch the levelSelected event (existing system expects this) // Dispatch the levelSelected event (existing system expects this)
// We'll refactor this later to call Main methods directly // We'll refactor this later to call Main methods directly
@ -107,10 +107,10 @@
window.dispatchEvent(event); window.dispatchEvent(event);
isInitialized = true; isInitialized = true;
debugLog('[PlayLevel] Level initialization started'); log.debug('[PlayLevel] Level initialization started');
} catch (err) { } catch (err) {
console.error('[PlayLevel] Error initializing level:', err); log.error('[PlayLevel] Error initializing level:', err);
error = err instanceof Error ? err.message : 'Unknown error'; error = err instanceof Error ? err.message : 'Unknown error';
// Show UI again on error // Show UI again on error
@ -127,8 +127,8 @@
}); });
onDestroy(async () => { onDestroy(async () => {
console.log('[PlayLevel] Component unmounting - cleaning up'); log.info('[PlayLevel] Component unmounting - cleaning up');
debugLog('[PlayLevel] Component unmounting - cleaning up'); log.debug('[PlayLevel] Component unmounting - cleaning up');
// Remove event listeners // Remove event listeners
window.removeEventListener('beforeunload', handleBeforeUnload); window.removeEventListener('beforeunload', handleBeforeUnload);
@ -138,8 +138,8 @@
const appElement = document.getElementById('app'); const appElement = document.getElementById('app');
if (appElement) { if (appElement) {
appElement.style.display = 'block'; appElement.style.display = 'block';
console.log('[PlayLevel] App UI restored'); log.info('[PlayLevel] App UI restored');
debugLog('[PlayLevel] App UI restored'); log.debug('[PlayLevel] App UI restored');
} }
try { try {
@ -148,7 +148,7 @@
await mainInstance.cleanupAndExit(); await mainInstance.cleanupAndExit();
} }
} catch (err) { } catch (err) {
console.error('[PlayLevel] Error during cleanup:', err); log.error('[PlayLevel] Error during cleanup:', err);
} }
}); });
</script> </script>

View File

@ -5,6 +5,7 @@
import { navigationStore } from '../../stores/navigation'; import { navigationStore } from '../../stores/navigation';
import { AuthService } from '../../services/authService'; import { AuthService } from '../../services/authService';
import { authStore } from '../../stores/auth'; import { authStore } from '../../stores/auth';
import log from '../../core/logger';
// Import game views // Import game views
import LevelSelect from '../game/LevelSelect.svelte'; import LevelSelect from '../game/LevelSelect.svelte';
@ -16,21 +17,21 @@
// Initialize Auth0 when component mounts // Initialize Auth0 when component mounts
onMount(async () => { onMount(async () => {
console.log('[App] ========== APP MOUNTED - INITIALIZING AUTH0 =========='); log.info('[App] ========== APP MOUNTED - INITIALIZING AUTH0 ==========');
try { try {
const authService = AuthService.getInstance(); const authService = AuthService.getInstance();
await authService.initialize(); await authService.initialize();
console.log('[App] Auth0 initialized successfully'); log.info('[App] Auth0 initialized successfully');
// Refresh auth store to update UI with current auth state // Refresh auth store to update UI with current auth state
console.log('[App] Refreshing auth store...'); log.info('[App] Refreshing auth store...');
await authStore.refresh(); await authStore.refresh();
console.log('[App] Auth store refreshed'); log.info('[App] Auth store refreshed');
} catch (error) { } catch (error) {
console.error('[App] !!!!! AUTH0 INITIALIZATION FAILED !!!!!', error); log.error('[App] !!!!! AUTH0 INITIALIZATION FAILED !!!!!', error);
console.error('[App] Error details:', error?.message, error?.stack); log.error('[App] Error details:', error?.message, error?.stack);
} }
console.log('[App] ========== AUTH0 INITIALIZATION COMPLETE =========='); log.info('[App] ========== AUTH0 INITIALIZATION COMPLETE ==========');
}); });
</script> </script>

View File

@ -5,6 +5,7 @@
import type { GameResult } from '../../services/gameResultsService'; import type { GameResult } from '../../services/gameResultsService';
import { CloudLeaderboardService, type CloudLeaderboardEntry, getDisplayName } from '../../services/cloudLeaderboardService'; import { CloudLeaderboardService, type CloudLeaderboardEntry, getDisplayName } from '../../services/cloudLeaderboardService';
import { formatStars } from '../../game/scoreCalculator'; import { formatStars } from '../../game/scoreCalculator';
import log from '../../core/logger';
// View toggle: 'local' or 'cloud' // View toggle: 'local' or 'cloud'
let activeView: 'local' | 'cloud' = 'cloud'; let activeView: 'local' | 'cloud' = 'cloud';
@ -52,7 +53,7 @@
} }
} catch (error) { } catch (error) {
cloudError = 'Failed to load cloud leaderboard'; cloudError = 'Failed to load cloud leaderboard';
console.error('[Leaderboard] Cloud load error:', error); log.error('[Leaderboard] Cloud load error:', error);
} finally { } finally {
cloudLoading = false; cloudLoading = false;
cloudLoadingMore = false; cloudLoadingMore = false;

View File

@ -8,6 +8,7 @@
import Checkbox from '../shared/Checkbox.svelte'; import Checkbox from '../shared/Checkbox.svelte';
import NumberInput from '../shared/NumberInput.svelte'; import NumberInput from '../shared/NumberInput.svelte';
import InfoBox from '../shared/InfoBox.svelte'; import InfoBox from '../shared/InfoBox.svelte';
import log from '../../core/logger';
let message = ''; let message = '';
let messageType: 'success' | 'error' | 'warning' = 'success'; let messageType: 'success' | 'error' | 'warning' = 'success';
@ -21,7 +22,7 @@
const config = JSON.parse(stored); const config = JSON.parse(stored);
gameConfigStore.set(config); gameConfigStore.set(config);
} catch (error) { } catch (error) {
console.warn('[SettingsScreen] Failed to reload config:', error); log.warn('[SettingsScreen] Failed to reload config:', error);
} }
} }
}); });

View File

@ -2,58 +2,46 @@ import { mount } from 'svelte';
import App from '../components/layouts/App.svelte'; import App from '../components/layouts/App.svelte';
import { LegacyMigration } from '../levels/migration/legacyMigration'; import { LegacyMigration } from '../levels/migration/legacyMigration';
import { LevelRegistry } from '../levels/storage/levelRegistry'; import { LevelRegistry } from '../levels/storage/levelRegistry';
import debugLog from './debug'; import log from './logger';
// Type for Main class - imported dynamically to avoid circular dependency // Type for Main class - imported dynamically to avoid circular dependency
type MainClass = new (progressCallback?: (percent: number, message: string) => void) => any; type MainClass = new (progressCallback?: (percent: number, message: string) => void) => any;
/** /**
* Initialize the application * Initialize the application
* - Check for legacy data migration
* - Initialize level registry
* - Mount Svelte app
* - Create Main instance
*/ */
export async function initializeApp(MainConstructor: MainClass): Promise<void> { export async function initializeApp(MainConstructor: MainClass): Promise<void> {
console.log('[Main] ========================================'); log.info('[Main] ========================================');
console.log('[Main] initializeApp() STARTED at', new Date().toISOString()); log.info('[Main] initializeApp() STARTED at', new Date().toISOString());
console.log('[Main] ========================================'); log.info('[Main] ========================================');
// Check for legacy data migration
const needsMigration = LegacyMigration.needsMigration(); const needsMigration = LegacyMigration.needsMigration();
console.log('[Main] Needs migration check:', needsMigration); log.info('[Main] Needs migration check:', needsMigration);
if (needsMigration) { if (needsMigration) {
debugLog('[Main] Legacy data detected - showing migration modal'); log.debug('[Main] Legacy data detected - showing migration modal');
return new Promise<void>((resolve) => { return new Promise<void>((resolve) => {
LegacyMigration.showMigrationModal(async (result) => { LegacyMigration.showMigrationModal(async (result) => {
debugLog('[Main] Migration completed:', result); log.debug('[Main] Migration completed:', result);
// Initialize the new registry system
try { try {
console.log('[Main] About to call LevelRegistry.getInstance().initialize() [AFTER MIGRATION]'); log.info('[Main] Initializing LevelRegistry [AFTER MIGRATION]');
await LevelRegistry.getInstance().initialize(); await LevelRegistry.getInstance().initialize();
console.log('[Main] LevelRegistry.initialize() completed successfully [AFTER MIGRATION]'); log.info('[Main] LevelRegistry initialized [AFTER MIGRATION]');
debugLog('[Main] LevelRegistry initialized after migration');
// Mount Svelte app and create Main
mountAppAndCreateMain(MainConstructor); mountAppAndCreateMain(MainConstructor);
resolve(); resolve();
} catch (error) { } catch (error) {
console.error('[Main] Failed to initialize LevelRegistry after migration:', error); log.error('[Main] Failed to initialize LevelRegistry after migration:', error);
resolve(); resolve();
} }
}); });
}); });
} else { }
console.log('[Main] No migration needed - proceeding to initialize registry');
// Initialize the new registry system log.info('[Main] No migration needed - proceeding to initialize registry');
try { try {
console.log('[Main] About to call LevelRegistry.getInstance().initialize()'); log.info('[Main] Initializing LevelRegistry');
console.log('[Main] Timestamp before initialize:', Date.now());
await LevelRegistry.getInstance().initialize(); await LevelRegistry.getInstance().initialize();
console.log('[Main] Timestamp after initialize:', Date.now()); log.info('[Main] LevelRegistry initialized successfully');
console.log('[Main] LevelRegistry.initialize() completed successfully');
debugLog('[Main] LevelRegistry initialized');
// Expose registry to window for debugging (dev mode) // Expose registry to window for debugging (dev mode)
const isDev = window.location.hostname === 'localhost' || const isDev = window.location.hostname === 'localhost' ||
@ -61,59 +49,48 @@ export async function initializeApp(MainConstructor: MainClass): Promise<void> {
window.location.port !== ''; window.location.port !== '';
if (isDev) { if (isDev) {
(window as any).__levelRegistry = LevelRegistry.getInstance(); (window as any).__levelRegistry = LevelRegistry.getInstance();
console.log('[Main] LevelRegistry exposed to window.__levelRegistry for debugging'); log.info('[Main] LevelRegistry exposed to window.__levelRegistry');
console.log('[Main] To clear caches: window.__levelRegistry.reset(); location.reload()');
} }
} catch (error) { } catch (error) {
console.error('[Main] !!!!! EXCEPTION in LevelRegistry initialization !!!!!'); log.error('[Main] Failed to initialize LevelRegistry:', error);
console.error('[Main] Failed to initialize LevelRegistry:', error);
console.error('[Main] Error stack:', (error as Error)?.stack);
}
} }
// Mount Svelte app and create Main
mountAppAndCreateMain(MainConstructor); mountAppAndCreateMain(MainConstructor);
log.info('[Main] initializeApp() FINISHED');
console.log('[Main] initializeApp() FINISHED at', new Date().toISOString());
} }
/** /**
* Mount the Svelte app and create Main instance * Mount the Svelte app and create Main instance
*/ */
function mountAppAndCreateMain(MainConstructor: MainClass): void { function mountAppAndCreateMain(MainConstructor: MainClass): void {
console.log('[Main] Mounting Svelte app'); log.info('[Main] Mounting Svelte app');
const appElement = document.getElementById('app'); const appElement = document.getElementById('app');
if (appElement) { if (appElement) {
mount(App, { mount(App, { target: appElement });
target: appElement log.info('[Main] Svelte app mounted successfully');
});
console.log('[Main] Svelte app mounted successfully');
// Create Main instance lazily only if it doesn't exist
if (!(window as any).__mainInstance) { if (!(window as any).__mainInstance) {
debugLog('[Main] Creating Main instance (not initialized)'); log.debug('[Main] Creating Main instance');
const main = new MainConstructor(); const main = new MainConstructor();
(window as any).__mainInstance = main; (window as any).__mainInstance = main;
} }
} else { } else {
console.error('[Main] Failed to mount Svelte app - #app element not found'); log.error('[Main] Failed to mount Svelte app - #app element not found');
} }
} }
/** /**
* Set up global error handler for shader loading errors * Set up global error handler for shader loading errors
* Suppress non-critical BabylonJS shader loading errors during development
*/ */
export function setupErrorHandler(): void { export function setupErrorHandler(): void {
window.addEventListener('unhandledrejection', (event) => { window.addEventListener('unhandledrejection', (event) => {
const error = event.reason; const error = event.reason;
if (error && error.message) { if (error?.message) {
// Only suppress specific shader-related errors, not asset loading errors
if (error.message.includes('rgbdDecode.fragment') || if (error.message.includes('rgbdDecode.fragment') ||
error.message.includes('procedural.vertex') || error.message.includes('procedural.vertex') ||
(error.message.includes('Failed to fetch dynamically imported module') && (error.message.includes('Failed to fetch dynamically imported module') &&
(error.message.includes('rgbdDecode') || error.message.includes('procedural')))) { (error.message.includes('rgbdDecode') || error.message.includes('procedural')))) {
debugLog('[Main] Suppressed shader loading error (should be fixed by Vite pre-bundling):', error.message); log.debug('[Main] Suppressed shader loading error:', error.message);
event.preventDefault(); event.preventDefault();
} }
} }

View File

@ -1,7 +1,7 @@
import { Engine } from "@babylonjs/core"; import { Engine } from "@babylonjs/core";
import { DefaultScene } from "./defaultScene"; import { DefaultScene } from "./defaultScene";
import { RockFactory } from "../environment/asteroids/rockFactory"; import { RockFactory } from "../environment/asteroids/rockFactory";
import debugLog from './debug'; import log from './logger';
import Level from "../levels/level"; import Level from "../levels/level";
export interface CleanupContext { export interface CleanupContext {
@ -18,7 +18,7 @@ export async function cleanupAndExit(
context: CleanupContext, context: CleanupContext,
canvas: HTMLCanvasElement canvas: HTMLCanvasElement
): Promise<void> { ): Promise<void> {
debugLog('[Main] cleanupAndExit() called - starting graceful shutdown'); log.debug('[Main] cleanupAndExit() called - starting graceful shutdown');
try { try {
context.getEngine().stopRenderLoop(); context.getEngine().stopRenderLoop();
disposeCurrentLevel(context); disposeCurrentLevel(context);
@ -29,7 +29,7 @@ export async function cleanupAndExit(
context.resetState(); context.resetState();
clearCanvas(canvas); clearCanvas(canvas);
} catch (error) { } catch (error) {
console.error('[Main] Cleanup failed:', error); log.error('[Main] Cleanup failed:', error);
window.location.reload(); window.location.reload();
} }
} }
@ -47,7 +47,7 @@ async function exitXRSession(): Promise<void> {
try { try {
await DefaultScene.XR.baseExperience.exitXRAsync(); await DefaultScene.XR.baseExperience.exitXRAsync();
} catch (error) { } catch (error) {
debugLog('[Main] Error exiting XR:', error); log.debug('[Main] Error exiting XR:', error);
} }
} }
DefaultScene.XR = null; DefaultScene.XR = null;

View File

@ -1,9 +0,0 @@
import {GameConfig} from "./gameConfig";
const config = GameConfig.getInstance();
export default function debugLog(...params: any[]) {
if (config.debug) {
console.log(...params);
}
}

View File

@ -1,3 +1,5 @@
import log from './logger';
/** /**
* Default ship physics configuration * Default ship physics configuration
*/ */
@ -93,7 +95,7 @@ export class GameConfig {
this.save(); this.save();
} }
} catch (error) { } catch (error) {
console.warn('Failed to load game config from localStorage:', error); log.warn('Failed to load game config from localStorage:', error);
} }
} }

View File

@ -5,7 +5,7 @@ import Level from "../../levels/level";
import { RockFactory } from "../../environment/asteroids/rockFactory"; import { RockFactory } from "../../environment/asteroids/rockFactory";
import { LevelConfig } from "../../levels/config/levelConfig"; import { LevelConfig } from "../../levels/config/levelConfig";
import { Preloader } from "../../ui/screens/preloader"; import { Preloader } from "../../ui/screens/preloader";
import debugLog from '../debug'; import log from '../logger';
/** /**
* Interface for Main class methods needed by the level selected handler * Interface for Main class methods needed by the level selected handler
@ -35,7 +35,7 @@ export function createLevelSelectedHandler(context: LevelSelectedContext): (e: C
context.setStarted(true); context.setStarted(true);
const { levelName, config } = e.detail as { levelName: string, config: LevelConfig }; const { levelName, config } = e.detail as { levelName: string, config: LevelConfig };
debugLog(`[Main] Starting level: ${levelName}`); log.debug(`[Main] Starting level: ${levelName}`);
// Hide all UI elements // Hide all UI elements
const levelSelect = document.querySelector('#levelSelect') as HTMLElement; const levelSelect = document.querySelector('#levelSelect') as HTMLElement;
@ -57,7 +57,7 @@ export function createLevelSelectedHandler(context: LevelSelectedContext): (e: C
try { try {
// Initialize engine if this is first time // Initialize engine if this is first time
if (!context.isInitialized()) { if (!context.isInitialized()) {
debugLog('[Main] First level selected - initializing engine'); log.debug('[Main] First level selected - initializing engine');
preloader.updateProgress(0, 'Initializing game engine...'); preloader.updateProgress(0, 'Initializing game engine...');
await context.initializeEngine(); await context.initializeEngine();
} }
@ -65,14 +65,14 @@ export function createLevelSelectedHandler(context: LevelSelectedContext): (e: C
// Load assets if this is the first level being played // Load assets if this is the first level being played
if (!context.areAssetsLoaded()) { if (!context.areAssetsLoaded()) {
preloader.updateProgress(40, 'Loading 3D models and textures...'); preloader.updateProgress(40, 'Loading 3D models and textures...');
debugLog('[Main] Loading assets for first time'); log.debug('[Main] Loading assets for first time');
// Load visual assets (meshes, particles) // Load visual assets (meshes, particles)
ParticleHelper.BaseAssetsUrl = window.location.href; ParticleHelper.BaseAssetsUrl = window.location.href;
await RockFactory.init(); await RockFactory.init();
context.setAssetsLoaded(true); context.setAssetsLoaded(true);
debugLog('[Main] Assets loaded successfully'); log.debug('[Main] Assets loaded successfully');
preloader.updateProgress(60, 'Assets loaded'); preloader.updateProgress(60, 'Assets loaded');
} }
@ -88,9 +88,9 @@ export function createLevelSelectedHandler(context: LevelSelectedContext): (e: C
try { try {
preloader.updateProgress(75, 'Entering VR...'); preloader.updateProgress(75, 'Entering VR...');
xrSession = await DefaultScene.XR.baseExperience.enterXRAsync('immersive-vr', 'local-floor'); xrSession = await DefaultScene.XR.baseExperience.enterXRAsync('immersive-vr', 'local-floor');
debugLog('XR session started successfully (render loop paused until camera is ready)'); log.debug('XR session started successfully (render loop paused until camera is ready)');
} catch (error) { } catch (error) {
debugLog('Failed to enter XR, will fall back to flat mode:', error); log.debug('Failed to enter XR, will fall back to flat mode:', error);
DefaultScene.XR = null; DefaultScene.XR = null;
engine.runRenderLoop(() => { engine.runRenderLoop(() => {
DefaultScene.MainScene.render(); DefaultScene.MainScene.render();
@ -112,9 +112,9 @@ export function createLevelSelectedHandler(context: LevelSelectedContext): (e: C
const camera = DefaultScene.XR?.baseExperience?.camera || DefaultScene.MainScene.activeCamera; const camera = DefaultScene.XR?.baseExperience?.camera || DefaultScene.MainScene.activeCamera;
if (camera && audioEngine.listener) { if (camera && audioEngine.listener) {
audioEngine.listener.attach(camera); audioEngine.listener.attach(camera);
debugLog('[Main] Audio listener attached to camera for spatial audio'); log.debug('[Main] Audio listener attached to camera for spatial audio');
} else { } else {
debugLog('[Main] WARNING: Could not attach audio listener - camera or listener not available'); log.warn('[Main] Could not attach audio listener - camera or listener not available');
} }
preloader.updateProgress(90, 'Creating level...'); preloader.updateProgress(90, 'Creating level...');
@ -134,26 +134,26 @@ export function createLevelSelectedHandler(context: LevelSelectedContext): (e: C
// Listen for replay requests from the ship // Listen for replay requests from the ship
if (ship) { if (ship) {
ship.onReplayRequestObservable.add(() => { ship.onReplayRequestObservable.add(() => {
debugLog('Replay requested - reloading page'); log.debug('Replay requested - reloading page');
window.location.reload(); window.location.reload();
}); });
} }
// If we entered XR before level creation, manually setup camera parenting // If we entered XR before level creation, manually setup camera parenting
console.log('[Main] ========== CHECKING XR STATE =========='); log.info('[Main] ========== CHECKING XR STATE ==========');
console.log('[Main] DefaultScene.XR exists:', !!DefaultScene.XR); log.info('[Main] DefaultScene.XR exists:', !!DefaultScene.XR);
console.log('[Main] xrSession exists:', !!xrSession); log.info('[Main] xrSession exists:', !!xrSession);
if (DefaultScene.XR) { if (DefaultScene.XR) {
console.log('[Main] XR base experience state:', DefaultScene.XR.baseExperience.state); log.info('[Main] XR base experience state:', DefaultScene.XR.baseExperience.state);
} }
if (DefaultScene.XR && xrSession && DefaultScene.XR.baseExperience.state === 2) { if (DefaultScene.XR && xrSession && DefaultScene.XR.baseExperience.state === 2) {
debugLog('[Main] XR already active - using consolidated setupXRCamera()'); log.debug('[Main] XR already active - using consolidated setupXRCamera()');
level1.setupXRCamera(); level1.setupXRCamera();
await level1.showMissionBrief(); await level1.showMissionBrief();
debugLog('[Main] XR setup and mission brief complete'); log.debug('[Main] XR setup and mission brief complete');
} else { } else {
console.log('[Main] XR not active yet - will use onInitialXRPoseSetObservable instead'); log.info('[Main] XR not active yet - will use onInitialXRPoseSetObservable instead');
engine.runRenderLoop(() => { engine.runRenderLoop(() => {
DefaultScene.MainScene.render(); DefaultScene.MainScene.render();
}); });
@ -166,20 +166,20 @@ export function createLevelSelectedHandler(context: LevelSelectedContext): (e: C
}, 500); }, 500);
// Hide UI (no longer remove from DOM - let Svelte routing handle it) // Hide UI (no longer remove from DOM - let Svelte routing handle it)
console.log('[Main] ========== HIDING UI FOR GAMEPLAY =========='); log.info('[Main] ========== HIDING UI FOR GAMEPLAY ==========');
console.log('[Main] Timestamp:', Date.now()); log.info('[Main] Timestamp:', Date.now());
// Start the game // Start the game
console.log('[Main] About to call context.play()'); log.info('[Main] About to call context.play()');
await context.play(); await context.play();
console.log('[Main] context.play() completed'); log.info('[Main] context.play() completed');
}); });
// Now initialize the level (after observable is registered) // Now initialize the level (after observable is registered)
await currentLevel.initialize(); await currentLevel.initialize();
} catch (error) { } catch (error) {
console.error('[Main] Level initialization failed:', error); log.error('[Main] Level initialization failed:', error);
preloader.updateProgress(0, 'Failed to load level. Please refresh and try again.'); preloader.updateProgress(0, 'Failed to load level. Please refresh and try again.');
} }
}; };

15
src/core/logger.ts Normal file
View File

@ -0,0 +1,15 @@
import log from 'loglevel';
// Check localStorage for custom level (enables production debugging)
const storedLevel = localStorage.getItem('log-level');
// Set level: localStorage override > environment default
if (storedLevel) {
log.setLevel(storedLevel as log.LogLevelDesc);
} else {
const isDev = window.location.hostname === 'localhost' ||
window.location.hostname.includes('dev.');
log.setLevel(isDev ? 'debug' : 'warn');
}
export default log;

View File

@ -1,7 +1,7 @@
import { WebXRDefaultExperience, WebXRFeaturesManager } from "@babylonjs/core"; import { WebXRDefaultExperience, WebXRFeaturesManager } from "@babylonjs/core";
import { DefaultScene } from "./defaultScene"; import { DefaultScene } from "./defaultScene";
import { InputControlManager } from "../ship/input/inputControlManager"; import { InputControlManager } from "../ship/input/inputControlManager";
import debugLog from './debug'; import log from './logger';
export interface ProgressReporter { export interface ProgressReporter {
reportProgress(percent: number, message: string): void; reportProgress(percent: number, message: string): void;
@ -24,7 +24,7 @@ export async function initializeXR(reporter: ProgressReporter): Promise<void> {
registerXRStateHandler(); registerXRStateHandler();
reporter.reportProgress(40, 'VR support enabled'); reporter.reportProgress(40, 'VR support enabled');
} catch (error) { } catch (error) {
debugLog("WebXR initialization failed:", error); log.debug("WebXR initialization failed:", error);
DefaultScene.XR = null; DefaultScene.XR = null;
reporter.reportProgress(40, 'Desktop mode'); reporter.reportProgress(40, 'Desktop mode');
} }
@ -37,7 +37,7 @@ async function createXRExperience(): Promise<void> {
disableHandTracking: true, disableHandTracking: true,
disableDefaultUI: true disableDefaultUI: true
}); });
debugLog(WebXRFeaturesManager.GetAvailableFeatures()); log.debug(WebXRFeaturesManager.GetAvailableFeatures());
} }
function registerXRStateHandler(): void { function registerXRStateHandler(): void {

View File

@ -7,7 +7,7 @@ import {
Vector3 Vector3
} from "@babylonjs/core"; } from "@babylonjs/core";
import {DefaultScene} from "../../core/defaultScene"; import {DefaultScene} from "../../core/defaultScene";
import debugLog from '../../core/debug'; import log from '../../core/logger';
/** /**
* Configuration for explosion effects * Configuration for explosion effects
@ -42,7 +42,7 @@ export class ExplosionManager {
constructor(scene: Scene, config?: ExplosionConfig) { constructor(scene: Scene, config?: ExplosionConfig) {
this.scene = scene; this.scene = scene;
this.config = { ...ExplosionManager.DEFAULT_CONFIG, ...config }; this.config = { ...ExplosionManager.DEFAULT_CONFIG, ...config };
debugLog(this.config); log.debug(this.config);
this._debrisBaseMesh = MeshBuilder.CreateIcoSphere( this._debrisBaseMesh = MeshBuilder.CreateIcoSphere(
'debrisBase', 'debrisBase',
{ {
@ -60,7 +60,7 @@ export class ExplosionManager {
* Initialize the explosion manager (no longer needed for MeshExploder, but kept for API compatibility) * Initialize the explosion manager (no longer needed for MeshExploder, but kept for API compatibility)
*/ */
public async initialize(): Promise<void> { public async initialize(): Promise<void> {
debugLog("ExplosionManager initialized with MeshExploder"); log.debug("ExplosionManager initialized with MeshExploder");
} }
/** /**
@ -69,7 +69,7 @@ export class ExplosionManager {
public async initAudio(audioEngine: AudioEngineV2): Promise<void> { public async initAudio(audioEngine: AudioEngineV2): Promise<void> {
this.audioEngine = audioEngine; this.audioEngine = audioEngine;
debugLog(`ExplosionManager: Initializing audio with pool size ${this.soundPoolSize}`); log.debug(`ExplosionManager: Initializing audio with pool size ${this.soundPoolSize}`);
// Create sound pool for concurrent explosions // Create sound pool for concurrent explosions
for (let i = 0; i < this.soundPoolSize; i++) { for (let i = 0; i < this.soundPoolSize; i++) {
@ -89,7 +89,7 @@ export class ExplosionManager {
this.explosionSounds.push(sound); this.explosionSounds.push(sound);
} }
debugLog(`ExplosionManager: Loaded ${this.explosionSounds.length} explosion sounds`); log.debug(`ExplosionManager: Loaded ${this.explosionSounds.length} explosion sounds`);
} }
/** /**
@ -104,7 +104,7 @@ export class ExplosionManager {
} }
// If all sounds are playing, reuse the first one (will cut off the oldest) // If all sounds are playing, reuse the first one (will cut off the oldest)
debugLog("ExplosionManager: All sounds in pool are playing, reusing sound 0"); log.debug("ExplosionManager: All sounds in pool are playing, reusing sound 0");
return this.explosionSounds[0] || null; return this.explosionSounds[0] || null;
} }
@ -119,7 +119,7 @@ export class ExplosionManager {
const sound = this.getAvailableSound(); const sound = this.getAvailableSound();
if (!sound) { if (!sound) {
debugLog("ExplosionManager: No sound available in pool"); log.debug("ExplosionManager: No sound available in pool");
return; return;
} }
@ -139,7 +139,7 @@ export class ExplosionManager {
} catch (error) { } catch (error) {
debugLog("ExplosionManager: Error playing explosion audio", error); log.debug("ExplosionManager: Error playing explosion audio", error);
explosionNode.dispose(); explosionNode.dispose();
} }
} }
@ -152,7 +152,7 @@ export class ExplosionManager {
* @returns Array of sphere mesh objects * @returns Array of sphere mesh objects
*/ */
private splitIntoSeparateMeshes(position: Vector3, pieces: number = 32): InstancedMesh[] { private splitIntoSeparateMeshes(position: Vector3, pieces: number = 32): InstancedMesh[] {
debugLog(`[ExplosionManager] Creating ${pieces} sphere debris pieces`); log.debug(`[ExplosionManager] Creating ${pieces} sphere debris pieces`);
const meshPieces: InstancedMesh[] = []; const meshPieces: InstancedMesh[] = [];
@ -182,13 +182,13 @@ export class ExplosionManager {
sphere.setEnabled(true); sphere.setEnabled(true);
meshPieces.push(sphere); meshPieces.push(sphere);
} catch (error) { } catch (error) {
console.error(`[ExplosionManager] ERROR creating debris piece ${i}:`, error); log.error(`[ExplosionManager] ERROR creating debris piece ${i}:`, error);
} }
} }
debugLog(`[ExplosionManager] Successfully created ${meshPieces.length}/${pieces} sphere debris pieces`); log.debug(`[ExplosionManager] Successfully created ${meshPieces.length}/${pieces} sphere debris pieces`);
if (meshPieces.length > 0) { if (meshPieces.length > 0) {
debugLog('[ExplosionManager] First piece sample:', { log.debug('[ExplosionManager] First piece sample:', {
name: meshPieces[0].name, name: meshPieces[0].name,
position: meshPieces[0].position.toString(), position: meshPieces[0].position.toString(),
isVisible: meshPieces[0].isVisible, isVisible: meshPieces[0].isVisible,
@ -203,8 +203,8 @@ export class ExplosionManager {
* @param mesh The mesh to explode (will be cloned internally) * @param mesh The mesh to explode (will be cloned internally)
*/ */
public playExplosion(mesh: AbstractMesh): void { public playExplosion(mesh: AbstractMesh): void {
debugLog('[ExplosionManager] playExplosion called'); log.debug('[ExplosionManager] playExplosion called');
debugLog('[ExplosionManager] Input mesh:', { log.debug('[ExplosionManager] Input mesh:', {
name: mesh.name, name: mesh.name,
id: mesh.id, id: mesh.id,
isInstancedMesh: !!(mesh as any).sourceMesh, isInstancedMesh: !!(mesh as any).sourceMesh,
@ -220,14 +220,14 @@ export class ExplosionManager {
let sourceMesh: Mesh; let sourceMesh: Mesh;
if ((mesh as any).sourceMesh) { if ((mesh as any).sourceMesh) {
sourceMesh = (mesh as any).sourceMesh as Mesh; sourceMesh = (mesh as any).sourceMesh as Mesh;
debugLog('[ExplosionManager] Using source mesh from instance:', sourceMesh.name); log.debug('[ExplosionManager] Using source mesh from instance:', sourceMesh.name);
} else { } else {
sourceMesh = mesh as Mesh; sourceMesh = mesh as Mesh;
debugLog('[ExplosionManager] Using mesh directly (not instanced)'); log.debug('[ExplosionManager] Using mesh directly (not instanced)');
} }
// Clone the source mesh so we don't affect the original // Clone the source mesh so we don't affect the original
debugLog('[ExplosionManager] Cloning mesh...'); log.debug('[ExplosionManager] Cloning mesh...');
mesh.computeWorldMatrix(true); mesh.computeWorldMatrix(true);
// Apply the instance's transformation to the cloned mesh // Apply the instance's transformation to the cloned mesh
const position = mesh.getAbsolutePosition().clone(); const position = mesh.getAbsolutePosition().clone();
@ -237,34 +237,34 @@ export class ExplosionManager {
// Check if mesh has proper geometry // Check if mesh has proper geometry
if (!mesh.getTotalVertices || mesh.getTotalVertices() === 0) { if (!mesh.getTotalVertices || mesh.getTotalVertices() === 0) {
console.error('[ExplosionManager] ERROR: Mesh has no vertices, cannot explode'); log.error('[ExplosionManager] ERROR: Mesh has no vertices, cannot explode');
mesh.dispose(); mesh.dispose();
return; return;
} }
// Split the mesh into separate mesh objects (MeshExploder requirement) // Split the mesh into separate mesh objects (MeshExploder requirement)
debugLog('[ExplosionManager] Splitting mesh into pieces...'); log.debug('[ExplosionManager] Splitting mesh into pieces...');
const meshPieces = this.splitIntoSeparateMeshes(position, 12); const meshPieces = this.splitIntoSeparateMeshes(position, 12);
if (meshPieces.length === 0) { if (meshPieces.length === 0) {
console.error('[ExplosionManager] ERROR: Failed to split mesh into pieces'); log.error('[ExplosionManager] ERROR: Failed to split mesh into pieces');
mesh.dispose(); mesh.dispose();
return; return;
} }
// Original mesh is no longer needed - the pieces replace it // Original mesh is no longer needed - the pieces replace it
debugLog('[ExplosionManager] Disposing original cloned mesh'); log.debug('[ExplosionManager] Disposing original cloned mesh');
mesh.dispose(); mesh.dispose();
// Create the exploder with the array of separate meshes // Create the exploder with the array of separate meshes
// The second parameter is optional - it's the center mesh to explode from // The second parameter is optional - it's the center mesh to explode from
// If not provided, MeshExploder will auto-calculate the center // If not provided, MeshExploder will auto-calculate the center
debugLog('[ExplosionManager] Creating MeshExploder...'); log.debug('[ExplosionManager] Creating MeshExploder...');
try { try {
const exploder = new MeshExploder((meshPieces as unknown) as Mesh[]); const exploder = new MeshExploder((meshPieces as unknown) as Mesh[]);
debugLog('[ExplosionManager] MeshExploder created successfully'); log.debug('[ExplosionManager] MeshExploder created successfully');
debugLog(`[ExplosionManager] Starting explosion animation:`, { log.debug(`[ExplosionManager] Starting explosion animation:`, {
pieceCount: meshPieces.length, pieceCount: meshPieces.length,
duration: this.config.duration, duration: this.config.duration,
maxForce: this.config.explosionForce maxForce: this.config.explosionForce
@ -286,7 +286,7 @@ export class ExplosionManager {
try { try {
exploder.explode(currentValue); exploder.explode(currentValue);
} catch (error) { } catch (error) {
console.error('[ExplosionManager] ERROR in explode():', error); log.error('[ExplosionManager] ERROR in explode():', error);
} }
// Animate debris size to zero (1.0 to 0.0) // Animate debris size to zero (1.0 to 0.0)
@ -301,7 +301,7 @@ export class ExplosionManager {
// Log every 15 frames (approximately every 250ms at 60fps) // Log every 15 frames (approximately every 250ms at 60fps)
if (frameCount % 15 === 0 || frameCount === 1) { if (frameCount % 15 === 0 || frameCount === 1) {
debugLog(`[ExplosionManager] Animation frame ${frameCount}:`, { log.debug(`[ExplosionManager] Animation frame ${frameCount}:`, {
elapsed: `${elapsed}ms`, elapsed: `${elapsed}ms`,
progress: progress.toFixed(3), progress: progress.toFixed(3),
currentValue: currentValue.toFixed(2), currentValue: currentValue.toFixed(2),
@ -313,16 +313,16 @@ export class ExplosionManager {
// Continue animation if not complete // Continue animation if not complete
if (progress >= 1.0) { if (progress >= 1.0) {
// Animation complete - remove observer and clean up // Animation complete - remove observer and clean up
debugLog(`[ExplosionManager] Animation complete after ${frameCount} frames, cleaning up`); log.debug(`[ExplosionManager] Animation complete after ${frameCount} frames, cleaning up`);
this.scene.onBeforeRenderObservable.remove(animationObserver); this.scene.onBeforeRenderObservable.remove(animationObserver);
this.cleanupExplosion(meshPieces); this.cleanupExplosion(meshPieces);
} }
}); });
// Log that animation loop is registered // Log that animation loop is registered
debugLog('[ExplosionManager] Starting animation loop...'); log.debug('[ExplosionManager] Starting animation loop...');
} catch (error) { } catch (error) {
console.error('[ExplosionManager] ERROR creating MeshExploder:', error); log.error('[ExplosionManager] ERROR creating MeshExploder:', error);
// Clean up pieces if exploder failed // Clean up pieces if exploder failed
meshPieces.forEach(piece => { meshPieces.forEach(piece => {
if (piece && !piece.isDisposed()) { if (piece && !piece.isDisposed()) {
@ -336,7 +336,7 @@ export class ExplosionManager {
* Clean up explosion meshes * Clean up explosion meshes
*/ */
private cleanupExplosion(meshPieces: InstancedMesh[]): void { private cleanupExplosion(meshPieces: InstancedMesh[]): void {
debugLog('[ExplosionManager] Starting cleanup of explosion meshes...'); log.debug('[ExplosionManager] Starting cleanup of explosion meshes...');
let disposedCount = 0; let disposedCount = 0;
// Dispose all the mesh pieces // Dispose all the mesh pieces
@ -346,12 +346,12 @@ export class ExplosionManager {
mesh.dispose(); mesh.dispose();
disposedCount++; disposedCount++;
} catch (error) { } catch (error) {
console.error(`[ExplosionManager] ERROR disposing piece ${index}:`, error); log.error(`[ExplosionManager] ERROR disposing piece ${index}:`, error);
} }
} }
}); });
debugLog(`[ExplosionManager] Cleanup complete - disposed ${disposedCount}/${meshPieces.length} pieces`); log.debug(`[ExplosionManager] Cleanup complete - disposed ${disposedCount}/${meshPieces.length} pieces`);
} }
/** /**
@ -360,6 +360,6 @@ export class ExplosionManager {
public dispose(): void { public dispose(): void {
this._debrisBaseMesh.dispose(false, true); this._debrisBaseMesh.dispose(false, true);
// Nothing to dispose with MeshExploder approach // Nothing to dispose with MeshExploder approach
debugLog("ExplosionManager disposed"); log.debug("ExplosionManager disposed");
} }
} }

View File

@ -18,7 +18,7 @@ import {DefaultScene} from "../../core/defaultScene";
import {ScoreEvent} from "../../ui/hud/scoreboard"; import {ScoreEvent} from "../../ui/hud/scoreboard";
import {GameConfig} from "../../core/gameConfig"; import {GameConfig} from "../../core/gameConfig";
import {ExplosionManager} from "./explosionManager"; import {ExplosionManager} from "./explosionManager";
import debugLog from '../../core/debug'; import log from '../../core/logger';
import loadAsset from "../../utils/loadAsset"; import loadAsset from "../../utils/loadAsset";
export class Rock { export class Rock {
@ -66,7 +66,7 @@ export class RockFactory {
* Reset static state - call during game cleanup * Reset static state - call during game cleanup
*/ */
public static reset(): void { public static reset(): void {
debugLog('[RockFactory] Resetting static state'); log.debug('[RockFactory] Resetting static state');
this._asteroidMesh = null; this._asteroidMesh = null;
if (this._explosionManager) { if (this._explosionManager) {
this._explosionManager.dispose(); this._explosionManager.dispose();
@ -83,20 +83,20 @@ export class RockFactory {
* Call this AFTER audio engine is unlocked * Call this AFTER audio engine is unlocked
*/ */
public static async initAudio(audioEngine: AudioEngineV2) { public static async initAudio(audioEngine: AudioEngineV2) {
debugLog('[RockFactory] Initializing audio via ExplosionManager'); log.debug('[RockFactory] Initializing audio via ExplosionManager');
if (this._explosionManager) { if (this._explosionManager) {
await this._explosionManager.initAudio(audioEngine); await this._explosionManager.initAudio(audioEngine);
} }
debugLog('[RockFactory] Audio initialization complete'); log.debug('[RockFactory] Audio initialization complete');
} }
private static async loadMesh() { private static async loadMesh() {
debugLog('loading mesh'); log.debug('loading mesh');
const asset = await loadAsset("asteroid.glb"); const asset = await loadAsset("asteroid.glb");
this._asteroidMesh = asset.meshes.get('Asteroid') || null; this._asteroidMesh = asset.meshes.get('Asteroid') || null;
if (this._asteroidMesh) { if (this._asteroidMesh) {
this._asteroidMesh.setEnabled(false); this._asteroidMesh.setEnabled(false);
} }
debugLog(this._asteroidMesh); log.debug(this._asteroidMesh);
} }
public static async createRock(i: number, position: Vector3, scale: number, public static async createRock(i: number, position: Vector3, scale: number,
@ -108,7 +108,7 @@ export class RockFactory {
} }
const rock = new InstancedMesh("asteroid-" +i, this._asteroidMesh as Mesh); const rock = new InstancedMesh("asteroid-" +i, this._asteroidMesh as Mesh);
debugLog(rock.id); log.debug(rock.id);
rock.scaling = new Vector3(scale, scale, scale); rock.scaling = new Vector3(scale, scale, scale);
rock.position = position; rock.position = position;
//rock.material = this._rockMaterial; //rock.material = this._rockMaterial;
@ -135,11 +135,11 @@ export class RockFactory {
// Only apply orbit constraint if enabled for this level and orbit center exists // Only apply orbit constraint if enabled for this level and orbit center exists
if (useOrbitConstraint && this._orbitCenter) { if (useOrbitConstraint && this._orbitCenter) {
debugLog(`[RockFactory] Applying orbit constraint for ${rock.name}`); log.debug(`[RockFactory] Applying orbit constraint for ${rock.name}`);
const constraint = new DistanceConstraint(Vector3.Distance(position, this._orbitCenter.body.transformNode.position), DefaultScene.MainScene); const constraint = new DistanceConstraint(Vector3.Distance(position, this._orbitCenter.body.transformNode.position), DefaultScene.MainScene);
body.addConstraint(this._orbitCenter.body, constraint); body.addConstraint(this._orbitCenter.body, constraint);
} else { } else {
debugLog(`[RockFactory] Orbit constraint disabled for ${rock.name} - asteroid will move freely`); log.debug(`[RockFactory] Orbit constraint disabled for ${rock.name} - asteroid will move freely`);
} }
body.setLinearDamping(0) body.setLinearDamping(0)
@ -152,9 +152,9 @@ export class RockFactory {
physicsPlugin.setActivationControl(body, PhysicsActivationControl.ALWAYS_ACTIVE); physicsPlugin.setActivationControl(body, PhysicsActivationControl.ALWAYS_ACTIVE);
} }
debugLog(`[RockFactory] Setting velocities for ${rock.name}:`); log.debug(`[RockFactory] Setting velocities for ${rock.name}:`);
debugLog(`[RockFactory] Linear velocity input: ${linearVelocitry.toString()}`); log.debug(`[RockFactory] Linear velocity input: ${linearVelocitry.toString()}`);
debugLog(`[RockFactory] Angular velocity input: ${angularVelocity.toString()}`); log.debug(`[RockFactory] Angular velocity input: ${angularVelocity.toString()}`);
body.setLinearVelocity(linearVelocitry); body.setLinearVelocity(linearVelocitry);
body.setAngularVelocity(angularVelocity); body.setAngularVelocity(angularVelocity);
@ -162,24 +162,24 @@ export class RockFactory {
// Verify velocities were set // Verify velocities were set
const setLinear = body.getLinearVelocity(); const setLinear = body.getLinearVelocity();
const setAngular = body.getAngularVelocity(); const setAngular = body.getAngularVelocity();
debugLog(`[RockFactory] Linear velocity after set: ${setLinear.toString()}`); log.debug(`[RockFactory] Linear velocity after set: ${setLinear.toString()}`);
debugLog(`[RockFactory] Angular velocity after set: ${setAngular.toString()}`); log.debug(`[RockFactory] Angular velocity after set: ${setAngular.toString()}`);
body.getCollisionObservable().add((eventData) => { body.getCollisionObservable().add((eventData) => {
if (eventData.type == 'COLLISION_STARTED') { if (eventData.type == 'COLLISION_STARTED') {
if ( eventData.collidedAgainst.transformNode.id == 'ammo') { if ( eventData.collidedAgainst.transformNode.id == 'ammo') {
debugLog('[RockFactory] ASTEROID HIT! Triggering explosion...'); log.debug('[RockFactory] ASTEROID HIT! Triggering explosion...');
score.notifyObservers({score: 1, remaining: -1, message: "Asteroid Destroyed"}); score.notifyObservers({score: 1, remaining: -1, message: "Asteroid Destroyed"});
// Get the asteroid mesh before disposing // Get the asteroid mesh before disposing
const asteroidMesh = eventData.collider.transformNode as AbstractMesh; const asteroidMesh = eventData.collider.transformNode as AbstractMesh;
debugLog('[RockFactory] Asteroid mesh to explode:', { log.debug('[RockFactory] Asteroid mesh to explode:', {
name: asteroidMesh.name, name: asteroidMesh.name,
id: asteroidMesh.id, id: asteroidMesh.id,
position: asteroidMesh.getAbsolutePosition().toString() position: asteroidMesh.getAbsolutePosition().toString()
}); });
// Dispose asteroid physics objects BEFORE explosion (to prevent double-disposal) // Dispose asteroid physics objects BEFORE explosion (to prevent double-disposal)
debugLog('[RockFactory] Disposing asteroid physics objects...'); log.debug('[RockFactory] Disposing asteroid physics objects...');
if (eventData.collider.shape) { if (eventData.collider.shape) {
eventData.collider.shape.dispose(); eventData.collider.shape.dispose();
} }
@ -194,7 +194,7 @@ export class RockFactory {
} }
// Dispose projectile physics objects // Dispose projectile physics objects
debugLog('[RockFactory] Disposing projectile physics objects...'); log.debug('[RockFactory] Disposing projectile physics objects...');
if (eventData.collidedAgainst.shape) { if (eventData.collidedAgainst.shape) {
eventData.collidedAgainst.shape.dispose(); eventData.collidedAgainst.shape.dispose();
} }
@ -204,7 +204,7 @@ export class RockFactory {
if (eventData.collidedAgainst) { if (eventData.collidedAgainst) {
eventData.collidedAgainst.dispose(); eventData.collidedAgainst.dispose();
} }
debugLog('[RockFactory] Disposal complete'); log.debug('[RockFactory] Disposal complete');
} }
} }
}); });

View File

@ -1,5 +1,5 @@
import {Color3, Color4, PointsCloudSystem, Scene, StandardMaterial, Vector3} from "@babylonjs/core"; import {Color3, Color4, PointsCloudSystem, Scene, StandardMaterial, Vector3} from "@babylonjs/core";
import debugLog from '../../core/debug'; import log from '../../core/logger';
/** /**
* Configuration options for background stars * Configuration options for background stars
@ -109,7 +109,7 @@ export class BackgroundStars {
// Make stars always render behind everything else // Make stars always render behind everything else
mesh.isPickable = false; mesh.isPickable = false;
debugLog(`Created ${this.config.count} background stars`); log.debug(`Created ${this.config.count} background stars`);
} }
}); });
} }

View File

@ -7,7 +7,7 @@ import {
} from "@babylonjs/core"; } from "@babylonjs/core";
import {DefaultScene} from "../../core/defaultScene"; import {DefaultScene} from "../../core/defaultScene";
import {GameConfig} from "../../core/gameConfig"; import {GameConfig} from "../../core/gameConfig";
import debugLog from "../../core/debug"; import log from "../../core/logger";
import loadAsset from "../../utils/loadAsset"; import loadAsset from "../../utils/loadAsset";
import {Vector3Array} from "../../levels/config/levelConfig"; import {Vector3Array} from "../../levels/config/levelConfig";
@ -50,7 +50,7 @@ export default class StarBase {
agg2.body.setMotionType(PhysicsMotionType.ANIMATED); agg2.body.setMotionType(PhysicsMotionType.ANIMATED);
agg2.body.getCollisionObservable().add((collidedBody) => { agg2.body.getCollisionObservable().add((collidedBody) => {
debugLog('collidedBody', collidedBody); log.debug('collidedBody', collidedBody);
}) })
landingAgg = new PhysicsAggregate(landingMesh, PhysicsShapeType.MESH); landingAgg = new PhysicsAggregate(landingMesh, PhysicsShapeType.MESH);

View File

@ -1,5 +1,5 @@
import { getAnalytics } from "../analytics"; import { getAnalytics } from "../analytics";
import debugLog from "../core/debug"; import log from "../core/logger";
import { calculateScore, ScoreCalculation } from "./scoreCalculator"; import { calculateScore, ScoreCalculation } from "./scoreCalculator";
/** /**
@ -64,7 +64,7 @@ export class GameStats {
hullDamage: this._hullDamageTaken hullDamage: this._hullDamageTaken
}, { sampleRate: 0.5 }); // 50% sampling for performance snapshots }, { sampleRate: 0.5 }); // 50% sampling for performance snapshots
} catch (error) { } catch (error) {
debugLog('Performance snapshot failed:', error); log.debug('Performance snapshot failed:', error);
} }
} }
@ -159,7 +159,7 @@ export class GameStats {
totalAsteroidsDestroyed: this._asteroidsDestroyed totalAsteroidsDestroyed: this._asteroidsDestroyed
}, { immediate: true }); // Send immediately }, { immediate: true }); // Send immediately
} catch (error) { } catch (error) {
debugLog('Session end tracking failed:', error); log.debug('Session end tracking failed:', error);
} }
// Stop performance tracking // Stop performance tracking

View File

@ -1,3 +1,5 @@
import log from '../core/logger';
/** /**
* Progression tracking system for level completion and feature unlocks * Progression tracking system for level completion and feature unlocks
*/ */
@ -62,7 +64,7 @@ export class ProgressionManager {
}; };
} }
} catch (error) { } catch (error) {
console.error('Error loading progression data:', error); log.error('Error loading progression data:', error);
} }
// Return fresh progression data // Return fresh progression data
@ -87,7 +89,7 @@ export class ProgressionManager {
}; };
localStorage.setItem(STORAGE_KEY, JSON.stringify(toSave)); localStorage.setItem(STORAGE_KEY, JSON.stringify(toSave));
} catch (error) { } catch (error) {
console.error('Error saving progression data:', error); log.error('Error saving progression data:', error);
} }
} }
@ -202,7 +204,7 @@ export class ProgressionManager {
const completedCount = this.getCompletedDefaultLevels().length; const completedCount = this.getCompletedDefaultLevels().length;
if (completedCount >= EDITOR_UNLOCK_REQUIREMENT && !this._data.editorUnlocked) { if (completedCount >= EDITOR_UNLOCK_REQUIREMENT && !this._data.editorUnlocked) {
this._data.editorUnlocked = true; this._data.editorUnlocked = true;
console.log(`🎉 Editor unlocked! (${completedCount} levels completed)`); log.info(`🎉 Editor unlocked! (${completedCount} levels completed)`);
} }
} }

View File

@ -19,7 +19,7 @@ import {
} from "./levelConfig"; } from "./levelConfig";
import { FireProceduralTexture } from "@babylonjs/procedural-textures"; import { FireProceduralTexture } from "@babylonjs/procedural-textures";
import { createSphereLightmap } from "../../environment/celestial/sphereLightmap"; import { createSphereLightmap } from "../../environment/celestial/sphereLightmap";
import debugLog from '../../core/debug'; import log from '../../core/logger';
import StarBase from "../../environment/stations/starBase"; import StarBase from "../../environment/stations/starBase";
import {LevelRegistry} from "../storage/levelRegistry"; import {LevelRegistry} from "../storage/levelRegistry";
@ -61,7 +61,7 @@ export class LevelDeserializer {
planets: AbstractMesh[]; planets: AbstractMesh[];
asteroids: AbstractMesh[]; asteroids: AbstractMesh[];
}> { }> {
debugLog('Deserializing level:', this.config.difficulty); log.debug('Deserializing level:', this.config.difficulty);
const baseResult = await this.createStartBase(); const baseResult = await this.createStartBase();
const sun = this.createSun(); const sun = this.createSun();
@ -158,7 +158,7 @@ export class LevelDeserializer {
planets.push(planet); planets.push(planet);
} }
debugLog(`Created ${planets.length} planets from config`); log.debug(`Created ${planets.length} planets from config`);
return planets; return planets;
} }
@ -173,15 +173,15 @@ export class LevelDeserializer {
for (let i = 0; i < this.config.asteroids.length; i++) { for (let i = 0; i < this.config.asteroids.length; i++) {
const asteroidConfig = this.config.asteroids[i]; const asteroidConfig = this.config.asteroids[i];
debugLog(`[LevelDeserializer] Creating asteroid ${i} (${asteroidConfig.id}):`); log.debug(`[LevelDeserializer] Creating asteroid ${i} (${asteroidConfig.id}):`);
debugLog(`[LevelDeserializer] Position: [${asteroidConfig.position.join(', ')}]`); log.debug(`[LevelDeserializer] Position: [${asteroidConfig.position.join(', ')}]`);
debugLog(`[LevelDeserializer] Scale: ${asteroidConfig.scale}`); log.debug(`[LevelDeserializer] Scale: ${asteroidConfig.scale}`);
debugLog(`[LevelDeserializer] Linear velocity: [${asteroidConfig.linearVelocity.join(', ')}]`); log.debug(`[LevelDeserializer] Linear velocity: [${asteroidConfig.linearVelocity.join(', ')}]`);
debugLog(`[LevelDeserializer] Angular velocity: [${asteroidConfig.angularVelocity.join(', ')}]`); log.debug(`[LevelDeserializer] Angular velocity: [${asteroidConfig.angularVelocity.join(', ')}]`);
// Use orbit constraints by default (true if not specified) // Use orbit constraints by default (true if not specified)
const useOrbitConstraints = this.config.useOrbitConstraints !== false; const useOrbitConstraints = this.config.useOrbitConstraints !== false;
debugLog(`[LevelDeserializer] Use orbit constraints: ${useOrbitConstraints}`); log.debug(`[LevelDeserializer] Use orbit constraints: ${useOrbitConstraints}`);
// Use RockFactory to create the asteroid // Use RockFactory to create the asteroid
const _rock = await RockFactory.createRock( const _rock = await RockFactory.createRock(
@ -202,7 +202,7 @@ export class LevelDeserializer {
} }
} }
debugLog(`Created ${asteroids.length} asteroids from config`); log.debug(`Created ${asteroids.length} asteroids from config`);
return asteroids; return asteroids;
} }

View File

@ -14,7 +14,7 @@ import setLoadingMessage from "../utils/setLoadingMessage";
import {LevelConfig} from "./config/levelConfig"; import {LevelConfig} from "./config/levelConfig";
import {LevelDeserializer} from "./config/levelDeserializer"; import {LevelDeserializer} from "./config/levelDeserializer";
import {BackgroundStars} from "../environment/background/backgroundStars"; import {BackgroundStars} from "../environment/background/backgroundStars";
import debugLog from '../core/debug'; import log from '../core/logger';
import {getAnalytics} from "../analytics"; import {getAnalytics} from "../analytics";
import {MissionBrief} from "../ui/hud/missionBrief"; import {MissionBrief} from "../ui/hud/missionBrief";
import {LevelRegistry} from "./storage/levelRegistry"; import {LevelRegistry} from "./storage/levelRegistry";
@ -52,18 +52,18 @@ export class Level1 implements Level {
if (!isReplayMode && DefaultScene.XR) { if (!isReplayMode && DefaultScene.XR) {
const xr = DefaultScene.XR; const xr = DefaultScene.XR;
debugLog('Level1 constructor - Setting up XR observables'); log.debug('Level1 constructor - Setting up XR observables');
debugLog('XR input exists:', !!xr.input); log.debug('XR input exists:', !!xr.input);
debugLog('onControllerAddedObservable exists:', !!xr.input?.onControllerAddedObservable); log.debug('onControllerAddedObservable exists:', !!xr.input?.onControllerAddedObservable);
xr.baseExperience.onInitialXRPoseSetObservable.add(() => { xr.baseExperience.onInitialXRPoseSetObservable.add(() => {
debugLog('[Level1] onInitialXRPoseSetObservable fired'); log.debug('[Level1] onInitialXRPoseSetObservable fired');
// Use consolidated XR camera setup // Use consolidated XR camera setup
this.setupXRCamera(); this.setupXRCamera();
// Show mission brief after camera setup // Show mission brief after camera setup
debugLog('[Level1] Showing mission brief on XR entry'); log.debug('[Level1] Showing mission brief on XR entry');
this.showMissionBrief(); this.showMissionBrief();
}); });
} }
@ -82,16 +82,16 @@ export class Level1 implements Level {
public setupXRCamera(): void { public setupXRCamera(): void {
const xr = DefaultScene.XR; const xr = DefaultScene.XR;
if (!xr) { if (!xr) {
debugLog('[Level1] setupXRCamera: No XR experience available'); log.debug('[Level1] setupXRCamera: No XR experience available');
return; return;
} }
if (!this._ship?.transformNode) { if (!this._ship?.transformNode) {
console.error('[Level1] setupXRCamera: Ship or transformNode not available'); log.error('[Level1] setupXRCamera: Ship or transformNode not available');
return; return;
} }
debugLog('[Level1] ========== setupXRCamera START =========='); log.debug('[Level1] ========== setupXRCamera START ==========');
// Create intermediate TransformNode for camera rotation // Create intermediate TransformNode for camera rotation
// WebXR camera only uses rotationQuaternion (not .rotation), and XR frame updates overwrite it // WebXR camera only uses rotationQuaternion (not .rotation), and XR frame updates overwrite it
@ -99,24 +99,24 @@ export class Level1 implements Level {
const cameraRig = new TransformNode("xrCameraRig", DefaultScene.MainScene); const cameraRig = new TransformNode("xrCameraRig", DefaultScene.MainScene);
cameraRig.parent = this._ship.transformNode; cameraRig.parent = this._ship.transformNode;
cameraRig.rotation = new Vector3(0, 0, 0); // Rotate 180° to face forward cameraRig.rotation = new Vector3(0, 0, 0); // Rotate 180° to face forward
debugLog('[Level1] Created cameraRig TransformNode, rotated 180°'); log.debug('[Level1] Created cameraRig TransformNode, rotated 180°');
// Parent XR camera to the rig // Parent XR camera to the rig
xr.baseExperience.camera.parent = cameraRig; xr.baseExperience.camera.parent = cameraRig;
xr.baseExperience.camera.position = new Vector3(0, .8, 0); xr.baseExperience.camera.position = new Vector3(0, .8, 0);
debugLog('[Level1] XR camera parented to cameraRig at position (0, 1.2, 0)'); log.debug('[Level1] XR camera parented to cameraRig at position (0, 1.2, 0)');
// Ensure render loop is running // Ensure render loop is running
const engine = DefaultScene.MainScene.getEngine(); const engine = DefaultScene.MainScene.getEngine();
engine.runRenderLoop(() => { engine.runRenderLoop(() => {
DefaultScene.MainScene.render(); DefaultScene.MainScene.render();
}); });
debugLog('[Level1] Render loop started/resumed'); log.debug('[Level1] Render loop started/resumed');
// Disable keyboard input in VR mode to prevent interference // Disable keyboard input in VR mode to prevent interference
if (this._ship.keyboardInput) { if (this._ship.keyboardInput) {
this._ship.keyboardInput.setEnabled(false); this._ship.keyboardInput.setEnabled(false);
debugLog('[Level1] Keyboard input disabled for VR mode'); log.debug('[Level1] Keyboard input disabled for VR mode');
} }
// Register pointer selection feature // Register pointer selection feature
@ -126,9 +126,9 @@ export class Level1 implements Level {
if (pointerFeature) { if (pointerFeature) {
const inputManager = InputControlManager.getInstance(); const inputManager = InputControlManager.getInstance();
inputManager.registerPointerFeature(pointerFeature); inputManager.registerPointerFeature(pointerFeature);
debugLog('[Level1] Pointer selection feature registered'); log.debug('[Level1] Pointer selection feature registered');
} else { } else {
debugLog('[Level1] WARNING: Pointer selection feature not available'); log.debug('[Level1] WARNING: Pointer selection feature not available');
} }
// Track WebXR session start // Track WebXR session start
@ -139,16 +139,16 @@ export class Level1 implements Level {
isImmersive: true isImmersive: true
}); });
} catch (error) { } catch (error) {
debugLog('[Level1] Analytics tracking failed:', error); log.debug('[Level1] Analytics tracking failed:', error);
} }
// Setup controller observer // Setup controller observer
xr.input.onControllerAddedObservable.add((controller) => { xr.input.onControllerAddedObservable.add((controller) => {
debugLog('[Level1] 🎮 Controller added:', controller.inputSource.handedness); log.debug('[Level1] 🎮 Controller added:', controller.inputSource.handedness);
this._ship.addController(controller); this._ship.addController(controller);
}); });
debugLog('[Level1] ========== setupXRCamera COMPLETE =========='); log.debug('[Level1] ========== setupXRCamera COMPLETE ==========');
} }
/** /**
@ -158,12 +158,12 @@ export class Level1 implements Level {
public async showMissionBrief(): Promise<void> { public async showMissionBrief(): Promise<void> {
// Prevent showing twice // Prevent showing twice
if (this._missionBriefShown) { if (this._missionBriefShown) {
console.log('[Level1] Mission brief already shown, skipping'); log.info('[Level1] Mission brief already shown, skipping');
return; return;
} }
this._missionBriefShown = true; this._missionBriefShown = true;
console.log('[Level1] showMissionBrief() called'); log.info('[Level1] showMissionBrief() called');
let directoryEntry: CloudLevelEntry | null = null; let directoryEntry: CloudLevelEntry | null = null;
@ -171,18 +171,18 @@ export class Level1 implements Level {
if (this._levelId) { if (this._levelId) {
try { try {
const registry = LevelRegistry.getInstance(); const registry = LevelRegistry.getInstance();
console.log('[Level1] ======================================'); log.info('[Level1] ======================================');
console.log('[Level1] Getting all levels from registry...'); log.info('[Level1] Getting all levels from registry...');
const allLevels = registry.getAllLevels(); const allLevels = registry.getAllLevels();
console.log('[Level1] Total levels in registry:', allLevels.size); log.info('[Level1] Total levels in registry:', allLevels.size);
console.log('[Level1] Looking for level ID:', this._levelId); log.info('[Level1] Looking for level ID:', this._levelId);
const registryEntry = allLevels.get(this._levelId); const registryEntry = allLevels.get(this._levelId);
console.log('[Level1] Registry entry found:', !!registryEntry); log.info('[Level1] Registry entry found:', !!registryEntry);
if (registryEntry) { if (registryEntry) {
directoryEntry = registryEntry; directoryEntry = registryEntry;
console.log('[Level1] Level entry data:', { log.info('[Level1] Level entry data:', {
id: directoryEntry?.id, id: directoryEntry?.id,
slug: directoryEntry?.slug, slug: directoryEntry?.slug,
name: directoryEntry?.name, name: directoryEntry?.name,
@ -193,39 +193,39 @@ export class Level1 implements Level {
}); });
if (directoryEntry?.missionBrief) { if (directoryEntry?.missionBrief) {
console.log('[Level1] Mission brief objectives:'); log.info('[Level1] Mission brief objectives:');
directoryEntry.missionBrief.forEach((item, i) => { directoryEntry.missionBrief.forEach((item, i) => {
console.log(` ${i + 1}. ${item}`); log.info(` ${i + 1}. ${item}`);
}); });
} else { } else {
console.warn('[Level1] ⚠️ No missionBrief found in level entry!'); log.warn('[Level1] ⚠️ No missionBrief found in level entry!');
} }
} else { } else {
console.error('[Level1] ❌ No registry entry found for level ID:', this._levelId); log.error('[Level1] ❌ No registry entry found for level ID:', this._levelId);
console.log('[Level1] Available level IDs:', Array.from(allLevels.keys())); log.info('[Level1] Available level IDs:', Array.from(allLevels.keys()));
} }
console.log('[Level1] ======================================'); log.info('[Level1] ======================================');
debugLog('[Level1] Retrieved directory entry for level:', this._levelId, directoryEntry); log.debug('[Level1] Retrieved directory entry for level:', this._levelId, directoryEntry);
} catch (error) { } catch (error) {
console.error('[Level1] ❌ Exception while getting directory entry:', error); log.error('[Level1] ❌ Exception while getting directory entry:', error);
debugLog('[Level1] Failed to get directory entry:', error); log.debug('[Level1] Failed to get directory entry:', error);
} }
} else { } else {
console.warn('[Level1] ⚠️ No level ID available, using config-only mission brief'); log.warn('[Level1] ⚠️ No level ID available, using config-only mission brief');
debugLog('[Level1] No level ID available, using config-only mission brief'); log.debug('[Level1] No level ID available, using config-only mission brief');
} }
console.log('[Level1] About to show mission brief. Has directoryEntry:', !!directoryEntry); log.info('[Level1] About to show mission brief. Has directoryEntry:', !!directoryEntry);
// Disable ship controls while mission brief is showing // Disable ship controls while mission brief is showing
debugLog('[Level1] Disabling ship controls for mission brief'); log.debug('[Level1] Disabling ship controls for mission brief');
const inputManager = InputControlManager.getInstance(); const inputManager = InputControlManager.getInstance();
inputManager.disableShipControls("MissionBrief"); inputManager.disableShipControls("MissionBrief");
// Show mission brief with trigger observable // Show mission brief with trigger observable
this._missionBrief.show(this._levelConfig, directoryEntry, this._ship.onMissionBriefTriggerObservable, () => { this._missionBrief.show(this._levelConfig, directoryEntry, this._ship.onMissionBriefTriggerObservable, () => {
debugLog('[Level1] Mission brief dismissed - enabling controls and starting game'); log.debug('[Level1] Mission brief dismissed - enabling controls and starting game');
inputManager.enableShipControls("MissionBrief"); inputManager.enableShipControls("MissionBrief");
this.startGameplay(); this.startGameplay();
}); });
@ -237,19 +237,19 @@ export class Level1 implements Level {
*/ */
private startGameplay(): void { private startGameplay(): void {
if (this._gameStarted) { if (this._gameStarted) {
debugLog('[Level1] startGameplay called but game already started'); log.debug('[Level1] startGameplay called but game already started');
return; return;
} }
this._gameStarted = true; this._gameStarted = true;
debugLog('[Level1] Starting gameplay'); log.debug('[Level1] Starting gameplay');
// Enable game end condition checking on ship // Enable game end condition checking on ship
this._ship.startGameplay(); this._ship.startGameplay();
// Start game timer // Start game timer
this._ship.gameStats.startTimer(); this._ship.gameStats.startTimer();
debugLog('Game timer started'); log.debug('Game timer started');
} }
public async play() { public async play() {
@ -266,55 +266,55 @@ export class Level1 implements Level {
playCount: 1 // TODO: Get actual play count from progression system playCount: 1 // TODO: Get actual play count from progression system
}); });
} catch (error) { } catch (error) {
debugLog('Analytics tracking failed:', error); log.debug('Analytics tracking failed:', error);
} }
// Play background music (already loaded during initialization) // Play background music (already loaded during initialization)
if (this._backgroundMusic) { if (this._backgroundMusic) {
this._backgroundMusic.play(); this._backgroundMusic.play();
debugLog('Started playing background music'); log.debug('Started playing background music');
} }
// If XR is available and session is active, mission brief will handle starting gameplay // If XR is available and session is active, mission brief will handle starting gameplay
if (DefaultScene.XR && DefaultScene.XR.baseExperience.state === WebXRState.IN_XR) { if (DefaultScene.XR && DefaultScene.XR.baseExperience.state === WebXRState.IN_XR) {
// XR session already active, mission brief is showing or has been dismissed // XR session already active, mission brief is showing or has been dismissed
debugLog('XR session already active, checking for controllers. Count:', DefaultScene.XR.input.controllers.length); log.debug('XR session already active, checking for controllers. Count:', DefaultScene.XR.input.controllers.length);
DefaultScene.XR.input.controllers.forEach((controller, index) => { DefaultScene.XR.input.controllers.forEach((controller, index) => {
debugLog(`Controller ${index} - handedness: ${controller.inputSource.handedness}`); log.debug(`Controller ${index} - handedness: ${controller.inputSource.handedness}`);
this._ship.addController(controller); this._ship.addController(controller);
}); });
// Wait and check again after a delay (controllers might connect later) // Wait and check again after a delay (controllers might connect later)
debugLog('Waiting 2 seconds to check for controllers again...'); log.debug('Waiting 2 seconds to check for controllers again...');
setTimeout(() => { setTimeout(() => {
debugLog('After 2 second delay - controller count:', DefaultScene.XR.input.controllers.length); log.debug('After 2 second delay - controller count:', DefaultScene.XR.input.controllers.length);
DefaultScene.XR.input.controllers.forEach((controller, index) => { DefaultScene.XR.input.controllers.forEach((controller, index) => {
debugLog(` Late controller ${index} - handedness: ${controller.inputSource.handedness}`); log.debug(` Late controller ${index} - handedness: ${controller.inputSource.handedness}`);
}); });
}, 2000); }, 2000);
// Note: Mission brief will call startGameplay() when start button is clicked // Note: Mission brief will call startGameplay() when start button is clicked
debugLog('XR mode: Mission brief will control game start'); log.debug('XR mode: Mission brief will control game start');
} else if (DefaultScene.XR) { } else if (DefaultScene.XR) {
// XR available but not entered yet, try to enter // XR available but not entered yet, try to enter
try { try {
const _xr = await DefaultScene.XR.baseExperience.enterXRAsync('immersive-vr', 'local-floor'); const _xr = await DefaultScene.XR.baseExperience.enterXRAsync('immersive-vr', 'local-floor');
debugLog('Entered XR mode from play()'); log.debug('Entered XR mode from play()');
// Check for controllers // Check for controllers
DefaultScene.XR.input.controllers.forEach((controller, index) => { DefaultScene.XR.input.controllers.forEach((controller, index) => {
debugLog(`Controller ${index} - handedness: ${controller.inputSource.handedness}`); log.debug(`Controller ${index} - handedness: ${controller.inputSource.handedness}`);
this._ship.addController(controller); this._ship.addController(controller);
}); });
// Mission brief will show and handle starting gameplay // Mission brief will show and handle starting gameplay
debugLog('XR mode entered: Mission brief will control game start'); log.debug('XR mode entered: Mission brief will control game start');
} catch (error) { } catch (error) {
debugLog('Failed to enter XR from play(), falling back to flat mode:', error); log.debug('Failed to enter XR from play(), falling back to flat mode:', error);
// Start flat mode immediately // Start flat mode immediately
this.startGameplay(); this.startGameplay();
} }
} else { } else {
// Flat camera mode - start game timer and physics recording immediately // Flat camera mode - start game timer and physics recording immediately
debugLog('Playing in flat camera mode (no XR)'); log.debug('Playing in flat camera mode (no XR)');
this.startGameplay(); this.startGameplay();
} }
} }
@ -341,9 +341,9 @@ export class Level1 implements Level {
} }
public async initialize() { public async initialize() {
debugLog('Initializing level from config:', this._levelConfig.difficulty); log.debug('Initializing level from config:', this._levelConfig.difficulty);
if (this._initialized) { if (this._initialized) {
console.error('Initialize called twice'); log.error('Initialize called twice');
return; return;
} }
await this._ship.initialize(); await this._ship.initialize();
@ -380,7 +380,7 @@ export class Level1 implements Level {
// Initialize scoreboard with total asteroid count // Initialize scoreboard with total asteroid count
this._ship.scoreboard.setRemainingCount(entities.asteroids.length); this._ship.scoreboard.setRemainingCount(entities.asteroids.length);
debugLog(`Initialized scoreboard with ${entities.asteroids.length} asteroids`); log.debug(`Initialized scoreboard with ${entities.asteroids.length} asteroids`);
// Create background starfield // Create background starfield
setLoadingMessage("Creating starfield..."); setLoadingMessage("Creating starfield...");
@ -407,7 +407,7 @@ export class Level1 implements Level {
if (!this._isReplayMode) { if (!this._isReplayMode) {
setLoadingMessage("Initializing physics recorder..."); setLoadingMessage("Initializing physics recorder...");
//this._physicsRecorder = new PhysicsRecorder(DefaultScene.MainScene, this._levelConfig); //this._physicsRecorder = new PhysicsRecorder(DefaultScene.MainScene, this._levelConfig);
debugLog('Physics recorder initialized (will start on XR pose)'); log.debug('Physics recorder initialized (will start on XR pose)');
} }
// Load background music before marking as ready // Load background music before marking as ready
@ -417,39 +417,39 @@ export class Level1 implements Level {
loop: true, loop: true,
volume: 0.5 volume: 0.5
}); });
debugLog('Background music loaded successfully'); log.debug('Background music loaded successfully');
} }
// Initialize mission brief (will be shown when entering XR) // Initialize mission brief (will be shown when entering XR)
setLoadingMessage("Initializing mission brief..."); setLoadingMessage("Initializing mission brief...");
console.log('[Level1] ========== ABOUT TO INITIALIZE MISSION BRIEF =========='); log.info('[Level1] ========== ABOUT TO INITIALIZE MISSION BRIEF ==========');
console.log('[Level1] _missionBrief object:', this._missionBrief); log.info('[Level1] _missionBrief object:', this._missionBrief);
console.log('[Level1] Ship exists:', !!this._ship); log.info('[Level1] Ship exists:', !!this._ship);
console.log('[Level1] Ship ID in scene:', DefaultScene.MainScene.getNodeById('Ship') !== null); log.info('[Level1] Ship ID in scene:', DefaultScene.MainScene.getNodeById('Ship') !== null);
this._missionBrief.initialize(); this._missionBrief.initialize();
console.log('[Level1] ========== MISSION BRIEF INITIALIZATION COMPLETE =========='); log.info('[Level1] ========== MISSION BRIEF INITIALIZATION COMPLETE ==========');
debugLog('Mission brief initialized'); log.debug('Mission brief initialized');
this._initialized = true; this._initialized = true;
// Set par time and level info for score calculation and results recording // Set par time and level info for score calculation and results recording
const parTime = this.getParTimeForDifficulty(this._levelConfig.difficulty); const parTime = this.getParTimeForDifficulty(this._levelConfig.difficulty);
const statusScreen = this._ship.statusScreen; const statusScreen = this._ship.statusScreen;
console.log('[Level1] StatusScreen reference:', statusScreen); log.info('[Level1] StatusScreen reference:', statusScreen);
console.log('[Level1] Level config metadata:', this._levelConfig.metadata); log.info('[Level1] Level config metadata:', this._levelConfig.metadata);
console.log('[Level1] Asteroids count:', entities.asteroids.length); log.info('[Level1] Asteroids count:', entities.asteroids.length);
if (statusScreen) { if (statusScreen) {
statusScreen.setParTime(parTime); statusScreen.setParTime(parTime);
console.log(`[Level1] Set par time to ${parTime}s for difficulty: ${this._levelConfig.difficulty}`); log.info(`[Level1] Set par time to ${parTime}s for difficulty: ${this._levelConfig.difficulty}`);
// Set level info for game results recording // Set level info for game results recording
const levelId = this._levelId || 'unknown'; const levelId = this._levelId || 'unknown';
const levelName = this._levelConfig.metadata?.description || 'Unknown Level'; const levelName = this._levelConfig.metadata?.description || 'Unknown Level';
console.log('[Level1] About to call setCurrentLevel with:', { levelId, levelName, asteroidCount: entities.asteroids.length }); log.info('[Level1] About to call setCurrentLevel with:', { levelId, levelName, asteroidCount: entities.asteroids.length });
statusScreen.setCurrentLevel(levelId, levelName, entities.asteroids.length); statusScreen.setCurrentLevel(levelId, levelName, entities.asteroids.length);
console.log('[Level1] setCurrentLevel called successfully'); log.info('[Level1] setCurrentLevel called successfully');
} else { } else {
console.error('[Level1] StatusScreen is null/undefined!'); log.error('[Level1] StatusScreen is null/undefined!');
} }
// Notify that initialization is complete // Notify that initialization is complete

View File

@ -1,4 +1,5 @@
import {LevelConfig} from "../config/levelConfig"; import {LevelConfig} from "../config/levelConfig";
import log from "../../core/logger";
const LEGACY_STORAGE_KEY = 'space-game-levels'; const LEGACY_STORAGE_KEY = 'space-game-levels';
const ARCHIVE_STORAGE_KEY = 'space-game-levels-archive'; const ARCHIVE_STORAGE_KEY = 'space-game-levels-archive';
@ -64,7 +65,7 @@ export class LegacyMigration {
} }
return status; return status;
} catch (error) { } catch (error) {
console.error('Failed to parse migration status:', error); log.error('Failed to parse migration status:', error);
return null; return null;
} }
} }
@ -127,10 +128,10 @@ export class LegacyMigration {
result.success = true; result.success = true;
console.log('Migration completed:', result); log.info('Migration completed:', result);
} catch (error) { } catch (error) {
result.error = error instanceof Error ? error.message : 'Unknown error'; result.error = error instanceof Error ? error.message : 'Unknown error';
console.error('Migration failed:', error); log.error('Migration failed:', error);
} }
return result; return result;
@ -162,7 +163,7 @@ export class LegacyMigration {
const archive = JSON.parse(stored); const archive = JSON.parse(stored);
return archive.data || null; return archive.data || null;
} catch (error) { } catch (error) {
console.error('Failed to parse archived data:', error); log.error('Failed to parse archived data:', error);
return null; return null;
} }
} }
@ -186,7 +187,7 @@ export class LegacyMigration {
return JSON.stringify(exportData, null, 2); return JSON.stringify(exportData, null, 2);
} catch (error) { } catch (error) {
console.error('Failed to export legacy data:', error); log.error('Failed to export legacy data:', error);
return null; return null;
} }
} }
@ -197,7 +198,7 @@ export class LegacyMigration {
public static downloadLegacyData(): void { public static downloadLegacyData(): void {
const jsonString = this.exportLegacyData(); const jsonString = this.exportLegacyData();
if (!jsonString) { if (!jsonString) {
console.warn('No legacy data to download'); log.warn('No legacy data to download');
return; return;
} }
@ -224,7 +225,7 @@ export class LegacyMigration {
*/ */
public static resetMigration(): void { public static resetMigration(): void {
localStorage.removeItem(MIGRATION_STATUS_KEY); localStorage.removeItem(MIGRATION_STATUS_KEY);
console.log('Migration status reset'); log.info('Migration status reset');
} }
/** /**
@ -235,7 +236,7 @@ export class LegacyMigration {
localStorage.removeItem(ARCHIVE_STORAGE_KEY); localStorage.removeItem(ARCHIVE_STORAGE_KEY);
localStorage.removeItem(CUSTOM_LEVELS_KEY); localStorage.removeItem(CUSTOM_LEVELS_KEY);
localStorage.removeItem(MIGRATION_STATUS_KEY); localStorage.removeItem(MIGRATION_STATUS_KEY);
console.log('Full migration reset completed'); log.info('Full migration reset completed');
} }
/** /**

View File

@ -1,5 +1,6 @@
import { LevelConfig } from "../config/levelConfig"; import { LevelConfig } from "../config/levelConfig";
import { CloudLevelService, CloudLevelEntry } from "../../services/cloudLevelService"; import { CloudLevelService, CloudLevelEntry } from "../../services/cloudLevelService";
import log from "../../core/logger";
/** /**
* Singleton registry for managing levels from cloud (Supabase) * Singleton registry for managing levels from cloud (Supabase)
@ -36,7 +37,7 @@ export class LevelRegistry {
} }
this.initialized = true; this.initialized = true;
console.log('[LevelRegistry] Loaded', this.levels.size, 'levels from cloud:', log.info('[LevelRegistry] Loaded', this.levels.size, 'levels from cloud:',
Array.from(this.levels.keys())); Array.from(this.levels.keys()));
} }
@ -74,6 +75,6 @@ export class LevelRegistry {
public reset(): void { public reset(): void {
this.levels.clear(); this.levels.clear();
this.initialized = false; this.initialized = false;
console.log('[LevelRegistry] Reset complete. Call initialize() to reload.'); log.info('[LevelRegistry] Reset complete. Call initialize() to reload.');
} }
} }

View File

@ -3,7 +3,7 @@ import '@babylonjs/loaders';
import { DefaultScene } from "./core/defaultScene"; import { DefaultScene } from "./core/defaultScene";
import Level from "./levels/level"; import Level from "./levels/level";
import debugLog from './core/debug'; import log from './core/logger';
import { initializeAnalytics } from './analytics/initAnalytics'; import { initializeAnalytics } from './analytics/initAnalytics';
import { createLevelSelectedHandler, LevelSelectedContext } from './core/handlers/levelSelectedHandler'; import { createLevelSelectedHandler, LevelSelectedContext } from './core/handlers/levelSelectedHandler';
@ -59,7 +59,7 @@ export class Main implements LevelSelectedContext, CleanupContext {
public async initializeEngine(): Promise<void> { public async initializeEngine(): Promise<void> {
if (this._initialized) return; if (this._initialized) return;
debugLog('[Main] Starting engine initialization'); log.debug('[Main] Starting engine initialization');
this.reportProgress(0, 'Initializing 3D engine...'); this.reportProgress(0, 'Initializing 3D engine...');
const result = await setupScene(canvas, this); const result = await setupScene(canvas, this);
this._engine = result.engine; this._engine = result.engine;

View File

@ -1,4 +1,5 @@
import { createAuth0Client, Auth0Client, User } from '@auth0/auth0-spa-js'; import { createAuth0Client, Auth0Client, User } from '@auth0/auth0-spa-js';
import log from '../core/logger';
/** /**
* Singleton service for managing Auth0 authentication * Singleton service for managing Auth0 authentication
@ -26,22 +27,22 @@ export class AuthService {
* Call this early in the application lifecycle * Call this early in the application lifecycle
*/ */
public async initialize(): Promise<void> { public async initialize(): Promise<void> {
console.log('[AuthService] ========== INITIALIZE CALLED =========='); log.info('[AuthService] ========== INITIALIZE CALLED ==========');
const domain = import.meta.env.VITE_AUTH0_DOMAIN; const domain = import.meta.env.VITE_AUTH0_DOMAIN;
const clientId = import.meta.env.VITE_AUTH0_CLIENT_ID; const clientId = import.meta.env.VITE_AUTH0_CLIENT_ID;
console.log('[AuthService] Config:', { log.info('[AuthService] Config:', {
domain, domain,
clientId: clientId ? clientId.substring(0, 10) + '...' : 'missing', clientId: clientId ? clientId.substring(0, 10) + '...' : 'missing',
redirectUri: window.location.origin redirectUri: window.location.origin
}); });
if (!domain || !clientId || domain.trim() === '') { if (!domain || !clientId || domain.trim() === '') {
console.warn('[AuthService] Auth0 not configured - authentication features will be disabled'); log.warn('[AuthService] Auth0 not configured - authentication features will be disabled');
return; return;
} }
console.log('[AuthService] Creating Auth0 client...'); log.info('[AuthService] Creating Auth0 client...');
const audience = import.meta.env.VITE_AUTH0_AUDIENCE; const audience = import.meta.env.VITE_AUTH0_AUDIENCE;
this._client = await createAuth0Client({ this._client = await createAuth0Client({
domain, domain,
@ -53,58 +54,58 @@ export class AuthService {
cacheLocation: 'localstorage', // Persist tokens across page reloads cacheLocation: 'localstorage', // Persist tokens across page reloads
useRefreshTokens: true // Enable silent token refresh useRefreshTokens: true // Enable silent token refresh
}); });
console.log('[AuthService] Auth0 client created successfully'); log.info('[AuthService] Auth0 client created successfully');
// Handle redirect callback after login // Handle redirect callback after login
const hasCallback = window.location.search.includes('code=') || const hasCallback = window.location.search.includes('code=') ||
window.location.search.includes('state='); window.location.search.includes('state=');
console.log('[AuthService] Checking for Auth0 callback:', hasCallback); log.info('[AuthService] Checking for Auth0 callback:', hasCallback);
console.log('[AuthService] Current URL:', window.location.href); log.info('[AuthService] Current URL:', window.location.href);
if (hasCallback) { if (hasCallback) {
console.log('[AuthService] ========== PROCESSING AUTH0 CALLBACK =========='); log.info('[AuthService] ========== PROCESSING AUTH0 CALLBACK ==========');
try { try {
const result = await this._client.handleRedirectCallback(); const result = await this._client.handleRedirectCallback();
console.log('[AuthService] Callback handled successfully:', result); log.info('[AuthService] Callback handled successfully:', result);
// Clean up the URL after handling callback // Clean up the URL after handling callback
window.history.replaceState({}, document.title, '/'); window.history.replaceState({}, document.title, '/');
console.log('[AuthService] URL cleaned, redirected to home'); log.info('[AuthService] URL cleaned, redirected to home');
} catch (error) { } catch (error) {
console.error('[AuthService] !!!!! CALLBACK ERROR !!!!!', error); log.error('[AuthService] !!!!! CALLBACK ERROR !!!!!', error);
console.error('[AuthService] Error details:', error?.message, error?.stack); log.error('[AuthService] Error details:', error?.message, error?.stack);
} }
} }
// Check if user is authenticated and load user info // Check if user is authenticated and load user info
console.log('[AuthService] Checking authentication status...'); log.info('[AuthService] Checking authentication status...');
const isAuth = await this._client.isAuthenticated(); const isAuth = await this._client.isAuthenticated();
console.log('[AuthService] Is authenticated:', isAuth); log.info('[AuthService] Is authenticated:', isAuth);
if (isAuth) { if (isAuth) {
console.log('[AuthService] Loading user info...'); log.info('[AuthService] Loading user info...');
this._user = await this._client.getUser() ?? null; this._user = await this._client.getUser() ?? null;
console.log('[AuthService] User loaded:', { log.info('[AuthService] User loaded:', {
name: this._user?.name, name: this._user?.name,
email: this._user?.email, email: this._user?.email,
sub: this._user?.sub sub: this._user?.sub
}); });
} else { } else {
console.log('[AuthService] User not authenticated'); log.info('[AuthService] User not authenticated');
} }
console.log('[AuthService] ========== INITIALIZATION COMPLETE =========='); log.info('[AuthService] ========== INITIALIZATION COMPLETE ==========');
} }
/** /**
* Redirect to Auth0 login page * Redirect to Auth0 login page
*/ */
public async login(): Promise<void> { public async login(): Promise<void> {
console.log('[AuthService] ========== LOGIN CALLED =========='); log.info('[AuthService] ========== LOGIN CALLED ==========');
if (!this._client) { if (!this._client) {
console.error('[AuthService] !!!!! CLIENT NOT INITIALIZED !!!!!'); log.error('[AuthService] !!!!! CLIENT NOT INITIALIZED !!!!!');
throw new Error('Auth client not initialized. Call initialize() first.'); throw new Error('Auth client not initialized. Call initialize() first.');
} }
console.log('[AuthService] Redirecting to Auth0 login...'); log.info('[AuthService] Redirecting to Auth0 login...');
await this._client.loginWithRedirect(); await this._client.loginWithRedirect();
} }
@ -112,13 +113,13 @@ export class AuthService {
* Log out the current user and redirect to home * Log out the current user and redirect to home
*/ */
public async logout(): Promise<void> { public async logout(): Promise<void> {
console.log('[AuthService] ========== LOGOUT CALLED =========='); log.info('[AuthService] ========== LOGOUT CALLED ==========');
if (!this._client) { if (!this._client) {
console.error('[AuthService] !!!!! CLIENT NOT INITIALIZED !!!!!'); log.error('[AuthService] !!!!! CLIENT NOT INITIALIZED !!!!!');
throw new Error('Auth client not initialized. Call initialize() first.'); throw new Error('Auth client not initialized. Call initialize() first.');
} }
this._user = null; this._user = null;
console.log('[AuthService] Logging out and redirecting to:', window.location.origin); log.info('[AuthService] Logging out and redirecting to:', window.location.origin);
await this._client.logout({ await this._client.logout({
logoutParams: { logoutParams: {
returnTo: window.location.origin returnTo: window.location.origin
@ -150,7 +151,7 @@ export class AuthService {
try { try {
return await this._client.getTokenSilently(); return await this._client.getTokenSilently();
} catch (error) { } catch (error) {
console.error('Error getting access token:', error); log.error('Error getting access token:', error);
return undefined; return undefined;
} }
} }

View File

@ -1,6 +1,7 @@
import { SupabaseService } from './supabaseService'; import { SupabaseService } from './supabaseService';
import { AuthService } from './authService'; import { AuthService } from './authService';
import type { GameResult } from './gameResultsService'; import type { GameResult } from './gameResultsService';
import log from '../core/logger';
/** /**
* Represents a leaderboard entry from Supabase * Represents a leaderboard entry from Supabase
@ -69,7 +70,7 @@ export class CloudLeaderboardService {
const client = await supabase.getAuthenticatedClient(); const client = await supabase.getAuthenticatedClient();
if (!client) { if (!client) {
console.warn('[CloudLeaderboardService] Not authenticated - cannot sync user'); log.warn('[CloudLeaderboardService] Not authenticated - cannot sync user');
return false; return false;
} }
@ -84,11 +85,11 @@ export class CloudLeaderboardService {
}); });
if (error) { if (error) {
console.error('[CloudLeaderboardService] Failed to sync user:', error); log.error('[CloudLeaderboardService] Failed to sync user:', error);
return false; return false;
} }
console.log('[CloudLeaderboardService] User synced:', userId); log.info('[CloudLeaderboardService] User synced:', userId);
return true; return true;
} }
@ -100,7 +101,7 @@ export class CloudLeaderboardService {
const supabase = SupabaseService.getInstance(); const supabase = SupabaseService.getInstance();
if (!supabase.isConfigured()) { if (!supabase.isConfigured()) {
console.warn('[CloudLeaderboardService] Supabase not configured'); log.warn('[CloudLeaderboardService] Supabase not configured');
return false; return false;
} }
@ -109,16 +110,16 @@ export class CloudLeaderboardService {
const user = authService.getUser(); const user = authService.getUser();
if (!user?.sub) { if (!user?.sub) {
console.warn('[CloudLeaderboardService] No user sub claim - user not logged in'); log.warn('[CloudLeaderboardService] No user sub claim - user not logged in');
return false; return false;
} }
console.log('[CloudLeaderboardService] Submitting score for user:', user.sub); log.info('[CloudLeaderboardService] Submitting score for user:', user.sub);
// Get authenticated client for insert (requires RLS) // Get authenticated client for insert (requires RLS)
const client = await supabase.getAuthenticatedClient(); const client = await supabase.getAuthenticatedClient();
if (!client) { if (!client) {
console.warn('[CloudLeaderboardService] Not authenticated - cannot submit score'); log.warn('[CloudLeaderboardService] Not authenticated - cannot submit score');
return false; return false;
} }
@ -141,7 +142,7 @@ export class CloudLeaderboardService {
star_rating: result.starRating star_rating: result.starRating
}; };
console.log('[CloudLeaderboardService] Inserting entry:', entry); log.info('[CloudLeaderboardService] Inserting entry:', entry);
const { data, error } = await client const { data, error } = await client
.from('leaderboard') .from('leaderboard')
@ -149,12 +150,12 @@ export class CloudLeaderboardService {
.select(); .select();
if (error) { if (error) {
console.error('[CloudLeaderboardService] Failed to submit score:', error); log.error('[CloudLeaderboardService] Failed to submit score:', error);
console.error('[CloudLeaderboardService] Error details:', JSON.stringify(error, null, 2)); log.error('[CloudLeaderboardService] Error details:', JSON.stringify(error, null, 2));
return false; return false;
} }
console.log('[CloudLeaderboardService] Score submitted successfully:', data); log.info('[CloudLeaderboardService] Score submitted successfully:', data);
return true; return true;
} }
@ -167,7 +168,7 @@ export class CloudLeaderboardService {
const client = supabase.getClient(); const client = supabase.getClient();
if (!client) { if (!client) {
console.warn('[CloudLeaderboardService] Supabase not configured'); log.warn('[CloudLeaderboardService] Supabase not configured');
return []; return [];
} }
@ -178,7 +179,7 @@ export class CloudLeaderboardService {
.range(offset, offset + limit - 1); .range(offset, offset + limit - 1);
if (error) { if (error) {
console.error('[CloudLeaderboardService] Failed to fetch leaderboard:', error); log.error('[CloudLeaderboardService] Failed to fetch leaderboard:', error);
return []; return [];
} }
@ -193,7 +194,7 @@ export class CloudLeaderboardService {
const client = supabase.getClient(); const client = supabase.getClient();
if (!client) { if (!client) {
console.warn('[CloudLeaderboardService] Supabase not configured'); log.warn('[CloudLeaderboardService] Supabase not configured');
return []; return [];
} }
@ -205,7 +206,7 @@ export class CloudLeaderboardService {
.limit(limit); .limit(limit);
if (error) { if (error) {
console.error('[CloudLeaderboardService] Failed to fetch user scores:', error); log.error('[CloudLeaderboardService] Failed to fetch user scores:', error);
return []; return [];
} }
@ -220,7 +221,7 @@ export class CloudLeaderboardService {
const client = supabase.getClient(); const client = supabase.getClient();
if (!client) { if (!client) {
console.warn('[CloudLeaderboardService] Supabase not configured'); log.warn('[CloudLeaderboardService] Supabase not configured');
return []; return [];
} }
@ -232,7 +233,7 @@ export class CloudLeaderboardService {
.limit(limit); .limit(limit);
if (error) { if (error) {
console.error('[CloudLeaderboardService] Failed to fetch level leaderboard:', error); log.error('[CloudLeaderboardService] Failed to fetch level leaderboard:', error);
return []; return [];
} }

View File

@ -1,6 +1,7 @@
import { SupabaseService } from './supabaseService'; import { SupabaseService } from './supabaseService';
import { AuthService } from './authService'; import { AuthService } from './authService';
import type { LevelConfig } from '../levels/config/levelConfig'; import type { LevelConfig } from '../levels/config/levelConfig';
import log from '../core/logger';
/** /**
* Level entry from the cloud database * Level entry from the cloud database
@ -66,7 +67,7 @@ function rowToEntry(row: LevelRow): CloudLevelEntry {
try { try {
config = JSON.parse(row.config); config = JSON.parse(row.config);
} catch (e) { } catch (e) {
console.error('[CloudLevelService] Failed to parse config string:', e); log.error('[CloudLevelService] Failed to parse config string:', e);
} }
} }
@ -130,24 +131,24 @@ export class CloudLevelService {
public async getOfficialLevels(): Promise<CloudLevelEntry[]> { public async getOfficialLevels(): Promise<CloudLevelEntry[]> {
const client = SupabaseService.getInstance().getClient(); const client = SupabaseService.getInstance().getClient();
if (!client) { if (!client) {
console.warn('[CloudLevelService] Supabase not configured'); log.warn('[CloudLevelService] Supabase not configured');
return []; return [];
} }
console.log('[CloudLevelService] Fetching official levels...'); log.info('[CloudLevelService] Fetching official levels...');
const { data, error } = await client const { data, error } = await client
.from('levels') .from('levels')
.select('*') .select('*')
.eq('level_type', 'official') .eq('level_type', 'official')
.order('sort_order', { ascending: true }); .order('sort_order', { ascending: true });
console.log('[CloudLevelService] Query result - data:', data?.length, 'rows, error:', error); log.info('[CloudLevelService] Query result - data:', data?.length, 'rows, error:', error);
if (data) { if (data) {
console.log('[CloudLevelService] Raw rows:', JSON.stringify(data, null, 2)); log.info('[CloudLevelService] Raw rows:', JSON.stringify(data, null, 2));
} }
if (error) { if (error) {
console.error('[CloudLevelService] Failed to fetch official levels:', error); log.error('[CloudLevelService] Failed to fetch official levels:', error);
return []; return [];
} }
@ -160,7 +161,7 @@ export class CloudLevelService {
public async getPublishedLevels(limit: number = 20, offset: number = 0): Promise<CloudLevelEntry[]> { public async getPublishedLevels(limit: number = 20, offset: number = 0): Promise<CloudLevelEntry[]> {
const client = SupabaseService.getInstance().getClient(); const client = SupabaseService.getInstance().getClient();
if (!client) { if (!client) {
console.warn('[CloudLevelService] Supabase not configured'); log.warn('[CloudLevelService] Supabase not configured');
return []; return [];
} }
@ -172,7 +173,7 @@ export class CloudLevelService {
.range(offset, offset + limit - 1); .range(offset, offset + limit - 1);
if (error) { if (error) {
console.error('[CloudLevelService] Failed to fetch published levels:', error); log.error('[CloudLevelService] Failed to fetch published levels:', error);
return []; return [];
} }
@ -186,14 +187,14 @@ export class CloudLevelService {
const supabaseService = SupabaseService.getInstance(); const supabaseService = SupabaseService.getInstance();
const client = await supabaseService.getAuthenticatedClient(); const client = await supabaseService.getAuthenticatedClient();
if (!client) { if (!client) {
console.warn('[CloudLevelService] Not authenticated'); log.warn('[CloudLevelService] Not authenticated');
return []; return [];
} }
// Get internal user ID (UUID) // Get internal user ID (UUID)
const internalUserId = await supabaseService.ensureUserExists(); const internalUserId = await supabaseService.ensureUserExists();
if (!internalUserId) { if (!internalUserId) {
console.warn('[CloudLevelService] No internal user ID available'); log.warn('[CloudLevelService] No internal user ID available');
return []; return [];
} }
@ -204,7 +205,7 @@ export class CloudLevelService {
.order('updated_at', { ascending: false }); .order('updated_at', { ascending: false });
if (error) { if (error) {
console.error('[CloudLevelService] Failed to fetch user levels:', error); log.error('[CloudLevelService] Failed to fetch user levels:', error);
return []; return [];
} }
@ -217,7 +218,7 @@ export class CloudLevelService {
public async getLevelById(id: string): Promise<CloudLevelEntry | null> { public async getLevelById(id: string): Promise<CloudLevelEntry | null> {
const client = SupabaseService.getInstance().getClient(); const client = SupabaseService.getInstance().getClient();
if (!client) { if (!client) {
console.warn('[CloudLevelService] Supabase not configured'); log.warn('[CloudLevelService] Supabase not configured');
return null; return null;
} }
@ -229,7 +230,7 @@ export class CloudLevelService {
if (error) { if (error) {
if (error.code !== 'PGRST116') { // Not found is not an error if (error.code !== 'PGRST116') { // Not found is not an error
console.error('[CloudLevelService] Failed to fetch level:', error); log.error('[CloudLevelService] Failed to fetch level:', error);
} }
return null; return null;
} }
@ -243,7 +244,7 @@ export class CloudLevelService {
public async getLevelBySlug(slug: string): Promise<CloudLevelEntry | null> { public async getLevelBySlug(slug: string): Promise<CloudLevelEntry | null> {
const client = SupabaseService.getInstance().getClient(); const client = SupabaseService.getInstance().getClient();
if (!client) { if (!client) {
console.warn('[CloudLevelService] Supabase not configured'); log.warn('[CloudLevelService] Supabase not configured');
return null; return null;
} }
@ -255,7 +256,7 @@ export class CloudLevelService {
if (error) { if (error) {
if (error.code !== 'PGRST116') { if (error.code !== 'PGRST116') {
console.error('[CloudLevelService] Failed to fetch level by slug:', error); log.error('[CloudLevelService] Failed to fetch level by slug:', error);
} }
return null; return null;
} }
@ -279,7 +280,7 @@ export class CloudLevelService {
}); });
if (error) { if (error) {
console.error('[CloudLevelService] Failed to check slug availability:', error); log.error('[CloudLevelService] Failed to check slug availability:', error);
return false; return false;
} }
@ -307,14 +308,14 @@ export class CloudLevelService {
const supabaseService = SupabaseService.getInstance(); const supabaseService = SupabaseService.getInstance();
const client = await supabaseService.getAuthenticatedClient(); const client = await supabaseService.getAuthenticatedClient();
if (!client) { if (!client) {
console.warn('[CloudLevelService] Not authenticated'); log.warn('[CloudLevelService] Not authenticated');
return null; return null;
} }
// Get internal user ID (UUID) // Get internal user ID (UUID)
const internalUserId = await supabaseService.ensureUserExists(); const internalUserId = await supabaseService.ensureUserExists();
if (!internalUserId) { if (!internalUserId) {
console.warn('[CloudLevelService] No internal user ID available'); log.warn('[CloudLevelService] No internal user ID available');
return null; return null;
} }
@ -335,7 +336,7 @@ export class CloudLevelService {
.single(); .single();
if (error) { if (error) {
console.error('[CloudLevelService] Failed to create level:', error); log.error('[CloudLevelService] Failed to create level:', error);
return null; return null;
} }
@ -359,7 +360,7 @@ export class CloudLevelService {
): Promise<CloudLevelEntry | null> { ): Promise<CloudLevelEntry | null> {
const client = await SupabaseService.getInstance().getAuthenticatedClient(); const client = await SupabaseService.getInstance().getAuthenticatedClient();
if (!client) { if (!client) {
console.warn('[CloudLevelService] Not authenticated'); log.warn('[CloudLevelService] Not authenticated');
return null; return null;
} }
@ -380,7 +381,7 @@ export class CloudLevelService {
.single(); .single();
if (error) { if (error) {
console.error('[CloudLevelService] Failed to update level:', error); log.error('[CloudLevelService] Failed to update level:', error);
return null; return null;
} }
@ -393,7 +394,7 @@ export class CloudLevelService {
public async deleteLevel(id: string): Promise<boolean> { public async deleteLevel(id: string): Promise<boolean> {
const client = await SupabaseService.getInstance().getAuthenticatedClient(); const client = await SupabaseService.getInstance().getAuthenticatedClient();
if (!client) { if (!client) {
console.warn('[CloudLevelService] Not authenticated'); log.warn('[CloudLevelService] Not authenticated');
return false; return false;
} }
@ -403,7 +404,7 @@ export class CloudLevelService {
.eq('id', id); .eq('id', id);
if (error) { if (error) {
console.error('[CloudLevelService] Failed to delete level:', error); log.error('[CloudLevelService] Failed to delete level:', error);
return false; return false;
} }
@ -420,7 +421,7 @@ export class CloudLevelService {
public async submitForReview(id: string): Promise<boolean> { public async submitForReview(id: string): Promise<boolean> {
const client = await SupabaseService.getInstance().getAuthenticatedClient(); const client = await SupabaseService.getInstance().getAuthenticatedClient();
if (!client) { if (!client) {
console.warn('[CloudLevelService] Not authenticated'); log.warn('[CloudLevelService] Not authenticated');
return false; return false;
} }
@ -429,7 +430,7 @@ export class CloudLevelService {
}); });
if (error) { if (error) {
console.error('[CloudLevelService] Failed to submit for review:', error); log.error('[CloudLevelService] Failed to submit for review:', error);
return false; return false;
} }
@ -442,7 +443,7 @@ export class CloudLevelService {
public async withdrawSubmission(id: string): Promise<boolean> { public async withdrawSubmission(id: string): Promise<boolean> {
const client = await SupabaseService.getInstance().getAuthenticatedClient(); const client = await SupabaseService.getInstance().getAuthenticatedClient();
if (!client) { if (!client) {
console.warn('[CloudLevelService] Not authenticated'); log.warn('[CloudLevelService] Not authenticated');
return false; return false;
} }
@ -453,7 +454,7 @@ export class CloudLevelService {
.eq('level_type', 'pending_review'); .eq('level_type', 'pending_review');
if (error) { if (error) {
console.error('[CloudLevelService] Failed to withdraw submission:', error); log.error('[CloudLevelService] Failed to withdraw submission:', error);
return false; return false;
} }
@ -470,7 +471,7 @@ export class CloudLevelService {
public async getPendingReviews(): Promise<CloudLevelEntry[]> { public async getPendingReviews(): Promise<CloudLevelEntry[]> {
const client = await SupabaseService.getInstance().getAuthenticatedClient(); const client = await SupabaseService.getInstance().getAuthenticatedClient();
if (!client) { if (!client) {
console.warn('[CloudLevelService] Not authenticated'); log.warn('[CloudLevelService] Not authenticated');
return []; return [];
} }
@ -481,7 +482,7 @@ export class CloudLevelService {
.order('submitted_at', { ascending: true }); .order('submitted_at', { ascending: true });
if (error) { if (error) {
console.error('[CloudLevelService] Failed to fetch pending reviews:', error); log.error('[CloudLevelService] Failed to fetch pending reviews:', error);
return []; return [];
} }
@ -494,7 +495,7 @@ export class CloudLevelService {
public async approveLevel(id: string, notes?: string): Promise<boolean> { public async approveLevel(id: string, notes?: string): Promise<boolean> {
const client = await SupabaseService.getInstance().getAuthenticatedClient(); const client = await SupabaseService.getInstance().getAuthenticatedClient();
if (!client) { if (!client) {
console.warn('[CloudLevelService] Not authenticated'); log.warn('[CloudLevelService] Not authenticated');
return false; return false;
} }
@ -504,7 +505,7 @@ export class CloudLevelService {
}); });
if (error) { if (error) {
console.error('[CloudLevelService] Failed to approve level:', error); log.error('[CloudLevelService] Failed to approve level:', error);
return false; return false;
} }
@ -517,7 +518,7 @@ export class CloudLevelService {
public async rejectLevel(id: string, notes: string): Promise<boolean> { public async rejectLevel(id: string, notes: string): Promise<boolean> {
const client = await SupabaseService.getInstance().getAuthenticatedClient(); const client = await SupabaseService.getInstance().getAuthenticatedClient();
if (!client) { if (!client) {
console.warn('[CloudLevelService] Not authenticated'); log.warn('[CloudLevelService] Not authenticated');
return false; return false;
} }
@ -527,7 +528,7 @@ export class CloudLevelService {
}); });
if (error) { if (error) {
console.error('[CloudLevelService] Failed to reject level:', error); log.error('[CloudLevelService] Failed to reject level:', error);
return false; return false;
} }
@ -550,7 +551,7 @@ export class CloudLevelService {
}); });
if (error) { if (error) {
console.error('[CloudLevelService] Failed to increment play count:', error); log.error('[CloudLevelService] Failed to increment play count:', error);
} }
} }
@ -566,7 +567,7 @@ export class CloudLevelService {
}); });
if (error) { if (error) {
console.error('[CloudLevelService] Failed to increment completion count:', error); log.error('[CloudLevelService] Failed to increment completion count:', error);
} }
} }
@ -575,21 +576,21 @@ export class CloudLevelService {
*/ */
public async rateLevel(levelId: string, rating: number): Promise<boolean> { public async rateLevel(levelId: string, rating: number): Promise<boolean> {
if (rating < 1 || rating > 5) { if (rating < 1 || rating > 5) {
console.error('[CloudLevelService] Rating must be between 1 and 5'); log.error('[CloudLevelService] Rating must be between 1 and 5');
return false; return false;
} }
const supabaseService = SupabaseService.getInstance(); const supabaseService = SupabaseService.getInstance();
const client = await supabaseService.getAuthenticatedClient(); const client = await supabaseService.getAuthenticatedClient();
if (!client) { if (!client) {
console.warn('[CloudLevelService] Not authenticated'); log.warn('[CloudLevelService] Not authenticated');
return false; return false;
} }
// Get internal user ID (UUID) // Get internal user ID (UUID)
const internalUserId = await supabaseService.ensureUserExists(); const internalUserId = await supabaseService.ensureUserExists();
if (!internalUserId) { if (!internalUserId) {
console.warn('[CloudLevelService] No internal user ID available'); log.warn('[CloudLevelService] No internal user ID available');
return false; return false;
} }
@ -604,7 +605,7 @@ export class CloudLevelService {
}); });
if (error) { if (error) {
console.error('[CloudLevelService] Failed to rate level:', error); log.error('[CloudLevelService] Failed to rate level:', error);
return false; return false;
} }

View File

@ -1,3 +1,5 @@
import log from '../core/logger';
/** /**
* Facebook Share Integration * Facebook Share Integration
* Handles sharing game results to Facebook when user is authenticated via Facebook * Handles sharing game results to Facebook when user is authenticated via Facebook
@ -37,7 +39,7 @@ export class FacebookShare {
} }
if (!this._appId) { if (!this._appId) {
console.warn('Facebook App ID not configured'); log.warn('Facebook App ID not configured');
return false; return false;
} }
@ -82,7 +84,7 @@ export class FacebookShare {
}; };
script.onerror = () => { script.onerror = () => {
console.error('Failed to load Facebook SDK'); log.error('Failed to load Facebook SDK');
resolve(false); resolve(false);
}; };
@ -103,13 +105,13 @@ export class FacebookShare {
*/ */
public async shareResults(shareData: ShareData): Promise<boolean> { public async shareResults(shareData: ShareData): Promise<boolean> {
if (!this._fbInitialized) { if (!this._fbInitialized) {
console.warn('Facebook SDK not initialized'); log.warn('Facebook SDK not initialized');
return false; return false;
} }
const FB = (window as any).FB; const FB = (window as any).FB;
if (!FB) { if (!FB) {
console.error('Facebook SDK not available'); log.error('Facebook SDK not available');
return false; return false;
} }
@ -126,10 +128,10 @@ export class FacebookShare {
hashtag: '#SpaceCombatVR' hashtag: '#SpaceCombatVR'
}, (response: any) => { }, (response: any) => {
if (response && !response.error_message) { if (response && !response.error_message) {
console.log('Successfully shared to Facebook'); log.info('Successfully shared to Facebook');
resolve(true); resolve(true);
} else { } else {
console.error('Error sharing to Facebook:', response?.error_message || 'Unknown error'); log.error('Error sharing to Facebook:', response?.error_message || 'Unknown error');
resolve(false); resolve(false);
} }
}); });
@ -142,7 +144,7 @@ export class FacebookShare {
*/ */
public async shareWithWebAPI(shareData: ShareData): Promise<boolean> { public async shareWithWebAPI(shareData: ShareData): Promise<boolean> {
if (!navigator.share) { if (!navigator.share) {
console.warn('Web Share API not supported'); log.warn('Web Share API not supported');
return false; return false;
} }
@ -158,7 +160,7 @@ export class FacebookShare {
return true; return true;
} catch (error) { } catch (error) {
// User cancelled or error occurred // User cancelled or error occurred
console.log('Share cancelled or failed:', error); log.info('Share cancelled or failed:', error);
return false; return false;
} }
} }
@ -200,7 +202,7 @@ export class FacebookShare {
await navigator.clipboard.writeText(message); await navigator.clipboard.writeText(message);
return true; return true;
} catch (error) { } catch (error) {
console.error('Failed to copy to clipboard:', error); log.error('Failed to copy to clipboard:', error);
return false; return false;
} }
} }

View File

@ -1,7 +1,7 @@
import { AuthService } from './authService'; import { AuthService } from './authService';
import { CloudLeaderboardService } from './cloudLeaderboardService'; import { CloudLeaderboardService } from './cloudLeaderboardService';
import { GameStats } from '../game/gameStats'; import { GameStats } from '../game/gameStats';
import debugLog from '../core/debug'; import log from '../core/logger';
/** /**
* Represents a completed game session result * Represents a completed game session result
@ -53,13 +53,12 @@ export class GameResultsService {
* Save a game result to storage (local + cloud) * Save a game result to storage (local + cloud)
*/ */
public saveResult(result: GameResult): void { public saveResult(result: GameResult): void {
console.log('[GameResultsService] saveResult called with:', result); log.info('[GameResultsService] saveResult called with:', result);
const results = this.getAllResults(); const results = this.getAllResults();
console.log('[GameResultsService] Existing results count:', results.length); log.info('[GameResultsService] Existing results count:', results.length);
results.push(result); results.push(result);
this.saveToStorage(results); this.saveToStorage(results);
console.log('[GameResultsService] Saved result:', result.id, result.finalScore); log.debug('[GameResultsService] Saved result:', result.id, result.finalScore);
debugLog('[GameResultsService] Saved result:', result.id, result.finalScore);
// Submit to cloud leaderboard (non-blocking) // Submit to cloud leaderboard (non-blocking)
this.submitToCloud(result); this.submitToCloud(result);
@ -74,14 +73,14 @@ export class GameResultsService {
if (cloudService.isAvailable()) { if (cloudService.isAvailable()) {
const success = await cloudService.submitScore(result); const success = await cloudService.submitScore(result);
if (success) { if (success) {
console.log('[GameResultsService] Cloud submission successful'); log.info('[GameResultsService] Cloud submission successful');
} else { } else {
console.log('[GameResultsService] Cloud submission skipped (not authenticated or failed)'); log.info('[GameResultsService] Cloud submission skipped (not authenticated or failed)');
} }
} }
} catch (error) { } catch (error) {
// Don't let cloud failures affect local save // Don't let cloud failures affect local save
console.warn('[GameResultsService] Cloud submission error:', error); log.warn('[GameResultsService] Cloud submission error:', error);
} }
} }
@ -96,7 +95,7 @@ export class GameResultsService {
} }
return JSON.parse(data) as GameResult[]; return JSON.parse(data) as GameResult[];
} catch (error) { } catch (error) {
debugLog('[GameResultsService] Error loading results:', error); log.debug('[GameResultsService] Error loading results:', error);
return []; return [];
} }
} }
@ -116,7 +115,7 @@ export class GameResultsService {
*/ */
public clearAll(): void { public clearAll(): void {
localStorage.removeItem(STORAGE_KEY); localStorage.removeItem(STORAGE_KEY);
debugLog('[GameResultsService] Cleared all results'); log.debug('[GameResultsService] Cleared all results');
} }
/** /**
@ -125,15 +124,14 @@ export class GameResultsService {
private saveToStorage(results: GameResult[]): void { private saveToStorage(results: GameResult[]): void {
try { try {
const json = JSON.stringify(results); const json = JSON.stringify(results);
console.log('[GameResultsService] Saving to localStorage, key:', STORAGE_KEY, 'size:', json.length); log.info('[GameResultsService] Saving to localStorage, key:', STORAGE_KEY, 'size:', json.length);
localStorage.setItem(STORAGE_KEY, json); localStorage.setItem(STORAGE_KEY, json);
console.log('[GameResultsService] Successfully saved to localStorage'); log.info('[GameResultsService] Successfully saved to localStorage');
// Verify it was saved // Verify it was saved
const verify = localStorage.getItem(STORAGE_KEY); const verify = localStorage.getItem(STORAGE_KEY);
console.log('[GameResultsService] Verification - stored data exists:', !!verify); log.info('[GameResultsService] Verification - stored data exists:', !!verify);
} catch (error) { } catch (error) {
console.error('[GameResultsService] Error saving results:', error); log.error('[GameResultsService] Error saving results:', error);
debugLog('[GameResultsService] Error saving results:', error);
} }
} }

View File

@ -1,5 +1,6 @@
import { createClient, SupabaseClient } from '@supabase/supabase-js'; import { createClient, SupabaseClient } from '@supabase/supabase-js';
import { AuthService } from './authService'; import { AuthService } from './authService';
import log from '../core/logger';
const SUPABASE_URL = import.meta.env.VITE_SUPABASE_PROJECT; const SUPABASE_URL = import.meta.env.VITE_SUPABASE_PROJECT;
const SUPABASE_ANON_KEY = import.meta.env.VITE_SUPABASE_KEY; const SUPABASE_ANON_KEY = import.meta.env.VITE_SUPABASE_KEY;
@ -17,7 +18,7 @@ export class SupabaseService {
if (SUPABASE_URL && SUPABASE_ANON_KEY) { if (SUPABASE_URL && SUPABASE_ANON_KEY) {
this._client = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); this._client = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
} else { } else {
console.warn('[SupabaseService] Supabase not configured - cloud features disabled'); log.warn('[SupabaseService] Supabase not configured - cloud features disabled');
} }
} }
@ -51,7 +52,7 @@ export class SupabaseService {
*/ */
public async getAuthenticatedClient(): Promise<SupabaseClient | null> { public async getAuthenticatedClient(): Promise<SupabaseClient | null> {
if (!SUPABASE_URL || !SUPABASE_ANON_KEY) { if (!SUPABASE_URL || !SUPABASE_ANON_KEY) {
console.warn('[SupabaseService] Missing Supabase URL or key'); log.warn('[SupabaseService] Missing Supabase URL or key');
return null; return null;
} }
@ -59,16 +60,16 @@ export class SupabaseService {
const token = await authService.getAccessToken(); const token = await authService.getAccessToken();
if (!token) { if (!token) {
console.warn('[SupabaseService] No auth token available'); log.warn('[SupabaseService] No auth token available');
return null; return null;
} }
console.log('[SupabaseService] Got Auth0 token, length:', token.length); log.info('[SupabaseService] Got Auth0 token, length:', token.length);
// Debug: decode JWT to see claims (without verification) // Debug: decode JWT to see claims (without verification)
try { try {
const payload = JSON.parse(atob(token.split('.')[1])); const payload = JSON.parse(atob(token.split('.')[1]));
console.log('[SupabaseService] Token claims:', { log.info('[SupabaseService] Token claims:', {
iss: payload.iss, iss: payload.iss,
sub: payload.sub, sub: payload.sub,
aud: payload.aud, aud: payload.aud,
@ -76,7 +77,7 @@ export class SupabaseService {
role: payload.role role: payload.role
}); });
} catch (_e) { } catch (_e) {
console.warn('[SupabaseService] Could not decode token'); log.warn('[SupabaseService] Could not decode token');
} }
// Create a new client with the Auth0 token for RLS // Create a new client with the Auth0 token for RLS
@ -105,7 +106,7 @@ export class SupabaseService {
const authService = AuthService.getInstance(); const authService = AuthService.getInstance();
const user = authService.getUser(); const user = authService.getUser();
if (!user?.sub) { if (!user?.sub) {
console.warn('[SupabaseService] No user sub available'); log.warn('[SupabaseService] No user sub available');
return null; return null;
} }
@ -132,7 +133,7 @@ export class SupabaseService {
.single(); .single();
if (insertError) { if (insertError) {
console.error('[SupabaseService] Failed to create user:', insertError); log.error('[SupabaseService] Failed to create user:', insertError);
return null; return null;
} }
@ -140,7 +141,7 @@ export class SupabaseService {
} }
if (fetchError) { if (fetchError) {
console.error('[SupabaseService] Failed to fetch user:', fetchError); log.error('[SupabaseService] Failed to fetch user:', fetchError);
} }
return null; return null;

View File

@ -5,7 +5,7 @@ import {
WebXRControllerComponent, WebXRControllerComponent,
WebXRInputSource, WebXRInputSource,
} from "@babylonjs/core"; } from "@babylonjs/core";
import debugLog from "../../core/debug"; import log from "../../core/logger";
import { ControllerMappingConfig, StickAction } from "./controllerMapping"; import { ControllerMappingConfig, StickAction } from "./controllerMapping";
const controllerComponents = [ const controllerComponents = [
@ -159,17 +159,17 @@ export class ControllerInput {
* Add a VR controller to the input system * Add a VR controller to the input system
*/ */
public addController(controller: WebXRInputSource): void { public addController(controller: WebXRInputSource): void {
debugLog( log.debug(
"ControllerInput.addController called for:", "ControllerInput.addController called for:",
controller.inputSource.handedness controller.inputSource.handedness
); );
if (controller.inputSource.handedness === "left") { if (controller.inputSource.handedness === "left") {
debugLog("Adding left controller"); log.debug("Adding left controller");
this._leftInputSource = controller; this._leftInputSource = controller;
this._leftInputSource.onMotionControllerInitObservable.add( this._leftInputSource.onMotionControllerInitObservable.add(
(motionController) => { (motionController) => {
debugLog( log.debug(
"Left motion controller initialized:", "Left motion controller initialized:",
motionController.handness motionController.handness
); );
@ -179,17 +179,17 @@ export class ControllerInput {
// Check if motion controller is already initialized // Check if motion controller is already initialized
if (controller.motionController) { if (controller.motionController) {
debugLog("Left motion controller already initialized, mapping now"); log.debug("Left motion controller already initialized, mapping now");
this.mapMotionController(controller.motionController); this.mapMotionController(controller.motionController);
} }
} }
if (controller.inputSource.handedness === "right") { if (controller.inputSource.handedness === "right") {
debugLog("Adding right controller"); log.debug("Adding right controller");
this._rightInputSource = controller; this._rightInputSource = controller;
this._rightInputSource.onMotionControllerInitObservable.add( this._rightInputSource.onMotionControllerInitObservable.add(
(motionController) => { (motionController) => {
debugLog( log.debug(
"Right motion controller initialized:", "Right motion controller initialized:",
motionController.handness motionController.handness
); );
@ -199,7 +199,7 @@ export class ControllerInput {
// Check if motion controller is already initialized // Check if motion controller is already initialized
if (controller.motionController) { if (controller.motionController) {
debugLog("Right motion controller already initialized, mapping now"); log.debug("Right motion controller already initialized, mapping now");
this.mapMotionController(controller.motionController); this.mapMotionController(controller.motionController);
} }
} }
@ -211,7 +211,7 @@ export class ControllerInput {
private mapMotionController( private mapMotionController(
controller: WebXRAbstractMotionController controller: WebXRAbstractMotionController
): void { ): void {
debugLog( log.debug(
"Mapping motion controller:", "Mapping motion controller:",
controller.handness, controller.handness,
"Profile:", "Profile:",
@ -222,13 +222,13 @@ export class ControllerInput {
const comp = controller.components[component]; const comp = controller.components[component];
if (!comp) { if (!comp) {
debugLog( log.debug(
` Component ${component} not found on ${controller.handness} controller` ` Component ${component} not found on ${controller.handness} controller`
); );
return; return;
} }
debugLog( log.debug(
` Found component ${component} on ${controller.handness} controller` ` Found component ${component} on ${controller.handness} controller`
); );
const observable = this._controllerObservable; const observable = this._controllerObservable;
@ -329,7 +329,7 @@ export class ControllerInput {
this._onStatusScreenToggleObservable.notifyObservers(); this._onStatusScreenToggleObservable.notifyObservers();
} }
} }
console.log(controllerEvent); log.info(controllerEvent);
} }
} }
} }

View File

@ -1,4 +1,4 @@
import debugLog from '../../core/debug'; import log from '../../core/logger';
const STORAGE_KEY = 'space-game-controller-mapping'; const STORAGE_KEY = 'space-game-controller-mapping';
@ -107,7 +107,7 @@ export class ControllerMappingConfig {
*/ */
public setMapping(mapping: ControllerMapping): void { public setMapping(mapping: ControllerMapping): void {
this._mapping = { ...mapping }; this._mapping = { ...mapping };
debugLog('[ControllerMapping] Configuration updated:', this._mapping); log.debug('[ControllerMapping] Configuration updated:', this._mapping);
} }
/** /**
@ -115,7 +115,7 @@ export class ControllerMappingConfig {
*/ */
public resetToDefault(): void { public resetToDefault(): void {
this._mapping = { ...ControllerMappingConfig.DEFAULT_MAPPING }; this._mapping = { ...ControllerMappingConfig.DEFAULT_MAPPING };
debugLog('[ControllerMapping] Reset to default configuration'); log.debug('[ControllerMapping] Reset to default configuration');
} }
/** /**
@ -125,9 +125,9 @@ export class ControllerMappingConfig {
try { try {
const json = JSON.stringify(this._mapping); const json = JSON.stringify(this._mapping);
localStorage.setItem(STORAGE_KEY, json); localStorage.setItem(STORAGE_KEY, json);
debugLog('[ControllerMapping] Saved to localStorage'); log.debug('[ControllerMapping] Saved to localStorage');
} catch (error) { } catch (error) {
console.error('[ControllerMapping] Failed to save to localStorage:', error); log.error('[ControllerMapping] Failed to save to localStorage:', error);
} }
} }
@ -146,12 +146,12 @@ export class ControllerMappingConfig {
...parsed, ...parsed,
}; };
debugLog('[ControllerMapping] Loaded from localStorage:', this._mapping); log.debug('[ControllerMapping] Loaded from localStorage:', this._mapping);
} else { } else {
debugLog('[ControllerMapping] No saved configuration, using defaults'); log.debug('[ControllerMapping] No saved configuration, using defaults');
} }
} catch (error) { } catch (error) {
console.warn('[ControllerMapping] Failed to load from localStorage, using defaults:', error); log.warn('[ControllerMapping] Failed to load from localStorage, using defaults:', error);
this._mapping = { ...ControllerMappingConfig.DEFAULT_MAPPING }; this._mapping = { ...ControllerMappingConfig.DEFAULT_MAPPING };
} }
} }

View File

@ -1,7 +1,7 @@
import { Observable } from "@babylonjs/core"; import { Observable } from "@babylonjs/core";
import { KeyboardInput } from "./keyboardInput"; import { KeyboardInput } from "./keyboardInput";
import { ControllerInput } from "./controllerInput"; import { ControllerInput } from "./controllerInput";
import debugLog from "../../core/debug"; import log from "../../core/logger";
/** /**
* State change event emitted when ship controls or pointer selection state changes * State change event emitted when ship controls or pointer selection state changes
@ -42,7 +42,7 @@ export class InputControlManager {
* Private constructor for singleton pattern * Private constructor for singleton pattern
*/ */
private constructor() { private constructor() {
debugLog('[InputControlManager] Instance created'); log.debug('[InputControlManager] Instance created');
} }
/** /**
@ -59,7 +59,7 @@ export class InputControlManager {
* Register input systems (called by Ship during initialization) * Register input systems (called by Ship during initialization)
*/ */
public registerInputSystems(keyboard: KeyboardInput | null, controller: ControllerInput | null): void { public registerInputSystems(keyboard: KeyboardInput | null, controller: ControllerInput | null): void {
debugLog('[InputControlManager] Registering input systems', { keyboard: !!keyboard, controller: !!controller }); log.debug('[InputControlManager] Registering input systems', { keyboard: !!keyboard, controller: !!controller });
this._keyboardInput = keyboard; this._keyboardInput = keyboard;
this._controllerInput = controller; this._controllerInput = controller;
} }
@ -68,7 +68,7 @@ export class InputControlManager {
* Register XR pointer feature (called by main.ts during XR setup) * Register XR pointer feature (called by main.ts during XR setup)
*/ */
public registerPointerFeature(pointerFeature: any): void { public registerPointerFeature(pointerFeature: any): void {
debugLog('[InputControlManager] Registering XR pointer feature'); log.debug('[InputControlManager] Registering XR pointer feature');
this._xrPointerFeature = pointerFeature; this._xrPointerFeature = pointerFeature;
// Apply current state to the newly registered pointer feature // Apply current state to the newly registered pointer feature
@ -79,7 +79,7 @@ export class InputControlManager {
* Enable ship controls, disable pointer selection * Enable ship controls, disable pointer selection
*/ */
public enableShipControls(requester: string): void { public enableShipControls(requester: string): void {
debugLog(`[InputControlManager] Enabling ship controls (requester: ${requester})`); log.debug(`[InputControlManager] Enabling ship controls (requester: ${requester})`);
// Update state // Update state
this._shipControlsEnabled = true; this._shipControlsEnabled = true;
@ -104,7 +104,7 @@ export class InputControlManager {
* Disable ship controls, enable pointer selection * Disable ship controls, enable pointer selection
*/ */
public disableShipControls(requester: string): void { public disableShipControls(requester: string): void {
debugLog(`[InputControlManager] Disabling ship controls (requester: ${requester})`); log.debug(`[InputControlManager] Disabling ship controls (requester: ${requester})`);
// Update state // Update state
this._shipControlsEnabled = false; this._shipControlsEnabled = false;
@ -119,12 +119,12 @@ export class InputControlManager {
} }
// Enable pointer selection // Enable pointer selection
console.log(`[InputControlManager] About to update pointer feature...`); log.info(`[InputControlManager] About to update pointer feature...`);
this.updatePointerFeature(); this.updatePointerFeature();
// Emit state change event // Emit state change event
this.emitStateChange(requester); this.emitStateChange(requester);
console.log(`[InputControlManager] ===== Ship controls disabled =====`); log.info(`[InputControlManager] ===== Ship controls disabled =====`);
} }
/** /**
@ -139,14 +139,14 @@ export class InputControlManager {
if (this._pointerSelectionEnabled) { if (this._pointerSelectionEnabled) {
// Enable pointer selection (attach feature) // Enable pointer selection (attach feature)
this._xrPointerFeature.attach(); this._xrPointerFeature.attach();
debugLog('[InputControlManager] Pointer selection enabled'); log.debug('[InputControlManager] Pointer selection enabled');
} else { } else {
// Disable pointer selection (detach feature) // Disable pointer selection (detach feature)
this._xrPointerFeature.detach(); this._xrPointerFeature.detach();
debugLog('[InputControlManager] Pointer selection disabled'); log.debug('[InputControlManager] Pointer selection disabled');
} }
} catch (error) { } catch (error) {
console.warn('[InputControlManager] Failed to update pointer feature:', error); log.warn('[InputControlManager] Failed to update pointer feature:', error);
} }
} }
@ -163,7 +163,7 @@ export class InputControlManager {
this._onStateChangedObservable.notifyObservers(stateChange); this._onStateChangedObservable.notifyObservers(stateChange);
debugLog('[InputControlManager] State changed:', stateChange); log.debug('[InputControlManager] State changed:', stateChange);
} }
/** /**
@ -191,7 +191,7 @@ export class InputControlManager {
* Cleanup (for testing or hot reload) * Cleanup (for testing or hot reload)
*/ */
public dispose(): void { public dispose(): void {
debugLog('[InputControlManager] Disposing'); log.debug('[InputControlManager] Disposing');
this._onStateChangedObservable.clear(); this._onStateChangedObservable.clear();
this._keyboardInput = null; this._keyboardInput = null;
this._controllerInput = null; this._controllerInput = null;

View File

@ -17,7 +17,7 @@ import type { AudioEngineV2 } from "@babylonjs/core";
import { DefaultScene } from "../core/defaultScene"; import { DefaultScene } from "../core/defaultScene";
import { GameConfig } from "../core/gameConfig"; import { GameConfig } from "../core/gameConfig";
import { Sight } from "./sight"; import { Sight } from "./sight";
import debugLog from "../core/debug"; import log from "../core/logger";
import { Scoreboard } from "../ui/hud/scoreboard"; import { Scoreboard } from "../ui/hud/scoreboard";
import loadAsset from "../utils/loadAsset"; import loadAsset from "../utils/loadAsset";
import { KeyboardInput } from "./input/keyboardInput"; import { KeyboardInput } from "./input/keyboardInput";
@ -107,7 +107,7 @@ export class Ship {
*/ */
public startGameplay(): void { public startGameplay(): void {
this._gameplayStarted = true; this._gameplayStarted = true;
debugLog('[Ship] Gameplay started - game end conditions now active'); log.debug('[Ship] Gameplay started - game end conditions now active');
} }
public get onMissionBriefTriggerObservable(): Observable<void> { public get onMissionBriefTriggerObservable(): Observable<void> {
@ -164,7 +164,7 @@ export class Ship {
// Create physics if enabled // Create physics if enabled
const config = GameConfig.getInstance(); const config = GameConfig.getInstance();
if (config.physicsEnabled) { if (config.physicsEnabled) {
console.log("Physics Enabled for Ship"); log.info("Physics Enabled for Ship");
if (this._ship) { if (this._ship) {
const agg = new PhysicsAggregate( const agg = new PhysicsAggregate(
this._ship, this._ship,
@ -184,9 +184,9 @@ export class Ship {
// Debug: Log center of mass before override // Debug: Log center of mass before override
const massProps = agg.body.getMassProperties(); const massProps = agg.body.getMassProperties();
console.log(`[Ship] Original center of mass (local): ${massProps.centerOfMass.toString()}`); log.info(`[Ship] Original center of mass (local): ${massProps.centerOfMass.toString()}`);
console.log(`[Ship] Mass: ${massProps.mass}`); log.info(`[Ship] Mass: ${massProps.mass}`);
console.log(`[Ship] Inertia: ${massProps.inertia.toString()}`); log.info(`[Ship] Inertia: ${massProps.inertia.toString()}`);
// Override center of mass to origin to prevent thrust from causing torque // Override center of mass to origin to prevent thrust from causing torque
// (mesh-based physics was calculating offset center of mass from geometry) // (mesh-based physics was calculating offset center of mass from geometry)
@ -197,7 +197,7 @@ export class Ship {
inertiaOrientation: massProps.inertiaOrientation inertiaOrientation: massProps.inertiaOrientation
}); });
console.log(`[Ship] Center of mass overridden to: ${agg.body.getMassProperties().centerOfMass.toString()}`); log.info(`[Ship] Center of mass overridden to: ${agg.body.getMassProperties().centerOfMass.toString()}`);
// Configure physics sleep behavior from config // Configure physics sleep behavior from config
// (disabling sleep prevents abrupt stops at zero linear velocity) // (disabling sleep prevents abrupt stops at zero linear velocity)
@ -243,7 +243,7 @@ export class Ship {
// Apply damage if above minimum threshold // Apply damage if above minimum threshold
if (this._scoreboard?.shipStatus && damage > 0.001) { if (this._scoreboard?.shipStatus && damage > 0.001) {
this._scoreboard.shipStatus.damageHull(damage); this._scoreboard.shipStatus.damageHull(damage);
debugLog(`Collision damage: ${damage.toFixed(4)} (energy: ${kineticEnergy.toFixed(1)}, speed: ${relativeSpeed.toFixed(1)} m/s)`); log.debug(`Collision damage: ${damage.toFixed(4)} (energy: ${kineticEnergy.toFixed(1)}, speed: ${relativeSpeed.toFixed(1)} m/s)`);
// Play collision sound // Play collision sound
if (this._audio) { if (this._audio) {
@ -253,7 +253,7 @@ export class Ship {
} }
}); });
} else { } else {
console.warn("No geometry mesh found, cannot create physics"); log.warn("No geometry mesh found, cannot create physics");
} }
} }
@ -361,7 +361,7 @@ export class Ship {
if (!DefaultScene.XR && !this._isReplayMode) { if (!DefaultScene.XR && !this._isReplayMode) {
DefaultScene.MainScene.activeCamera = this._camera; DefaultScene.MainScene.activeCamera = this._camera;
//this._camera.attachControl(DefaultScene.MainScene.getEngine().getRenderingCanvas(), true); //this._camera.attachControl(DefaultScene.MainScene.getEngine().getRenderingCanvas(), true);
debugLog('Flat camera set as active camera'); log.debug('Flat camera set as active camera');
} }
// Create sight reticle // Create sight reticle
@ -394,7 +394,7 @@ export class Ship {
}, { sampleRate: 0.2 }); // Sample 20% of asteroid events to reduce data }, { sampleRate: 0.2 }); // Sample 20% of asteroid events to reduce data
} catch (error) { } catch (error) {
// Analytics not initialized or failed - don't break gameplay // Analytics not initialized or failed - don't break gameplay
debugLog('Analytics tracking failed:', error); log.debug('Analytics tracking failed:', error);
} }
}); });
@ -415,7 +415,7 @@ export class Ship {
source: 'asteroid_collision' // Default assumption source: 'asteroid_collision' // Default assumption
}); });
} catch (error) { } catch (error) {
debugLog('Analytics tracking failed:', error); log.debug('Analytics tracking failed:', error);
} }
} }
}); });
@ -436,7 +436,7 @@ export class Ship {
* Handle replay button click from status screen * Handle replay button click from status screen
*/ */
private handleReplayRequest(): void { private handleReplayRequest(): void {
debugLog('Replay button clicked - notifying observers'); log.debug('Replay button clicked - notifying observers');
this.onReplayRequestObservable.notifyObservers(); this.onReplayRequestObservable.notifyObservers();
} }
@ -444,7 +444,7 @@ export class Ship {
* Handle exit VR button click from status screen * Handle exit VR button click from status screen
*/ */
private async handleExitVR(): Promise<void> { private async handleExitVR(): Promise<void> {
debugLog('Exit VR button clicked - navigating to home'); log.debug('Exit VR button clicked - navigating to home');
try { try {
// Ensure the app UI is visible before navigating (safety net) // Ensure the app UI is visible before navigating (safety net)
@ -463,7 +463,7 @@ export class Ship {
const { navigate } = await import('svelte-routing'); const { navigate } = await import('svelte-routing');
navigate('/', { replace: true }); navigate('/', { replace: true });
} catch (error) { } catch (error) {
console.error('Failed to navigate, falling back to reload:', error); log.error('Failed to navigate, falling back to reload:', error);
window.location.reload(); window.location.reload();
} }
} }
@ -472,7 +472,7 @@ export class Ship {
* Handle resume button click from status screen * Handle resume button click from status screen
*/ */
private handleResume(): void { private handleResume(): void {
debugLog('Resume button clicked - hiding status screen'); log.debug('Resume button clicked - hiding status screen');
// InputControlManager will handle re-enabling controls when status screen hides // InputControlManager will handle re-enabling controls when status screen hides
this._statusScreen.hide(); this._statusScreen.hide();
} }
@ -481,7 +481,7 @@ export class Ship {
* Handle next level button click from status screen * Handle next level button click from status screen
*/ */
private handleNextLevel(): void { private handleNextLevel(): void {
debugLog('Next Level button clicked - navigating to level selector'); log.debug('Next Level button clicked - navigating to level selector');
// Navigate back to level selector (root route) // Navigate back to level selector (root route)
window.location.hash = '#/'; window.location.hash = '#/';
window.location.reload(); window.location.reload();
@ -521,7 +521,7 @@ export class Ship {
// Check condition 1: Death by hull damage (outside landing zone) // Check condition 1: Death by hull damage (outside landing zone)
if (!this._isInLandingZone && hull < 0.01) { if (!this._isInLandingZone && hull < 0.01) {
debugLog('Game end condition met: Hull critical outside landing zone'); log.debug('Game end condition met: Hull critical outside landing zone');
this._statusScreen.show(true, false, 'death'); // Game ended, not victory, death reason this._statusScreen.show(true, false, 'death'); // Game ended, not victory, death reason
// InputControlManager will handle disabling controls when status screen shows // InputControlManager will handle disabling controls when status screen shows
this._statusScreenAutoShown = true; this._statusScreenAutoShown = true;
@ -530,7 +530,7 @@ export class Ship {
// Check condition 2: Stranded (outside landing zone, no fuel, low velocity) // Check condition 2: Stranded (outside landing zone, no fuel, low velocity)
if (!this._isInLandingZone && fuel < 0.01 && totalVelocity < 5) { if (!this._isInLandingZone && fuel < 0.01 && totalVelocity < 5) {
debugLog('Game end condition met: Stranded (no fuel, low velocity)'); log.debug('Game end condition met: Stranded (no fuel, low velocity)');
this._statusScreen.show(true, false, 'stranded'); // Game ended, not victory, stranded reason this._statusScreen.show(true, false, 'stranded'); // Game ended, not victory, stranded reason
// InputControlManager will handle disabling controls when status screen shows // InputControlManager will handle disabling controls when status screen shows
this._statusScreenAutoShown = true; this._statusScreenAutoShown = true;
@ -540,7 +540,7 @@ export class Ship {
// Check condition 3: Victory (all asteroids destroyed, inside landing zone) // Check condition 3: Victory (all asteroids destroyed, inside landing zone)
// Must have had asteroids to destroy in the first place (prevents false victory on init) // Must have had asteroids to destroy in the first place (prevents false victory on init)
if (asteroidsRemaining <= 0 && this._isInLandingZone && this._scoreboard.hasAsteroidsToDestroy) { if (asteroidsRemaining <= 0 && this._isInLandingZone && this._scoreboard.hasAsteroidsToDestroy) {
debugLog('Game end condition met: Victory (all asteroids destroyed)'); log.debug('Game end condition met: Victory (all asteroids destroyed)');
this._statusScreen.show(true, true, 'victory'); // Game ended, VICTORY! this._statusScreen.show(true, true, 'victory'); // Game ended, VICTORY!
// InputControlManager will handle disabling controls when status screen shows // InputControlManager will handle disabling controls when status screen shows
this._statusScreenAutoShown = true; this._statusScreenAutoShown = true;
@ -633,9 +633,9 @@ export class Ship {
// Log zone transitions // Log zone transitions
if (this._isInLandingZone && !wasInZone) { if (this._isInLandingZone && !wasInZone) {
debugLog("Ship entered landing zone - resupply active"); log.debug("Ship entered landing zone - resupply active");
} else if (!this._isInLandingZone && wasInZone) { } else if (!this._isInLandingZone && wasInZone) {
debugLog("Ship exited landing zone - resupply inactive"); log.debug("Ship exited landing zone - resupply inactive");
} }
// Resupply at 0.1 per second if in zone // Resupply at 0.1 per second if in zone
@ -669,7 +669,7 @@ export class Ship {
private handleShoot(): void { private handleShoot(): void {
// If controls are disabled, fire mission brief trigger observable instead of shooting // If controls are disabled, fire mission brief trigger observable instead of shooting
if (!this._controlsEnabled) { if (!this._controlsEnabled) {
debugLog('[Ship] Controls disabled - firing mission brief trigger observable'); log.debug('[Ship] Controls disabled - firing mission brief trigger observable');
this._onMissionBriefTriggerObservable.notifyObservers(); this._onMissionBriefTriggerObservable.notifyObservers();
return; return;
} }
@ -703,7 +703,7 @@ export class Ship {
landingAggregate.body.getCollisionObservable().add((collisionEvent) => { landingAggregate.body.getCollisionObservable().add((collisionEvent) => {
// Check if the collision is with our ship // Check if the collision is with our ship
if (collisionEvent.collider === this._ship.physicsBody) { if (collisionEvent.collider === this._ship.physicsBody) {
debugLog("Physics trigger fired for landing zone"); log.debug("Physics trigger fired for landing zone");
} }
}); });
} }
@ -712,7 +712,7 @@ export class Ship {
* Add a VR controller to the input system * Add a VR controller to the input system
*/ */
public addController(controller: WebXRInputSource) { public addController(controller: WebXRInputSource) {
debugLog( log.debug(
"Ship.addController called for:", "Ship.addController called for:",
controller.inputSource.handedness controller.inputSource.handedness
); );

View File

@ -1,5 +1,5 @@
import { AudioEngineV2, StaticSound, SoundState } from "@babylonjs/core"; import { AudioEngineV2, StaticSound, SoundState } from "@babylonjs/core";
import debugLog from "../core/debug"; import log from "../core/logger";
import { ShipStatus, ShipStatusChangeEvent } from "./shipStatus"; import { ShipStatus, ShipStatusChangeEvent } from "./shipStatus";
/** /**
@ -66,7 +66,7 @@ export class VoiceAudioSystem {
public async initialize(audioEngine: AudioEngineV2): Promise<void> { public async initialize(audioEngine: AudioEngineV2): Promise<void> {
this._audioEngine = audioEngine; this._audioEngine = audioEngine;
debugLog('VoiceAudioSystem: Loading voice clips...'); log.debug('VoiceAudioSystem: Loading voice clips...');
// Load all voice files as non-spatial sounds // Load all voice files as non-spatial sounds
for (const fileName of this.VOICE_FILES) { for (const fileName of this.VOICE_FILES) {
@ -82,11 +82,11 @@ export class VoiceAudioSystem {
); );
this._sounds.set(fileName, sound); this._sounds.set(fileName, sound);
} catch (error) { } catch (error) {
debugLog(`VoiceAudioSystem: Failed to load ${fileName}.mp3`, error); log.debug(`VoiceAudioSystem: Failed to load ${fileName}.mp3`, error);
} }
} }
debugLog(`VoiceAudioSystem: Loaded ${this._sounds.size}/${this.VOICE_FILES.length} voice clips`); log.debug(`VoiceAudioSystem: Loaded ${this._sounds.size}/${this.VOICE_FILES.length} voice clips`);
} }
/** /**
@ -98,7 +98,7 @@ export class VoiceAudioSystem {
this.handleStatusChange(event); this.handleStatusChange(event);
}); });
debugLog('VoiceAudioSystem: Subscribed to game events'); log.debug('VoiceAudioSystem: Subscribed to game events');
} }
/** /**
@ -108,7 +108,7 @@ export class VoiceAudioSystem {
const { statusType, newValue, delta } = event; const { statusType, newValue, delta } = event;
const maxValue = 1; const maxValue = 1;
const percentage = maxValue > 0 ? newValue / maxValue : 0; const percentage = maxValue > 0 ? newValue / maxValue : 0;
debugLog(event); log.debug(event);
// Clear warning states if resources increase above thresholds // Clear warning states if resources increase above thresholds
if (delta > 0) { if (delta > 0) {
@ -126,7 +126,7 @@ export class VoiceAudioSystem {
if (percentage < 0.2 && !this._warningStates.has(`danger_${statusType}`)) { if (percentage < 0.2 && !this._warningStates.has(`danger_${statusType}`)) {
debugLog(`VoiceAudioSystem: DANGER warning triggered for ${statusType} (${(percentage * 100).toFixed(1)}%)`); log.debug(`VoiceAudioSystem: DANGER warning triggered for ${statusType} (${(percentage * 100).toFixed(1)}%)`);
this._warningStates.add(`danger_${statusType}`); this._warningStates.add(`danger_${statusType}`);
// Clear warning state if it exists (danger supersedes warning) // Clear warning state if it exists (danger supersedes warning)
this.clearWarningState(`warning_${statusType}`); this.clearWarningState(`warning_${statusType}`);
@ -134,13 +134,13 @@ export class VoiceAudioSystem {
} }
// Warning (10% <= x < 30%) - repeat every 4 seconds ONLY if not in danger // Warning (10% <= x < 30%) - repeat every 4 seconds ONLY if not in danger
else if (percentage >= 0.2 && percentage < 0.5 && !this._warningStates.has(`warning_${statusType}`) && !this._warningStates.has(`danger_${statusType}`)) { else if (percentage >= 0.2 && percentage < 0.5 && !this._warningStates.has(`warning_${statusType}`) && !this._warningStates.has(`danger_${statusType}`)) {
debugLog(`VoiceAudioSystem: Warning triggered for ${statusType} (${(percentage * 100).toFixed(1)}%)`); log.debug(`VoiceAudioSystem: Warning triggered for ${statusType} (${(percentage * 100).toFixed(1)}%)`);
this._warningStates.add(`warning_${statusType}`); this._warningStates.add(`warning_${statusType}`);
this.queueMessage(['warning', statusType], VoiceMessagePriority.NORMAL, false, 4000, `warning_${statusType}`); this.queueMessage(['warning', statusType], VoiceMessagePriority.NORMAL, false, 4000, `warning_${statusType}`);
} }
// Empty (= 0) - no repeat // Empty (= 0) - no repeat
else if (newValue === 0 && !this._warningStates.has(`empty_${statusType}`)) { else if (newValue === 0 && !this._warningStates.has(`empty_${statusType}`)) {
debugLog(`VoiceAudioSystem: EMPTY warning triggered for ${statusType}`); log.debug(`VoiceAudioSystem: EMPTY warning triggered for ${statusType}`);
this._warningStates.add(`empty_${statusType}`); this._warningStates.add(`empty_${statusType}`);
this.queueMessage([statusType, 'empty'], VoiceMessagePriority.HIGH, false, 0, `empty_${statusType}`); this.queueMessage([statusType, 'empty'], VoiceMessagePriority.HIGH, false, 0, `empty_${statusType}`);
} }
@ -158,7 +158,7 @@ export class VoiceAudioSystem {
stateKey?: string stateKey?: string
): void { ): void {
if (!this._audioEngine) { if (!this._audioEngine) {
debugLog('VoiceAudioSystem: Cannot queue message - audio not initialized'); log.debug('VoiceAudioSystem: Cannot queue message - audio not initialized');
return; return;
} }
@ -185,7 +185,7 @@ export class VoiceAudioSystem {
} }
const repeatInfo = repeatInterval > 0 ? ` (repeat every ${repeatInterval}ms)` : ''; const repeatInfo = repeatInterval > 0 ? ` (repeat every ${repeatInterval}ms)` : '';
debugLog(`VoiceAudioSystem: Queued message [${sounds.join(', ')}] with priority ${priority}${repeatInfo}`); log.debug(`VoiceAudioSystem: Queued message [${sounds.join(', ')}] with priority ${priority}${repeatInfo}`);
} }
/** /**
@ -222,7 +222,7 @@ export class VoiceAudioSystem {
this.playCurrentSound(); this.playCurrentSound();
} else { } else {
// Sequence complete // Sequence complete
debugLog('VoiceAudioSystem: Sequence complete'); log.debug('VoiceAudioSystem: Sequence complete');
// Check if this message should repeat // Check if this message should repeat
if (this._currentMessage.repeatInterval && this._currentMessage.repeatInterval > 0) { if (this._currentMessage.repeatInterval && this._currentMessage.repeatInterval > 0) {
@ -233,9 +233,9 @@ export class VoiceAudioSystem {
// Re-queue the message for repeat // Re-queue the message for repeat
this._queue.push({ ...this._currentMessage }); this._queue.push({ ...this._currentMessage });
debugLog(`VoiceAudioSystem: Message re-queued for repeat in ${this._currentMessage.repeatInterval}ms`); log.debug(`VoiceAudioSystem: Message re-queued for repeat in ${this._currentMessage.repeatInterval}ms`);
} else { } else {
debugLog(`VoiceAudioSystem: Message NOT re-queued - warning state '${this._currentMessage.stateKey}' cleared`); log.debug(`VoiceAudioSystem: Message NOT re-queued - warning state '${this._currentMessage.stateKey}' cleared`);
} }
} }
@ -265,7 +265,7 @@ export class VoiceAudioSystem {
this._currentMessage = this._queue.shift()!; this._currentMessage = this._queue.shift()!;
this._currentSoundIndex = 0; this._currentSoundIndex = 0;
this._isPlaying = true; this._isPlaying = true;
debugLog(`VoiceAudioSystem: Starting sequence [${this._currentMessage.sounds.join(' → ')}]`); log.debug(`VoiceAudioSystem: Starting sequence [${this._currentMessage.sounds.join(' → ')}]`);
this.playCurrentSound(); this.playCurrentSound();
} }
} }
@ -283,9 +283,9 @@ export class VoiceAudioSystem {
if (sound) { if (sound) {
sound.play(); sound.play();
debugLog(`VoiceAudioSystem: Playing ${soundName} (${this._currentSoundIndex + 1}/${this._currentMessage.sounds.length})`); log.debug(`VoiceAudioSystem: Playing ${soundName} (${this._currentSoundIndex + 1}/${this._currentMessage.sounds.length})`);
} else { } else {
debugLog(`VoiceAudioSystem: Sound ${soundName} not found, skipping`); log.debug(`VoiceAudioSystem: Sound ${soundName} not found, skipping`);
// Skip to next sound // Skip to next sound
this._currentSoundIndex++; this._currentSoundIndex++;
} }
@ -314,7 +314,7 @@ export class VoiceAudioSystem {
*/ */
public clearQueue(): void { public clearQueue(): void {
this._queue = []; this._queue = [];
debugLog('VoiceAudioSystem: Queue cleared'); log.debug('VoiceAudioSystem: Queue cleared');
} }
/** /**
@ -330,9 +330,9 @@ export class VoiceAudioSystem {
const removed = originalLength - this._queue.length; const removed = originalLength - this._queue.length;
if (removed > 0) { if (removed > 0) {
debugLog(`VoiceAudioSystem: Cleared warning state '${key}' and purged ${removed} queued message(s)`); log.debug(`VoiceAudioSystem: Cleared warning state '${key}' and purged ${removed} queued message(s)`);
} else { } else {
debugLog(`VoiceAudioSystem: Cleared warning state '${key}'`); log.debug(`VoiceAudioSystem: Cleared warning state '${key}'`);
} }
} }
} }
@ -342,7 +342,7 @@ export class VoiceAudioSystem {
*/ */
public resetWarningStates(): void { public resetWarningStates(): void {
this._warningStates.clear(); this._warningStates.clear();
debugLog('VoiceAudioSystem: Warning states reset'); log.debug('VoiceAudioSystem: Warning states reset');
} }
/** /**
@ -353,6 +353,6 @@ export class VoiceAudioSystem {
this.clearQueue(); this.clearQueue();
this._sounds.clear(); this._sounds.clear();
this._warningStates.clear(); this._warningStates.clear();
debugLog('VoiceAudioSystem: Disposed'); log.debug('VoiceAudioSystem: Disposed');
} }
} }

View File

@ -1,5 +1,6 @@
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';
import { AuthService } from '../services/authService'; import { AuthService } from '../services/authService';
import log from '../core/logger';
interface AuthState { interface AuthState {
isAuthenticated: boolean; isAuthenticated: boolean;
@ -18,34 +19,34 @@ function createAuthStore() {
const { subscribe, set, update } = writable<AuthState>(initial); const { subscribe, set, update } = writable<AuthState>(initial);
console.log('[AuthStore] Store created with initial state:', initial); log.info('[AuthStore] Store created with initial state:', initial);
// Initialize auth state - will be properly initialized after AuthService.initialize() is called // Initialize auth state - will be properly initialized after AuthService.initialize() is called
(async () => { (async () => {
console.log('[AuthStore] Checking initial auth state...'); log.info('[AuthStore] Checking initial auth state...');
const isAuth = await authService.isAuthenticated(); const isAuth = await authService.isAuthenticated();
const user = authService.getUser(); const user = authService.getUser();
console.log('[AuthStore] Initial auth check:', { isAuth, user: user?.name || user?.email || null }); log.info('[AuthStore] Initial auth check:', { isAuth, user: user?.name || user?.email || null });
set({ isAuthenticated: isAuth, user, isLoading: false }); set({ isAuthenticated: isAuth, user, isLoading: false });
})(); })();
return { return {
subscribe, subscribe,
login: async () => { login: async () => {
console.log('[AuthStore] login() called'); log.info('[AuthStore] login() called');
await authService.login(); await authService.login();
// After redirect, page will reload and auth state will be refreshed // After redirect, page will reload and auth state will be refreshed
}, },
logout: async () => { logout: async () => {
console.log('[AuthStore] logout() called'); log.info('[AuthStore] logout() called');
await authService.logout(); await authService.logout();
// After logout redirect, page will reload // After logout redirect, page will reload
}, },
refresh: async () => { refresh: async () => {
console.log('[AuthStore] refresh() called'); log.info('[AuthStore] refresh() called');
const isAuth = await authService.isAuthenticated(); const isAuth = await authService.isAuthenticated();
const user = authService.getUser(); const user = authService.getUser();
console.log('[AuthStore] Refreshed auth state:', { isAuth, user: user?.name || user?.email || null }); log.info('[AuthStore] Refreshed auth state:', { isAuth, user: user?.name || user?.email || null });
update(state => ({ ...state, isAuthenticated: isAuth, user })); update(state => ({ ...state, isAuthenticated: isAuth, user }));
}, },
}; };

View File

@ -1,6 +1,7 @@
import { writable, get } from 'svelte/store'; import { writable, get } from 'svelte/store';
import type { ControllerMapping } from '../ship/input/controllerMapping'; import type { ControllerMapping } from '../ship/input/controllerMapping';
import { ControllerMappingConfig } from '../ship/input/controllerMapping'; import { ControllerMappingConfig } from '../ship/input/controllerMapping';
import log from '../core/logger';
const _STORAGE_KEY = 'space-game-controller-mapping'; const _STORAGE_KEY = 'space-game-controller-mapping';
@ -21,13 +22,13 @@ function createControllerMappingStore() {
const mapping = get(controllerMappingStore); const mapping = get(controllerMappingStore);
config.setMapping(mapping); config.setMapping(mapping);
config.save(); config.save();
console.log('[ControllerMapping Store] Saved'); log.info('[ControllerMapping Store] Saved');
}, },
reset: () => { reset: () => {
config.resetToDefault(); config.resetToDefault();
config.save(); config.save();
set(config.getMapping()); set(config.getMapping());
console.log('[ControllerMapping Store] Reset to defaults'); log.info('[ControllerMapping Store] Reset to defaults');
}, },
validate: () => { validate: () => {
return config.validate(); return config.validate();

View File

@ -1,4 +1,5 @@
import { writable, get } from 'svelte/store'; import { writable, get } from 'svelte/store';
import log from '../core/logger';
const STORAGE_KEY = 'game-config'; const STORAGE_KEY = 'game-config';
@ -34,7 +35,7 @@ function loadFromStorage(): GameConfigData {
return { ...defaultConfig, ...parsed }; return { ...defaultConfig, ...parsed };
} }
} catch (error) { } catch (error) {
console.warn('[GameConfig Store] Failed to load from localStorage:', error); log.warn('[GameConfig Store] Failed to load from localStorage:', error);
} }
return { ...defaultConfig }; return { ...defaultConfig };
} }
@ -51,18 +52,18 @@ function createGameConfigStore() {
const config = get(gameConfigStore); const config = get(gameConfigStore);
try { try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(config)); localStorage.setItem(STORAGE_KEY, JSON.stringify(config));
console.log('[GameConfig Store] Saved to localStorage'); log.info('[GameConfig Store] Saved to localStorage');
} catch (error) { } catch (error) {
console.error('[GameConfig Store] Failed to save:', error); log.error('[GameConfig Store] Failed to save:', error);
} }
}, },
reset: () => { reset: () => {
set({ ...defaultConfig }); set({ ...defaultConfig });
try { try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(defaultConfig)); localStorage.setItem(STORAGE_KEY, JSON.stringify(defaultConfig));
console.log('[GameConfig Store] Reset to defaults'); log.info('[GameConfig Store] Reset to defaults');
} catch (error) { } catch (error) {
console.error('[GameConfig Store] Failed to save defaults:', error); log.error('[GameConfig Store] Failed to save defaults:', error);
} }
}, },
}; };

View File

@ -2,6 +2,7 @@ import { writable } from 'svelte/store';
import { LevelRegistry } from '../levels/storage/levelRegistry'; import { LevelRegistry } from '../levels/storage/levelRegistry';
import type { LevelConfig } from '../levels/config/levelConfig'; import type { LevelConfig } from '../levels/config/levelConfig';
import type { CloudLevelEntry } from '../services/cloudLevelService'; import type { CloudLevelEntry } from '../services/cloudLevelService';
import log from '../core/logger';
interface LevelRegistryState { interface LevelRegistryState {
isInitialized: boolean; isInitialized: boolean;
@ -28,7 +29,7 @@ function createLevelRegistryStore() {
levels: registry.getAllLevels(), levels: registry.getAllLevels(),
})); }));
} catch (error) { } catch (error) {
console.error('[LevelRegistryStore] Failed to initialize:', error); log.error('[LevelRegistryStore] Failed to initialize:', error);
} }
})(); })();

View File

@ -8,7 +8,7 @@ import {
} from "@babylonjs/gui"; } from "@babylonjs/gui";
import { DefaultScene } from "../../core/defaultScene"; import { DefaultScene } from "../../core/defaultScene";
import {MeshBuilder, Vector3, Observable, Observer} from "@babylonjs/core"; import {MeshBuilder, Vector3, Observable, Observer} from "@babylonjs/core";
import debugLog from '../../core/debug'; import log from '../../core/logger';
import { LevelConfig } from "../../levels/config/levelConfig"; import { LevelConfig } from "../../levels/config/levelConfig";
import { CloudLevelEntry } from "../../services/cloudLevelService"; import { CloudLevelEntry } from "../../services/cloudLevelService";
@ -27,20 +27,20 @@ export class MissionBrief {
* Initialize the mission brief as a fullscreen overlay * Initialize the mission brief as a fullscreen overlay
*/ */
public initialize(): void { public initialize(): void {
console.log('[MissionBrief] ========== INITIALIZE CALLED =========='); log.info('[MissionBrief] ========== INITIALIZE CALLED ==========');
const scene = DefaultScene.MainScene; const scene = DefaultScene.MainScene;
console.log('[MissionBrief] Scene exists:', !!scene); log.info('[MissionBrief] Scene exists:', !!scene);
try { try {
console.log('[MissionBrief] Initializing as fullscreen overlay'); log.info('[MissionBrief] Initializing as fullscreen overlay');
const mesh = MeshBuilder.CreatePlane('brief', {size: 2}); const mesh = MeshBuilder.CreatePlane('brief', {size: 2});
console.log('[MissionBrief] Mesh created:', mesh.name, 'ID:', mesh.id); log.info('[MissionBrief] Mesh created:', mesh.name, 'ID:', mesh.id);
const ship = scene.getNodeById('Ship'); const ship = scene.getNodeById('Ship');
console.log('[MissionBrief] Ship node found:', !!ship); log.info('[MissionBrief] Ship node found:', !!ship);
if (!ship) { if (!ship) {
console.error('[MissionBrief] ERROR: Ship node not found! Cannot parent mission brief mesh.'); log.error('[MissionBrief] ERROR: Ship node not found! Cannot parent mission brief mesh.');
return; return;
} }
@ -49,18 +49,18 @@ export class MissionBrief {
mesh.rotation = new Vector3(0, 0, 0); mesh.rotation = new Vector3(0, 0, 0);
mesh.renderingGroupId = 3; // Same as status screen for consistent rendering mesh.renderingGroupId = 3; // Same as status screen for consistent rendering
mesh.metadata = { uiPickable: true }; // TAG: VR UI - allow pointer selection mesh.metadata = { uiPickable: true }; // TAG: VR UI - allow pointer selection
console.log('[MissionBrief] Mesh parented to ship at position:', mesh.position); log.info('[MissionBrief] Mesh parented to ship at position:', mesh.position);
console.log('[MissionBrief] Mesh absolute position:', mesh.getAbsolutePosition()); log.info('[MissionBrief] Mesh absolute position:', mesh.getAbsolutePosition());
console.log('[MissionBrief] Mesh scaling:', mesh.scaling); log.info('[MissionBrief] Mesh scaling:', mesh.scaling);
console.log('[MissionBrief] Mesh isEnabled:', mesh.isEnabled()); log.info('[MissionBrief] Mesh isEnabled:', mesh.isEnabled());
console.log('[MissionBrief] Mesh isVisible:', mesh.isVisible); log.info('[MissionBrief] Mesh isVisible:', mesh.isVisible);
// Create fullscreen advanced texture (not attached to mesh) // Create fullscreen advanced texture (not attached to mesh)
this._advancedTexture = AdvancedDynamicTexture.CreateForMesh(mesh); this._advancedTexture = AdvancedDynamicTexture.CreateForMesh(mesh);
console.log('[MissionBrief] AdvancedDynamicTexture created for mesh'); log.info('[MissionBrief] AdvancedDynamicTexture created for mesh');
console.log('[MissionBrief] Texture dimensions:', this._advancedTexture.getSize()); log.info('[MissionBrief] Texture dimensions:', this._advancedTexture.getSize());
console.log('[MissionBrief] Fullscreen UI created'); log.info('[MissionBrief] Fullscreen UI created');
// Create main container - centered overlay // Create main container - centered overlay
this._container = new Rectangle("missionBriefContainer"); this._container = new Rectangle("missionBriefContainer");
@ -73,16 +73,16 @@ export class MissionBrief {
this._container.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER; this._container.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
this._container.verticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER; this._container.verticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
this._advancedTexture.addControl(this._container); this._advancedTexture.addControl(this._container);
console.log('[MissionBrief] Container created and added to texture'); log.info('[MissionBrief] Container created and added to texture');
// Initially hidden // Initially hidden
this._container.isVisible = false; this._container.isVisible = false;
console.log('[MissionBrief] Container initially hidden'); log.info('[MissionBrief] Container initially hidden');
console.log('[MissionBrief] ========== INITIALIZATION COMPLETE =========='); log.info('[MissionBrief] ========== INITIALIZATION COMPLETE ==========');
} catch (error) { } catch (error) {
console.error('[MissionBrief] !!!!! INITIALIZATION FAILED !!!!!', error); log.error('[MissionBrief] !!!!! INITIALIZATION FAILED !!!!!', error);
console.error('[MissionBrief] Error stack:', error?.stack); log.error('[MissionBrief] Error stack:', error?.stack);
} }
} }
@ -94,18 +94,18 @@ export class MissionBrief {
* @param onStart - Callback when start button is pressed * @param onStart - Callback when start button is pressed
*/ */
public show(levelConfig: LevelConfig, directoryEntry: CloudLevelEntry | null, triggerObservable: Observable<void>, onStart: () => void): void { public show(levelConfig: LevelConfig, directoryEntry: CloudLevelEntry | null, triggerObservable: Observable<void>, onStart: () => void): void {
console.log('[MissionBrief] ========== SHOW() CALLED =========='); log.info('[MissionBrief] ========== SHOW() CALLED ==========');
console.log('[MissionBrief] Container exists:', !!this._container); log.info('[MissionBrief] Container exists:', !!this._container);
console.log('[MissionBrief] AdvancedTexture exists:', !!this._advancedTexture); log.info('[MissionBrief] AdvancedTexture exists:', !!this._advancedTexture);
if (!this._container || !this._advancedTexture) { if (!this._container || !this._advancedTexture) {
console.error('[MissionBrief] !!!!! CANNOT SHOW - NOT INITIALIZED !!!!!'); log.error('[MissionBrief] !!!!! CANNOT SHOW - NOT INITIALIZED !!!!!');
console.error('[MissionBrief] Container:', this._container); log.error('[MissionBrief] Container:', this._container);
console.error('[MissionBrief] AdvancedTexture:', this._advancedTexture); log.error('[MissionBrief] AdvancedTexture:', this._advancedTexture);
return; return;
} }
console.log('[MissionBrief] Showing with config:', { log.info('[MissionBrief] Showing with config:', {
difficulty: levelConfig.difficulty, difficulty: levelConfig.difficulty,
description: levelConfig.metadata?.description, description: levelConfig.metadata?.description,
asteroidCount: levelConfig.asteroids?.length, asteroidCount: levelConfig.asteroids?.length,
@ -117,7 +117,7 @@ export class MissionBrief {
// Listen for trigger pulls to dismiss the mission brief // Listen for trigger pulls to dismiss the mission brief
this._triggerObserver = triggerObservable.add(() => { this._triggerObserver = triggerObservable.add(() => {
debugLog('[MissionBrief] Trigger pulled - dismissing mission brief'); log.debug('[MissionBrief] Trigger pulled - dismissing mission brief');
this.hide(); this.hide();
if (this._onStartCallback) { if (this._onStartCallback) {
this._onStartCallback(); this._onStartCallback();
@ -215,7 +215,7 @@ export class MissionBrief {
startButton.fontWeight = "bold"; startButton.fontWeight = "bold";
startButton.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER; startButton.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
startButton.onPointerClickObservable.add(() => { startButton.onPointerClickObservable.add(() => {
debugLog('[MissionBrief] START button clicked - dismissing mission brief'); log.debug('[MissionBrief] START button clicked - dismissing mission brief');
this.hide(); this.hide();
if (this._onStartCallback) { if (this._onStartCallback) {
this._onStartCallback(); this._onStartCallback();
@ -232,12 +232,12 @@ export class MissionBrief {
this._container.isVisible = true; this._container.isVisible = true;
this._isVisible = true; this._isVisible = true;
console.log('[MissionBrief] ========== CONTAINER NOW VISIBLE =========='); log.info('[MissionBrief] ========== CONTAINER NOW VISIBLE ==========');
console.log('[MissionBrief] Container.isVisible:', this._container.isVisible); log.info('[MissionBrief] Container.isVisible:', this._container.isVisible);
console.log('[MissionBrief] _isVisible flag:', this._isVisible); log.info('[MissionBrief] _isVisible flag:', this._isVisible);
console.log('[MissionBrief] Container children count:', this._container.children.length); log.info('[MissionBrief] Container children count:', this._container.children.length);
console.log('[MissionBrief] AdvancedTexture control count:', this._advancedTexture.rootContainer.children.length); log.info('[MissionBrief] AdvancedTexture control count:', this._advancedTexture.rootContainer.children.length);
console.log('[MissionBrief] ========== MISSION BRIEF DISPLAY COMPLETE =========='); log.info('[MissionBrief] ========== MISSION BRIEF DISPLAY COMPLETE ==========');
} }
/** /**
@ -247,7 +247,7 @@ export class MissionBrief {
if (this._container) { if (this._container) {
this._container.isVisible = false; this._container.isVisible = false;
this._isVisible = false; this._isVisible = false;
debugLog('[MissionBrief] Mission brief hidden'); log.debug('[MissionBrief] Mission brief hidden');
} }
} }
@ -306,6 +306,6 @@ export class MissionBrief {
this._onStartCallback = null; this._onStartCallback = null;
this._triggerObserver = null; this._triggerObserver = null;
this._isVisible = false; this._isVisible = false;
debugLog('[MissionBrief] Disposed'); log.debug('[MissionBrief] Disposed');
} }
} }

View File

@ -6,7 +6,7 @@ import {
Observable, Observable,
Vector3, Vector3,
} from "@babylonjs/core"; } from "@babylonjs/core";
import debugLog from '../../core/debug'; import log from '../../core/logger';
import { ShipStatus } from '../../ship/shipStatus'; import { ShipStatus } from '../../ship/shipStatus';
export type ScoreEvent = { export type ScoreEvent = {
@ -101,8 +101,8 @@ export class Scoreboard {
const scene = DefaultScene.MainScene; const scene = DefaultScene.MainScene;
const parent = scene.getNodeById('ship'); const parent = scene.getNodeById('ship');
debugLog('Scoreboard parent:', parent); log.debug('Scoreboard parent:', parent);
debugLog('Initializing scoreboard'); log.debug('Initializing scoreboard');
let scoreboard: Mesh | null = null; let scoreboard: Mesh | null = null;
// Retrieve and setup screen mesh from the loaded GLB // Retrieve and setup screen mesh from the loaded GLB
@ -137,7 +137,7 @@ export class Scoreboard {
// Fallback: create a plane if screen mesh not found // Fallback: create a plane if screen mesh not found
if (!scoreboard) { if (!scoreboard) {
console.error('Screen mesh not found, creating fallback plane'); log.error('Screen mesh not found, creating fallback plane');
scoreboard = MeshBuilder.CreatePlane("scoreboard", {width: 1, height: 1}, scene); scoreboard = MeshBuilder.CreatePlane("scoreboard", {width: 1, height: 1}, scene);
scoreboard.parent = parent; scoreboard.parent = parent;
@ -249,7 +249,7 @@ export class Scoreboard {
oldMaterial.dispose(true, true); oldMaterial.dispose(true, true);
} }
debugLog('Gauges texture created, material:', gaugesMesh.material?.name); log.debug('Gauges texture created, material:', gaugesMesh.material?.name);
// Create a vertical stack panel for the gauges // Create a vertical stack panel for the gauges
const panel = new StackPanel('GaugesPanel'); const panel = new StackPanel('GaugesPanel');
@ -274,7 +274,7 @@ export class Scoreboard {
this._shipStatus.setHull(1); this._shipStatus.setHull(1);
this._shipStatus.setAmmo(1); this._shipStatus.setAmmo(1);
debugLog('Gauges display created with initial test values'); log.debug('Gauges display created with initial test values');
} }
/** /**

View File

@ -22,7 +22,7 @@ import { FacebookShare, ShareData } from "../../services/facebookShare";
import { InputControlManager } from "../../ship/input/inputControlManager"; import { InputControlManager } from "../../ship/input/inputControlManager";
import { formatStars } from "../../game/scoreCalculator"; import { formatStars } from "../../game/scoreCalculator";
import { GameResultsService } from "../../services/gameResultsService"; import { GameResultsService } from "../../services/gameResultsService";
import debugLog from "../../core/debug"; import log from "../../core/logger";
/** /**
* Status screen that displays game statistics * Status screen that displays game statistics
@ -374,7 +374,7 @@ export class StatusScreen {
* Set the current level info for progression tracking and results * Set the current level info for progression tracking and results
*/ */
public setCurrentLevel(levelId: string, levelName: string, totalAsteroids: number): void { public setCurrentLevel(levelId: string, levelName: string, totalAsteroids: number): void {
console.log('[StatusScreen] setCurrentLevel called:', { levelId, levelName, totalAsteroids }); log.info('[StatusScreen] setCurrentLevel called:', { levelId, levelName, totalAsteroids });
this._currentLevelId = levelId; this._currentLevelId = levelId;
this._currentLevelName = levelName; this._currentLevelName = levelName;
this._totalAsteroids = totalAsteroids; this._totalAsteroids = totalAsteroids;
@ -445,7 +445,7 @@ export class StatusScreen {
if (this._shareButton.isVisible) { if (this._shareButton.isVisible) {
const fbShare = FacebookShare.getInstance(); const fbShare = FacebookShare.getInstance();
fbShare.initialize().catch(error => { fbShare.initialize().catch(error => {
console.error('Failed to initialize Facebook SDK:', error); log.error('Failed to initialize Facebook SDK:', error);
}); });
} }
} }
@ -564,7 +564,7 @@ export class StatusScreen {
const copied = await fbShare.copyToClipboard(shareData); const copied = await fbShare.copyToClipboard(shareData);
if (copied) { if (copied) {
// Show notification (you could add a toast notification here) // Show notification (you could add a toast notification here)
console.log('Results copied to clipboard!'); log.info('Results copied to clipboard!');
// Update button text temporarily to show feedback // Update button text temporarily to show feedback
if (this._shareButton) { if (this._shareButton) {
@ -605,8 +605,8 @@ export class StatusScreen {
* Record game result to the results service * Record game result to the results service
*/ */
private recordGameResult(endReason: 'victory' | 'death' | 'stranded'): void { private recordGameResult(endReason: 'victory' | 'death' | 'stranded'): void {
console.log('[StatusScreen] recordGameResult called with endReason:', endReason); log.info('[StatusScreen] recordGameResult called with endReason:', endReason);
console.log('[StatusScreen] Level info:', { log.info('[StatusScreen] Level info:', {
levelId: this._currentLevelId, levelId: this._currentLevelId,
levelName: this._currentLevelName, levelName: this._currentLevelName,
totalAsteroids: this._totalAsteroids, totalAsteroids: this._totalAsteroids,
@ -615,8 +615,8 @@ export class StatusScreen {
// Only record if we have level info // Only record if we have level info
if (!this._currentLevelId || !this._currentLevelName) { if (!this._currentLevelId || !this._currentLevelName) {
console.warn('[StatusScreen] Cannot record result - missing level info'); log.warn('[StatusScreen] Cannot record result - missing level info');
debugLog('[StatusScreen] Cannot record result - missing level info'); log.debug('[StatusScreen] Cannot record result - missing level info');
return; return;
} }
@ -630,15 +630,15 @@ export class StatusScreen {
this._parTime this._parTime
); );
console.log('[StatusScreen] Built result:', result); log.info('[StatusScreen] Built result:', result);
const service = GameResultsService.getInstance(); const service = GameResultsService.getInstance();
service.saveResult(result); service.saveResult(result);
console.log('[StatusScreen] Game result saved successfully'); log.info('[StatusScreen] Game result saved successfully');
debugLog('[StatusScreen] Game result recorded:', result.id, result.finalScore, result.endReason); log.debug('[StatusScreen] Game result recorded:', result.id, result.finalScore, result.endReason);
} catch (error) { } catch (error) {
console.error('[StatusScreen] Failed to record game result:', error); log.error('[StatusScreen] Failed to record game result:', error);
debugLog('[StatusScreen] Failed to record game result:', error); log.debug('[StatusScreen] Failed to record game result:', error);
} }
} }

View File

@ -1,6 +1,6 @@
import {DefaultScene} from "../core/defaultScene"; import {DefaultScene} from "../core/defaultScene";
import {AbstractMesh, AssetContainer, LoadAssetContainerAsync} from "@babylonjs/core"; import {AbstractMesh, AssetContainer, LoadAssetContainerAsync} from "@babylonjs/core";
import debugLog from "../core/debug"; import log from "../core/logger";
type LoadedAsset = { type LoadedAsset = {
container: AssetContainer, container: AssetContainer,
@ -8,23 +8,23 @@ type LoadedAsset = {
} }
export default async function loadAsset(file: string, theme: string = "default"): Promise<LoadedAsset> { export default async function loadAsset(file: string, theme: string = "default"): Promise<LoadedAsset> {
const assetPath = `/assets/themes/${theme}/models/${file}`; const assetPath = `/assets/themes/${theme}/models/${file}`;
debugLog(`[loadAsset] Loading: ${assetPath}`); log.debug(`[loadAsset] Loading: ${assetPath}`);
try { try {
const container = await LoadAssetContainerAsync(assetPath, DefaultScene.MainScene); const container = await LoadAssetContainerAsync(assetPath, DefaultScene.MainScene);
debugLog(`[loadAsset] ✓ Container loaded for ${file}`); log.debug(`[loadAsset] ✓ Container loaded for ${file}`);
const map: Map<string, AbstractMesh> = new Map(); const map: Map<string, AbstractMesh> = new Map();
container.addAllToScene(); container.addAllToScene();
debugLog(`[loadAsset] Root nodes count: ${container.rootNodes.length}`); log.debug(`[loadAsset] Root nodes count: ${container.rootNodes.length}`);
if (container.rootNodes.length === 0) { if (container.rootNodes.length === 0) {
console.error(`[loadAsset] ERROR: No root nodes found in ${file}`); log.error(`[loadAsset] ERROR: No root nodes found in ${file}`);
return {container: container, meshes: map}; return {container: container, meshes: map};
} }
for (const mesh of container.rootNodes[0].getChildMeshes(false)) { for (const mesh of container.rootNodes[0].getChildMeshes(false)) {
console.log(mesh.id, mesh); log.info(mesh.id, mesh);
// Ensure mesh is visible and enabled // Ensure mesh is visible and enabled
mesh.isVisible = true; mesh.isVisible = true;
mesh.setEnabled(true); mesh.setEnabled(true);
@ -42,10 +42,10 @@ export default async function loadAsset(file: string, theme: string = "default")
map.set(mesh.id, mesh); map.set(mesh.id, mesh);
} }
debugLog(`[loadAsset] ✓ Loaded ${map.size} meshes from ${file}`); log.debug(`[loadAsset] ✓ Loaded ${map.size} meshes from ${file}`);
return {container: container, meshes: map}; return {container: container, meshes: map};
} catch (error) { } catch (error) {
console.error(`[loadAsset] FAILED to load ${assetPath}:`, error); log.error(`[loadAsset] FAILED to load ${assetPath}:`, error);
throw error; throw error;
} }
} }