Compare commits
3 Commits
e8ac3a8f0a
...
dfa46c85a6
| Author | SHA1 | Date | |
|---|---|---|---|
| dfa46c85a6 | |||
| 54d562d210 | |||
| 123b341ed7 |
@ -107,6 +107,7 @@ body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
display: none; /* Hidden until camera is positioned in ship */
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { AnalyticsAdapter, AnalyticsEvent } from './analyticsAdapter';
|
||||
import { BrowserAgent } from '@newrelic/browser-agent/loaders/browser-agent';
|
||||
import log from '../../core/logger';
|
||||
|
||||
interface NewRelicAdapterConfig {
|
||||
/** Maximum events to batch before auto-flush */
|
||||
@ -137,7 +138,7 @@ export class NewRelicAdapter implements AnalyticsAdapter {
|
||||
this.agent.addPageAction(event.name, payload);
|
||||
this.log('Event sent:', event.name, payload);
|
||||
} 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.log(`Batched ${events.length} events:`, eventName, batchPayload);
|
||||
} 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 {
|
||||
if (this.config.debug) {
|
||||
console.log(`[NewRelicAdapter] ${message}`, ...args);
|
||||
log.info(`[NewRelicAdapter] ${message}`, ...args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { AnalyticsAdapter, AnalyticsConfig, EventMetadata, EventOptions } from './adapters/analyticsAdapter';
|
||||
import { GameEventName, GameEventProperties } from './events/gameEvents';
|
||||
import log from '../core/logger';
|
||||
|
||||
/**
|
||||
* Central analytics service with pluggable adapters
|
||||
@ -36,7 +37,7 @@ export class AnalyticsService {
|
||||
|
||||
static initialize(config?: AnalyticsConfig): AnalyticsService {
|
||||
if (AnalyticsService.instance) {
|
||||
console.warn('AnalyticsService already initialized');
|
||||
log.warn('AnalyticsService already initialized');
|
||||
return AnalyticsService.instance;
|
||||
}
|
||||
|
||||
@ -108,7 +109,7 @@ export class AnalyticsService {
|
||||
try {
|
||||
adapter.track(event);
|
||||
} 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 {
|
||||
adapter.track(event);
|
||||
} 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 {
|
||||
adapter.flush();
|
||||
} 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 {
|
||||
adapter.shutdown();
|
||||
} catch (error) {
|
||||
console.error(`Adapter ${adapter.name} failed to shutdown:`, error);
|
||||
log.error(`Adapter ${adapter.name} failed to shutdown:`, error);
|
||||
}
|
||||
}
|
||||
AnalyticsService.instance = null;
|
||||
@ -239,7 +240,7 @@ export class AnalyticsService {
|
||||
|
||||
private log(message: string, ...args: any[]): void {
|
||||
if (this.config.debug) {
|
||||
console.log(`[AnalyticsService] ${message}`, ...args);
|
||||
log.info(`[AnalyticsService] ${message}`, ...args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,12 +5,13 @@
|
||||
import { progressionStore } from '../../stores/progression';
|
||||
import { gameConfigStore } from '../../stores/gameConfig';
|
||||
import Button from '../shared/Button.svelte';
|
||||
import log from '../../core/logger';
|
||||
|
||||
export let levelId: string;
|
||||
export let levelEntry: CloudLevelEntry;
|
||||
|
||||
async function handleLevelClick() {
|
||||
console.log('[LevelCard] Level clicked:', {
|
||||
log.info('[LevelCard] Level clicked:', {
|
||||
levelId,
|
||||
levelName: levelEntry.name,
|
||||
isUnlocked,
|
||||
@ -20,24 +21,24 @@
|
||||
|
||||
// If level is locked and user not authenticated, prompt to sign in
|
||||
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 {
|
||||
await authStore.login();
|
||||
console.log('[LevelCard] Login completed');
|
||||
log.info('[LevelCard] Login completed');
|
||||
} catch (error) {
|
||||
console.error('[LevelCard] Login failed:', error);
|
||||
log.error('[LevelCard] Login failed:', error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If level is still locked (progression), don't allow play
|
||||
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;
|
||||
}
|
||||
|
||||
// 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}`);
|
||||
}
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
import { Main } from '../../main';
|
||||
import type { LevelConfig } from '../../levels/config/levelConfig';
|
||||
import { LevelRegistry } from '../../levels/storage/levelRegistry';
|
||||
import debugLog from '../../core/debug';
|
||||
import log from '../../core/logger';
|
||||
import { DefaultScene } from '../../core/defaultScene';
|
||||
|
||||
// svelte-routing passes params as an object with route params
|
||||
@ -34,17 +34,17 @@
|
||||
// Handle popstate (browser back/forward buttons)
|
||||
function handlePopState() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
console.log('[PlayLevel] Component mounted');
|
||||
console.log('[PlayLevel] params:', params);
|
||||
console.log('[PlayLevel] levelId prop:', levelId);
|
||||
console.log('[PlayLevel] actualLevelId:', actualLevelId);
|
||||
console.log('[PlayLevel] window.location.pathname:', window.location.pathname);
|
||||
log.info('[PlayLevel] Component mounted');
|
||||
log.info('[PlayLevel] params:', params);
|
||||
log.info('[PlayLevel] levelId prop:', levelId);
|
||||
log.info('[PlayLevel] actualLevelId:', actualLevelId);
|
||||
log.info('[PlayLevel] window.location.pathname:', window.location.pathname);
|
||||
|
||||
// Try to extract levelId from URL if props don't have it
|
||||
let extractedLevelId = actualLevelId;
|
||||
@ -52,19 +52,19 @@
|
||||
const match = window.location.pathname.match(/\/play\/(.+)/);
|
||||
if (match) {
|
||||
extractedLevelId = match[1];
|
||||
console.log('[PlayLevel] Extracted levelId from URL:', extractedLevelId);
|
||||
log.info('[PlayLevel] Extracted levelId from URL:', extractedLevelId);
|
||||
}
|
||||
}
|
||||
|
||||
levelName = extractedLevelId;
|
||||
|
||||
if (!levelName) {
|
||||
console.error('[PlayLevel] No levelId found!');
|
||||
log.error('[PlayLevel] No levelId found!');
|
||||
error = 'No level specified';
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[PlayLevel] Using levelName:', levelName);
|
||||
log.info('[PlayLevel] Using levelName:', levelName);
|
||||
|
||||
// Add event listeners
|
||||
window.addEventListener('beforeunload', handleBeforeUnload);
|
||||
@ -75,7 +75,7 @@
|
||||
const appElement = document.getElementById('app');
|
||||
if (appElement) {
|
||||
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)
|
||||
@ -93,7 +93,7 @@
|
||||
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)
|
||||
// We'll refactor this later to call Main methods directly
|
||||
@ -107,10 +107,10 @@
|
||||
window.dispatchEvent(event);
|
||||
|
||||
isInitialized = true;
|
||||
debugLog('[PlayLevel] Level initialization started');
|
||||
log.debug('[PlayLevel] Level initialization started');
|
||||
|
||||
} catch (err) {
|
||||
console.error('[PlayLevel] Error initializing level:', err);
|
||||
log.error('[PlayLevel] Error initializing level:', err);
|
||||
error = err instanceof Error ? err.message : 'Unknown error';
|
||||
|
||||
// Show UI again on error
|
||||
@ -127,8 +127,8 @@
|
||||
});
|
||||
|
||||
onDestroy(async () => {
|
||||
console.log('[PlayLevel] Component unmounting - cleaning up');
|
||||
debugLog('[PlayLevel] Component unmounting - cleaning up');
|
||||
log.info('[PlayLevel] Component unmounting - cleaning up');
|
||||
log.debug('[PlayLevel] Component unmounting - cleaning up');
|
||||
|
||||
// Remove event listeners
|
||||
window.removeEventListener('beforeunload', handleBeforeUnload);
|
||||
@ -138,8 +138,8 @@
|
||||
const appElement = document.getElementById('app');
|
||||
if (appElement) {
|
||||
appElement.style.display = 'block';
|
||||
console.log('[PlayLevel] App UI restored');
|
||||
debugLog('[PlayLevel] App UI restored');
|
||||
log.info('[PlayLevel] App UI restored');
|
||||
log.debug('[PlayLevel] App UI restored');
|
||||
}
|
||||
|
||||
try {
|
||||
@ -148,7 +148,7 @@
|
||||
await mainInstance.cleanupAndExit();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[PlayLevel] Error during cleanup:', err);
|
||||
log.error('[PlayLevel] Error during cleanup:', err);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
import { navigationStore } from '../../stores/navigation';
|
||||
import { AuthService } from '../../services/authService';
|
||||
import { authStore } from '../../stores/auth';
|
||||
import log from '../../core/logger';
|
||||
|
||||
// Import game views
|
||||
import LevelSelect from '../game/LevelSelect.svelte';
|
||||
@ -16,21 +17,21 @@
|
||||
|
||||
// Initialize Auth0 when component mounts
|
||||
onMount(async () => {
|
||||
console.log('[App] ========== APP MOUNTED - INITIALIZING AUTH0 ==========');
|
||||
log.info('[App] ========== APP MOUNTED - INITIALIZING AUTH0 ==========');
|
||||
try {
|
||||
const authService = AuthService.getInstance();
|
||||
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
|
||||
console.log('[App] Refreshing auth store...');
|
||||
log.info('[App] Refreshing auth store...');
|
||||
await authStore.refresh();
|
||||
console.log('[App] Auth store refreshed');
|
||||
log.info('[App] Auth store refreshed');
|
||||
} catch (error) {
|
||||
console.error('[App] !!!!! AUTH0 INITIALIZATION FAILED !!!!!', error);
|
||||
console.error('[App] Error details:', error?.message, error?.stack);
|
||||
log.error('[App] !!!!! AUTH0 INITIALIZATION FAILED !!!!!', error);
|
||||
log.error('[App] Error details:', error?.message, error?.stack);
|
||||
}
|
||||
console.log('[App] ========== AUTH0 INITIALIZATION COMPLETE ==========');
|
||||
log.info('[App] ========== AUTH0 INITIALIZATION COMPLETE ==========');
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
import type { GameResult } from '../../services/gameResultsService';
|
||||
import { CloudLeaderboardService, type CloudLeaderboardEntry, getDisplayName } from '../../services/cloudLeaderboardService';
|
||||
import { formatStars } from '../../game/scoreCalculator';
|
||||
import log from '../../core/logger';
|
||||
|
||||
// View toggle: 'local' or 'cloud'
|
||||
let activeView: 'local' | 'cloud' = 'cloud';
|
||||
@ -52,7 +53,7 @@
|
||||
}
|
||||
} catch (error) {
|
||||
cloudError = 'Failed to load cloud leaderboard';
|
||||
console.error('[Leaderboard] Cloud load error:', error);
|
||||
log.error('[Leaderboard] Cloud load error:', error);
|
||||
} finally {
|
||||
cloudLoading = false;
|
||||
cloudLoadingMore = false;
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
import Checkbox from '../shared/Checkbox.svelte';
|
||||
import NumberInput from '../shared/NumberInput.svelte';
|
||||
import InfoBox from '../shared/InfoBox.svelte';
|
||||
import log from '../../core/logger';
|
||||
|
||||
let message = '';
|
||||
let messageType: 'success' | 'error' | 'warning' = 'success';
|
||||
@ -21,7 +22,7 @@
|
||||
const config = JSON.parse(stored);
|
||||
gameConfigStore.set(config);
|
||||
} catch (error) {
|
||||
console.warn('[SettingsScreen] Failed to reload config:', error);
|
||||
log.warn('[SettingsScreen] Failed to reload config:', error);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -2,118 +2,95 @@ import { mount } from 'svelte';
|
||||
import App from '../components/layouts/App.svelte';
|
||||
import { LegacyMigration } from '../levels/migration/legacyMigration';
|
||||
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 MainClass = new (progressCallback?: (percent: number, message: string) => void) => any;
|
||||
|
||||
/**
|
||||
* 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> {
|
||||
console.log('[Main] ========================================');
|
||||
console.log('[Main] initializeApp() STARTED at', new Date().toISOString());
|
||||
console.log('[Main] ========================================');
|
||||
log.info('[Main] ========================================');
|
||||
log.info('[Main] initializeApp() STARTED at', new Date().toISOString());
|
||||
log.info('[Main] ========================================');
|
||||
|
||||
// Check for legacy data migration
|
||||
const needsMigration = LegacyMigration.needsMigration();
|
||||
console.log('[Main] Needs migration check:', needsMigration);
|
||||
log.info('[Main] Needs migration check:', 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) => {
|
||||
LegacyMigration.showMigrationModal(async (result) => {
|
||||
debugLog('[Main] Migration completed:', result);
|
||||
// Initialize the new registry system
|
||||
log.debug('[Main] Migration completed:', result);
|
||||
try {
|
||||
console.log('[Main] About to call LevelRegistry.getInstance().initialize() [AFTER MIGRATION]');
|
||||
log.info('[Main] Initializing LevelRegistry [AFTER MIGRATION]');
|
||||
await LevelRegistry.getInstance().initialize();
|
||||
console.log('[Main] LevelRegistry.initialize() completed successfully [AFTER MIGRATION]');
|
||||
debugLog('[Main] LevelRegistry initialized after migration');
|
||||
|
||||
// Mount Svelte app and create Main
|
||||
log.info('[Main] LevelRegistry initialized [AFTER MIGRATION]');
|
||||
mountAppAndCreateMain(MainConstructor);
|
||||
resolve();
|
||||
} catch (error) {
|
||||
console.error('[Main] Failed to initialize LevelRegistry after migration:', error);
|
||||
log.error('[Main] Failed to initialize LevelRegistry after migration:', error);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
console.log('[Main] No migration needed - proceeding to initialize registry');
|
||||
// Initialize the new registry system
|
||||
try {
|
||||
console.log('[Main] About to call LevelRegistry.getInstance().initialize()');
|
||||
console.log('[Main] Timestamp before initialize:', Date.now());
|
||||
await LevelRegistry.getInstance().initialize();
|
||||
console.log('[Main] Timestamp after initialize:', Date.now());
|
||||
console.log('[Main] LevelRegistry.initialize() completed successfully');
|
||||
debugLog('[Main] LevelRegistry initialized');
|
||||
|
||||
// Expose registry to window for debugging (dev mode)
|
||||
const isDev = window.location.hostname === 'localhost' ||
|
||||
window.location.hostname.includes('dev.') ||
|
||||
window.location.port !== '';
|
||||
if (isDev) {
|
||||
(window as any).__levelRegistry = LevelRegistry.getInstance();
|
||||
console.log('[Main] LevelRegistry exposed to window.__levelRegistry for debugging');
|
||||
console.log('[Main] To clear caches: window.__levelRegistry.reset(); location.reload()');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Main] !!!!! EXCEPTION in LevelRegistry initialization !!!!!');
|
||||
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);
|
||||
log.info('[Main] No migration needed - proceeding to initialize registry');
|
||||
try {
|
||||
log.info('[Main] Initializing LevelRegistry');
|
||||
await LevelRegistry.getInstance().initialize();
|
||||
log.info('[Main] LevelRegistry initialized successfully');
|
||||
|
||||
console.log('[Main] initializeApp() FINISHED at', new Date().toISOString());
|
||||
// Expose registry to window for debugging (dev mode)
|
||||
const isDev = window.location.hostname === 'localhost' ||
|
||||
window.location.hostname.includes('dev.') ||
|
||||
window.location.port !== '';
|
||||
if (isDev) {
|
||||
(window as any).__levelRegistry = LevelRegistry.getInstance();
|
||||
log.info('[Main] LevelRegistry exposed to window.__levelRegistry');
|
||||
}
|
||||
} catch (error) {
|
||||
log.error('[Main] Failed to initialize LevelRegistry:', error);
|
||||
}
|
||||
|
||||
mountAppAndCreateMain(MainConstructor);
|
||||
log.info('[Main] initializeApp() FINISHED');
|
||||
}
|
||||
|
||||
/**
|
||||
* Mount the Svelte app and create Main instance
|
||||
*/
|
||||
function mountAppAndCreateMain(MainConstructor: MainClass): void {
|
||||
console.log('[Main] Mounting Svelte app');
|
||||
log.info('[Main] Mounting Svelte app');
|
||||
const appElement = document.getElementById('app');
|
||||
if (appElement) {
|
||||
mount(App, {
|
||||
target: appElement
|
||||
});
|
||||
console.log('[Main] Svelte app mounted successfully');
|
||||
mount(App, { target: appElement });
|
||||
log.info('[Main] Svelte app mounted successfully');
|
||||
|
||||
// Create Main instance lazily only if it doesn't exist
|
||||
if (!(window as any).__mainInstance) {
|
||||
debugLog('[Main] Creating Main instance (not initialized)');
|
||||
log.debug('[Main] Creating Main instance');
|
||||
const main = new MainConstructor();
|
||||
(window as any).__mainInstance = main;
|
||||
}
|
||||
} 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
|
||||
* Suppress non-critical BabylonJS shader loading errors during development
|
||||
*/
|
||||
export function setupErrorHandler(): void {
|
||||
window.addEventListener('unhandledrejection', (event) => {
|
||||
const error = event.reason;
|
||||
if (error && error.message) {
|
||||
// Only suppress specific shader-related errors, not asset loading errors
|
||||
if (error?.message) {
|
||||
if (error.message.includes('rgbdDecode.fragment') ||
|
||||
error.message.includes('procedural.vertex') ||
|
||||
(error.message.includes('Failed to fetch dynamically imported module') &&
|
||||
(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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Engine } from "@babylonjs/core";
|
||||
import { DefaultScene } from "./defaultScene";
|
||||
import { RockFactory } from "../environment/asteroids/rockFactory";
|
||||
import debugLog from './debug';
|
||||
import log from './logger';
|
||||
import Level from "../levels/level";
|
||||
|
||||
export interface CleanupContext {
|
||||
@ -18,7 +18,7 @@ export async function cleanupAndExit(
|
||||
context: CleanupContext,
|
||||
canvas: HTMLCanvasElement
|
||||
): Promise<void> {
|
||||
debugLog('[Main] cleanupAndExit() called - starting graceful shutdown');
|
||||
log.debug('[Main] cleanupAndExit() called - starting graceful shutdown');
|
||||
try {
|
||||
context.getEngine().stopRenderLoop();
|
||||
disposeCurrentLevel(context);
|
||||
@ -29,7 +29,7 @@ export async function cleanupAndExit(
|
||||
context.resetState();
|
||||
clearCanvas(canvas);
|
||||
} catch (error) {
|
||||
console.error('[Main] Cleanup failed:', error);
|
||||
log.error('[Main] Cleanup failed:', error);
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
@ -47,7 +47,7 @@ async function exitXRSession(): Promise<void> {
|
||||
try {
|
||||
await DefaultScene.XR.baseExperience.exitXRAsync();
|
||||
} catch (error) {
|
||||
debugLog('[Main] Error exiting XR:', error);
|
||||
log.debug('[Main] Error exiting XR:', error);
|
||||
}
|
||||
}
|
||||
DefaultScene.XR = null;
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
import {GameConfig} from "./gameConfig";
|
||||
|
||||
const config = GameConfig.getInstance();
|
||||
|
||||
export default function debugLog(...params: any[]) {
|
||||
if (config.debug) {
|
||||
console.log(...params);
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,5 @@
|
||||
import log from './logger';
|
||||
|
||||
/**
|
||||
* Default ship physics configuration
|
||||
*/
|
||||
@ -93,7 +95,7 @@ export class GameConfig {
|
||||
this.save();
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to load game config from localStorage:', error);
|
||||
log.warn('Failed to load game config from localStorage:', error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ import Level from "../../levels/level";
|
||||
import { RockFactory } from "../../environment/asteroids/rockFactory";
|
||||
import { LevelConfig } from "../../levels/config/levelConfig";
|
||||
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
|
||||
@ -35,7 +35,7 @@ export function createLevelSelectedHandler(context: LevelSelectedContext): (e: C
|
||||
context.setStarted(true);
|
||||
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
|
||||
const levelSelect = document.querySelector('#levelSelect') as HTMLElement;
|
||||
@ -57,7 +57,7 @@ export function createLevelSelectedHandler(context: LevelSelectedContext): (e: C
|
||||
try {
|
||||
// Initialize engine if this is first time
|
||||
if (!context.isInitialized()) {
|
||||
debugLog('[Main] First level selected - initializing engine');
|
||||
log.debug('[Main] First level selected - initializing engine');
|
||||
preloader.updateProgress(0, 'Initializing game engine...');
|
||||
await context.initializeEngine();
|
||||
}
|
||||
@ -65,14 +65,14 @@ export function createLevelSelectedHandler(context: LevelSelectedContext): (e: C
|
||||
// Load assets if this is the first level being played
|
||||
if (!context.areAssetsLoaded()) {
|
||||
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)
|
||||
ParticleHelper.BaseAssetsUrl = window.location.href;
|
||||
await RockFactory.init();
|
||||
context.setAssetsLoaded(true);
|
||||
|
||||
debugLog('[Main] Assets loaded successfully');
|
||||
log.debug('[Main] Assets loaded successfully');
|
||||
preloader.updateProgress(60, 'Assets loaded');
|
||||
}
|
||||
|
||||
@ -88,10 +88,15 @@ export function createLevelSelectedHandler(context: LevelSelectedContext): (e: C
|
||||
try {
|
||||
preloader.updateProgress(75, 'Entering VR...');
|
||||
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) {
|
||||
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;
|
||||
// Show canvas for flat mode
|
||||
const canvas = document.getElementById('gameCanvas');
|
||||
if (canvas) {
|
||||
canvas.style.display = 'block';
|
||||
}
|
||||
engine.runRenderLoop(() => {
|
||||
DefaultScene.MainScene.render();
|
||||
});
|
||||
@ -112,9 +117,9 @@ export function createLevelSelectedHandler(context: LevelSelectedContext): (e: C
|
||||
const camera = DefaultScene.XR?.baseExperience?.camera || DefaultScene.MainScene.activeCamera;
|
||||
if (camera && audioEngine.listener) {
|
||||
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 {
|
||||
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...');
|
||||
@ -134,26 +139,31 @@ export function createLevelSelectedHandler(context: LevelSelectedContext): (e: C
|
||||
// Listen for replay requests from the ship
|
||||
if (ship) {
|
||||
ship.onReplayRequestObservable.add(() => {
|
||||
debugLog('Replay requested - reloading page');
|
||||
log.debug('Replay requested - reloading page');
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
// If we entered XR before level creation, manually setup camera parenting
|
||||
console.log('[Main] ========== CHECKING XR STATE ==========');
|
||||
console.log('[Main] DefaultScene.XR exists:', !!DefaultScene.XR);
|
||||
console.log('[Main] xrSession exists:', !!xrSession);
|
||||
log.info('[Main] ========== CHECKING XR STATE ==========');
|
||||
log.info('[Main] DefaultScene.XR exists:', !!DefaultScene.XR);
|
||||
log.info('[Main] xrSession exists:', !!xrSession);
|
||||
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) {
|
||||
debugLog('[Main] XR already active - using consolidated setupXRCamera()');
|
||||
log.debug('[Main] XR already active - using consolidated setupXRCamera()');
|
||||
level1.setupXRCamera();
|
||||
await level1.showMissionBrief();
|
||||
debugLog('[Main] XR setup and mission brief complete');
|
||||
log.debug('[Main] XR setup and mission brief complete');
|
||||
} else {
|
||||
console.log('[Main] XR not active yet - will use onInitialXRPoseSetObservable instead');
|
||||
log.info('[Main] XR not active yet - will use onInitialXRPoseSetObservable instead');
|
||||
// Show canvas for non-XR mode
|
||||
const canvas = document.getElementById('gameCanvas');
|
||||
if (canvas) {
|
||||
canvas.style.display = 'block';
|
||||
}
|
||||
engine.runRenderLoop(() => {
|
||||
DefaultScene.MainScene.render();
|
||||
});
|
||||
@ -166,20 +176,20 @@ export function createLevelSelectedHandler(context: LevelSelectedContext): (e: C
|
||||
}, 500);
|
||||
|
||||
// Hide UI (no longer remove from DOM - let Svelte routing handle it)
|
||||
console.log('[Main] ========== HIDING UI FOR GAMEPLAY ==========');
|
||||
console.log('[Main] Timestamp:', Date.now());
|
||||
log.info('[Main] ========== HIDING UI FOR GAMEPLAY ==========');
|
||||
log.info('[Main] Timestamp:', Date.now());
|
||||
|
||||
// Start the game
|
||||
console.log('[Main] About to call context.play()');
|
||||
log.info('[Main] About to call 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)
|
||||
await currentLevel.initialize();
|
||||
|
||||
} 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.');
|
||||
}
|
||||
};
|
||||
|
||||
15
src/core/logger.ts
Normal file
15
src/core/logger.ts
Normal 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;
|
||||
@ -1,7 +1,7 @@
|
||||
import { WebXRDefaultExperience, WebXRFeaturesManager } from "@babylonjs/core";
|
||||
import { DefaultScene } from "./defaultScene";
|
||||
import { InputControlManager } from "../ship/input/inputControlManager";
|
||||
import debugLog from './debug';
|
||||
import log from './logger';
|
||||
|
||||
export interface ProgressReporter {
|
||||
reportProgress(percent: number, message: string): void;
|
||||
@ -24,7 +24,7 @@ export async function initializeXR(reporter: ProgressReporter): Promise<void> {
|
||||
registerXRStateHandler();
|
||||
reporter.reportProgress(40, 'VR support enabled');
|
||||
} catch (error) {
|
||||
debugLog("WebXR initialization failed:", error);
|
||||
log.debug("WebXR initialization failed:", error);
|
||||
DefaultScene.XR = null;
|
||||
reporter.reportProgress(40, 'Desktop mode');
|
||||
}
|
||||
@ -37,7 +37,7 @@ async function createXRExperience(): Promise<void> {
|
||||
disableHandTracking: true,
|
||||
disableDefaultUI: true
|
||||
});
|
||||
debugLog(WebXRFeaturesManager.GetAvailableFeatures());
|
||||
log.debug(WebXRFeaturesManager.GetAvailableFeatures());
|
||||
}
|
||||
|
||||
function registerXRStateHandler(): void {
|
||||
|
||||
@ -7,7 +7,7 @@ import {
|
||||
Vector3
|
||||
} from "@babylonjs/core";
|
||||
import {DefaultScene} from "../../core/defaultScene";
|
||||
import debugLog from '../../core/debug';
|
||||
import log from '../../core/logger';
|
||||
|
||||
/**
|
||||
* Configuration for explosion effects
|
||||
@ -42,7 +42,7 @@ export class ExplosionManager {
|
||||
constructor(scene: Scene, config?: ExplosionConfig) {
|
||||
this.scene = scene;
|
||||
this.config = { ...ExplosionManager.DEFAULT_CONFIG, ...config };
|
||||
debugLog(this.config);
|
||||
log.debug(this.config);
|
||||
this._debrisBaseMesh = MeshBuilder.CreateIcoSphere(
|
||||
'debrisBase',
|
||||
{
|
||||
@ -60,7 +60,7 @@ export class ExplosionManager {
|
||||
* Initialize the explosion manager (no longer needed for MeshExploder, but kept for API compatibility)
|
||||
*/
|
||||
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> {
|
||||
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
|
||||
for (let i = 0; i < this.soundPoolSize; i++) {
|
||||
@ -89,7 +89,7 @@ export class ExplosionManager {
|
||||
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)
|
||||
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;
|
||||
}
|
||||
|
||||
@ -119,7 +119,7 @@ export class ExplosionManager {
|
||||
|
||||
const sound = this.getAvailableSound();
|
||||
if (!sound) {
|
||||
debugLog("ExplosionManager: No sound available in pool");
|
||||
log.debug("ExplosionManager: No sound available in pool");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -139,7 +139,7 @@ export class ExplosionManager {
|
||||
|
||||
|
||||
} catch (error) {
|
||||
debugLog("ExplosionManager: Error playing explosion audio", error);
|
||||
log.debug("ExplosionManager: Error playing explosion audio", error);
|
||||
explosionNode.dispose();
|
||||
}
|
||||
}
|
||||
@ -152,7 +152,7 @@ export class ExplosionManager {
|
||||
* @returns Array of sphere mesh objects
|
||||
*/
|
||||
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[] = [];
|
||||
|
||||
@ -182,13 +182,13 @@ export class ExplosionManager {
|
||||
sphere.setEnabled(true);
|
||||
meshPieces.push(sphere);
|
||||
} 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) {
|
||||
debugLog('[ExplosionManager] First piece sample:', {
|
||||
log.debug('[ExplosionManager] First piece sample:', {
|
||||
name: meshPieces[0].name,
|
||||
position: meshPieces[0].position.toString(),
|
||||
isVisible: meshPieces[0].isVisible,
|
||||
@ -203,8 +203,8 @@ export class ExplosionManager {
|
||||
* @param mesh The mesh to explode (will be cloned internally)
|
||||
*/
|
||||
public playExplosion(mesh: AbstractMesh): void {
|
||||
debugLog('[ExplosionManager] playExplosion called');
|
||||
debugLog('[ExplosionManager] Input mesh:', {
|
||||
log.debug('[ExplosionManager] playExplosion called');
|
||||
log.debug('[ExplosionManager] Input mesh:', {
|
||||
name: mesh.name,
|
||||
id: mesh.id,
|
||||
isInstancedMesh: !!(mesh as any).sourceMesh,
|
||||
@ -220,14 +220,14 @@ export class ExplosionManager {
|
||||
let sourceMesh: Mesh;
|
||||
if ((mesh as any).sourceMesh) {
|
||||
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 {
|
||||
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
|
||||
debugLog('[ExplosionManager] Cloning mesh...');
|
||||
log.debug('[ExplosionManager] Cloning mesh...');
|
||||
mesh.computeWorldMatrix(true);
|
||||
// Apply the instance's transformation to the cloned mesh
|
||||
const position = mesh.getAbsolutePosition().clone();
|
||||
@ -237,34 +237,34 @@ export class ExplosionManager {
|
||||
|
||||
// Check if mesh has proper geometry
|
||||
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();
|
||||
return;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
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();
|
||||
return;
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
// Create the exploder with the array of separate meshes
|
||||
// The second parameter is optional - it's the center mesh to explode from
|
||||
// If not provided, MeshExploder will auto-calculate the center
|
||||
debugLog('[ExplosionManager] Creating MeshExploder...');
|
||||
log.debug('[ExplosionManager] Creating MeshExploder...');
|
||||
try {
|
||||
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,
|
||||
duration: this.config.duration,
|
||||
maxForce: this.config.explosionForce
|
||||
@ -286,7 +286,7 @@ export class ExplosionManager {
|
||||
try {
|
||||
exploder.explode(currentValue);
|
||||
} 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)
|
||||
@ -301,7 +301,7 @@ export class ExplosionManager {
|
||||
|
||||
// Log every 15 frames (approximately every 250ms at 60fps)
|
||||
if (frameCount % 15 === 0 || frameCount === 1) {
|
||||
debugLog(`[ExplosionManager] Animation frame ${frameCount}:`, {
|
||||
log.debug(`[ExplosionManager] Animation frame ${frameCount}:`, {
|
||||
elapsed: `${elapsed}ms`,
|
||||
progress: progress.toFixed(3),
|
||||
currentValue: currentValue.toFixed(2),
|
||||
@ -313,16 +313,16 @@ export class ExplosionManager {
|
||||
// Continue animation if not complete
|
||||
if (progress >= 1.0) {
|
||||
// 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.cleanupExplosion(meshPieces);
|
||||
}
|
||||
});
|
||||
|
||||
// Log that animation loop is registered
|
||||
debugLog('[ExplosionManager] Starting animation loop...');
|
||||
log.debug('[ExplosionManager] Starting animation loop...');
|
||||
} catch (error) {
|
||||
console.error('[ExplosionManager] ERROR creating MeshExploder:', error);
|
||||
log.error('[ExplosionManager] ERROR creating MeshExploder:', error);
|
||||
// Clean up pieces if exploder failed
|
||||
meshPieces.forEach(piece => {
|
||||
if (piece && !piece.isDisposed()) {
|
||||
@ -336,7 +336,7 @@ export class ExplosionManager {
|
||||
* Clean up explosion meshes
|
||||
*/
|
||||
private cleanupExplosion(meshPieces: InstancedMesh[]): void {
|
||||
debugLog('[ExplosionManager] Starting cleanup of explosion meshes...');
|
||||
log.debug('[ExplosionManager] Starting cleanup of explosion meshes...');
|
||||
|
||||
let disposedCount = 0;
|
||||
// Dispose all the mesh pieces
|
||||
@ -346,12 +346,12 @@ export class ExplosionManager {
|
||||
mesh.dispose();
|
||||
disposedCount++;
|
||||
} 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 {
|
||||
this._debrisBaseMesh.dispose(false, true);
|
||||
// Nothing to dispose with MeshExploder approach
|
||||
debugLog("ExplosionManager disposed");
|
||||
log.debug("ExplosionManager disposed");
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ import {DefaultScene} from "../../core/defaultScene";
|
||||
import {ScoreEvent} from "../../ui/hud/scoreboard";
|
||||
import {GameConfig} from "../../core/gameConfig";
|
||||
import {ExplosionManager} from "./explosionManager";
|
||||
import debugLog from '../../core/debug';
|
||||
import log from '../../core/logger';
|
||||
import loadAsset from "../../utils/loadAsset";
|
||||
|
||||
export class Rock {
|
||||
@ -66,7 +66,7 @@ export class RockFactory {
|
||||
* Reset static state - call during game cleanup
|
||||
*/
|
||||
public static reset(): void {
|
||||
debugLog('[RockFactory] Resetting static state');
|
||||
log.debug('[RockFactory] Resetting static state');
|
||||
this._asteroidMesh = null;
|
||||
if (this._explosionManager) {
|
||||
this._explosionManager.dispose();
|
||||
@ -83,20 +83,20 @@ export class RockFactory {
|
||||
* Call this AFTER audio engine is unlocked
|
||||
*/
|
||||
public static async initAudio(audioEngine: AudioEngineV2) {
|
||||
debugLog('[RockFactory] Initializing audio via ExplosionManager');
|
||||
log.debug('[RockFactory] Initializing audio via ExplosionManager');
|
||||
if (this._explosionManager) {
|
||||
await this._explosionManager.initAudio(audioEngine);
|
||||
}
|
||||
debugLog('[RockFactory] Audio initialization complete');
|
||||
log.debug('[RockFactory] Audio initialization complete');
|
||||
}
|
||||
private static async loadMesh() {
|
||||
debugLog('loading mesh');
|
||||
log.debug('loading mesh');
|
||||
const asset = await loadAsset("asteroid.glb");
|
||||
this._asteroidMesh = asset.meshes.get('Asteroid') || null;
|
||||
if (this._asteroidMesh) {
|
||||
this._asteroidMesh.setEnabled(false);
|
||||
}
|
||||
debugLog(this._asteroidMesh);
|
||||
log.debug(this._asteroidMesh);
|
||||
}
|
||||
|
||||
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);
|
||||
debugLog(rock.id);
|
||||
log.debug(rock.id);
|
||||
rock.scaling = new Vector3(scale, scale, scale);
|
||||
rock.position = position;
|
||||
//rock.material = this._rockMaterial;
|
||||
@ -135,11 +135,11 @@ export class RockFactory {
|
||||
|
||||
// Only apply orbit constraint if enabled for this level and orbit center exists
|
||||
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);
|
||||
body.addConstraint(this._orbitCenter.body, constraint);
|
||||
} 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)
|
||||
@ -152,9 +152,9 @@ export class RockFactory {
|
||||
physicsPlugin.setActivationControl(body, PhysicsActivationControl.ALWAYS_ACTIVE);
|
||||
}
|
||||
|
||||
debugLog(`[RockFactory] Setting velocities for ${rock.name}:`);
|
||||
debugLog(`[RockFactory] Linear velocity input: ${linearVelocitry.toString()}`);
|
||||
debugLog(`[RockFactory] Angular velocity input: ${angularVelocity.toString()}`);
|
||||
log.debug(`[RockFactory] Setting velocities for ${rock.name}:`);
|
||||
log.debug(`[RockFactory] Linear velocity input: ${linearVelocitry.toString()}`);
|
||||
log.debug(`[RockFactory] Angular velocity input: ${angularVelocity.toString()}`);
|
||||
|
||||
body.setLinearVelocity(linearVelocitry);
|
||||
body.setAngularVelocity(angularVelocity);
|
||||
@ -162,24 +162,24 @@ export class RockFactory {
|
||||
// Verify velocities were set
|
||||
const setLinear = body.getLinearVelocity();
|
||||
const setAngular = body.getAngularVelocity();
|
||||
debugLog(`[RockFactory] Linear velocity after set: ${setLinear.toString()}`);
|
||||
debugLog(`[RockFactory] Angular velocity after set: ${setAngular.toString()}`);
|
||||
log.debug(`[RockFactory] Linear velocity after set: ${setLinear.toString()}`);
|
||||
log.debug(`[RockFactory] Angular velocity after set: ${setAngular.toString()}`);
|
||||
body.getCollisionObservable().add((eventData) => {
|
||||
if (eventData.type == 'COLLISION_STARTED') {
|
||||
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"});
|
||||
|
||||
// Get the asteroid mesh before disposing
|
||||
const asteroidMesh = eventData.collider.transformNode as AbstractMesh;
|
||||
debugLog('[RockFactory] Asteroid mesh to explode:', {
|
||||
log.debug('[RockFactory] Asteroid mesh to explode:', {
|
||||
name: asteroidMesh.name,
|
||||
id: asteroidMesh.id,
|
||||
position: asteroidMesh.getAbsolutePosition().toString()
|
||||
});
|
||||
|
||||
// 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) {
|
||||
eventData.collider.shape.dispose();
|
||||
}
|
||||
@ -194,7 +194,7 @@ export class RockFactory {
|
||||
}
|
||||
|
||||
// Dispose projectile physics objects
|
||||
debugLog('[RockFactory] Disposing projectile physics objects...');
|
||||
log.debug('[RockFactory] Disposing projectile physics objects...');
|
||||
if (eventData.collidedAgainst.shape) {
|
||||
eventData.collidedAgainst.shape.dispose();
|
||||
}
|
||||
@ -204,7 +204,7 @@ export class RockFactory {
|
||||
if (eventData.collidedAgainst) {
|
||||
eventData.collidedAgainst.dispose();
|
||||
}
|
||||
debugLog('[RockFactory] Disposal complete');
|
||||
log.debug('[RockFactory] Disposal complete');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
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
|
||||
@ -109,7 +109,7 @@ export class BackgroundStars {
|
||||
// Make stars always render behind everything else
|
||||
mesh.isPickable = false;
|
||||
|
||||
debugLog(`Created ${this.config.count} background stars`);
|
||||
log.debug(`Created ${this.config.count} background stars`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import {
|
||||
} from "@babylonjs/core";
|
||||
import {DefaultScene} from "../../core/defaultScene";
|
||||
import {GameConfig} from "../../core/gameConfig";
|
||||
import debugLog from "../../core/debug";
|
||||
import log from "../../core/logger";
|
||||
import loadAsset from "../../utils/loadAsset";
|
||||
import {Vector3Array} from "../../levels/config/levelConfig";
|
||||
|
||||
@ -50,7 +50,7 @@ export default class StarBase {
|
||||
agg2.body.setMotionType(PhysicsMotionType.ANIMATED);
|
||||
|
||||
agg2.body.getCollisionObservable().add((collidedBody) => {
|
||||
debugLog('collidedBody', collidedBody);
|
||||
log.debug('collidedBody', collidedBody);
|
||||
})
|
||||
|
||||
landingAgg = new PhysicsAggregate(landingMesh, PhysicsShapeType.MESH);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { getAnalytics } from "../analytics";
|
||||
import debugLog from "../core/debug";
|
||||
import log from "../core/logger";
|
||||
import { calculateScore, ScoreCalculation } from "./scoreCalculator";
|
||||
|
||||
/**
|
||||
@ -64,7 +64,7 @@ export class GameStats {
|
||||
hullDamage: this._hullDamageTaken
|
||||
}, { sampleRate: 0.5 }); // 50% sampling for performance snapshots
|
||||
} catch (error) {
|
||||
debugLog('Performance snapshot failed:', error);
|
||||
log.debug('Performance snapshot failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
@ -159,7 +159,7 @@ export class GameStats {
|
||||
totalAsteroidsDestroyed: this._asteroidsDestroyed
|
||||
}, { immediate: true }); // Send immediately
|
||||
} catch (error) {
|
||||
debugLog('Session end tracking failed:', error);
|
||||
log.debug('Session end tracking failed:', error);
|
||||
}
|
||||
|
||||
// Stop performance tracking
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import log from '../core/logger';
|
||||
|
||||
/**
|
||||
* Progression tracking system for level completion and feature unlocks
|
||||
*/
|
||||
@ -62,7 +64,7 @@ export class ProgressionManager {
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading progression data:', error);
|
||||
log.error('Error loading progression data:', error);
|
||||
}
|
||||
|
||||
// Return fresh progression data
|
||||
@ -87,7 +89,7 @@ export class ProgressionManager {
|
||||
};
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(toSave));
|
||||
} 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;
|
||||
if (completedCount >= EDITOR_UNLOCK_REQUIREMENT && !this._data.editorUnlocked) {
|
||||
this._data.editorUnlocked = true;
|
||||
console.log(`🎉 Editor unlocked! (${completedCount} levels completed)`);
|
||||
log.info(`🎉 Editor unlocked! (${completedCount} levels completed)`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@ import {
|
||||
} from "./levelConfig";
|
||||
import { FireProceduralTexture } from "@babylonjs/procedural-textures";
|
||||
import { createSphereLightmap } from "../../environment/celestial/sphereLightmap";
|
||||
import debugLog from '../../core/debug';
|
||||
import log from '../../core/logger';
|
||||
import StarBase from "../../environment/stations/starBase";
|
||||
import {LevelRegistry} from "../storage/levelRegistry";
|
||||
|
||||
@ -61,7 +61,7 @@ export class LevelDeserializer {
|
||||
planets: AbstractMesh[];
|
||||
asteroids: AbstractMesh[];
|
||||
}> {
|
||||
debugLog('Deserializing level:', this.config.difficulty);
|
||||
log.debug('Deserializing level:', this.config.difficulty);
|
||||
|
||||
const baseResult = await this.createStartBase();
|
||||
const sun = this.createSun();
|
||||
@ -158,7 +158,7 @@ export class LevelDeserializer {
|
||||
planets.push(planet);
|
||||
}
|
||||
|
||||
debugLog(`Created ${planets.length} planets from config`);
|
||||
log.debug(`Created ${planets.length} planets from config`);
|
||||
return planets;
|
||||
}
|
||||
|
||||
@ -173,15 +173,15 @@ export class LevelDeserializer {
|
||||
for (let i = 0; i < this.config.asteroids.length; i++) {
|
||||
const asteroidConfig = this.config.asteroids[i];
|
||||
|
||||
debugLog(`[LevelDeserializer] Creating asteroid ${i} (${asteroidConfig.id}):`);
|
||||
debugLog(`[LevelDeserializer] Position: [${asteroidConfig.position.join(', ')}]`);
|
||||
debugLog(`[LevelDeserializer] Scale: ${asteroidConfig.scale}`);
|
||||
debugLog(`[LevelDeserializer] Linear velocity: [${asteroidConfig.linearVelocity.join(', ')}]`);
|
||||
debugLog(`[LevelDeserializer] Angular velocity: [${asteroidConfig.angularVelocity.join(', ')}]`);
|
||||
log.debug(`[LevelDeserializer] Creating asteroid ${i} (${asteroidConfig.id}):`);
|
||||
log.debug(`[LevelDeserializer] Position: [${asteroidConfig.position.join(', ')}]`);
|
||||
log.debug(`[LevelDeserializer] Scale: ${asteroidConfig.scale}`);
|
||||
log.debug(`[LevelDeserializer] Linear velocity: [${asteroidConfig.linearVelocity.join(', ')}]`);
|
||||
log.debug(`[LevelDeserializer] Angular velocity: [${asteroidConfig.angularVelocity.join(', ')}]`);
|
||||
|
||||
// Use orbit constraints by default (true if not specified)
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ import setLoadingMessage from "../utils/setLoadingMessage";
|
||||
import {LevelConfig} from "./config/levelConfig";
|
||||
import {LevelDeserializer} from "./config/levelDeserializer";
|
||||
import {BackgroundStars} from "../environment/background/backgroundStars";
|
||||
import debugLog from '../core/debug';
|
||||
import log from '../core/logger';
|
||||
import {getAnalytics} from "../analytics";
|
||||
import {MissionBrief} from "../ui/hud/missionBrief";
|
||||
import {LevelRegistry} from "./storage/levelRegistry";
|
||||
@ -52,18 +52,18 @@ export class Level1 implements Level {
|
||||
if (!isReplayMode && DefaultScene.XR) {
|
||||
const xr = DefaultScene.XR;
|
||||
|
||||
debugLog('Level1 constructor - Setting up XR observables');
|
||||
debugLog('XR input exists:', !!xr.input);
|
||||
debugLog('onControllerAddedObservable exists:', !!xr.input?.onControllerAddedObservable);
|
||||
log.debug('Level1 constructor - Setting up XR observables');
|
||||
log.debug('XR input exists:', !!xr.input);
|
||||
log.debug('onControllerAddedObservable exists:', !!xr.input?.onControllerAddedObservable);
|
||||
|
||||
xr.baseExperience.onInitialXRPoseSetObservable.add(() => {
|
||||
debugLog('[Level1] onInitialXRPoseSetObservable fired');
|
||||
log.debug('[Level1] onInitialXRPoseSetObservable fired');
|
||||
|
||||
// Use consolidated XR camera setup
|
||||
this.setupXRCamera();
|
||||
|
||||
// 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();
|
||||
});
|
||||
}
|
||||
@ -82,16 +82,16 @@ export class Level1 implements Level {
|
||||
public setupXRCamera(): void {
|
||||
const xr = DefaultScene.XR;
|
||||
if (!xr) {
|
||||
debugLog('[Level1] setupXRCamera: No XR experience available');
|
||||
log.debug('[Level1] setupXRCamera: No XR experience available');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._ship?.transformNode) {
|
||||
console.error('[Level1] setupXRCamera: Ship or transformNode not available');
|
||||
log.error('[Level1] setupXRCamera: Ship or transformNode not available');
|
||||
return;
|
||||
}
|
||||
|
||||
debugLog('[Level1] ========== setupXRCamera START ==========');
|
||||
log.debug('[Level1] ========== setupXRCamera START ==========');
|
||||
|
||||
// Create intermediate TransformNode for camera rotation
|
||||
// WebXR camera only uses rotationQuaternion (not .rotation), and XR frame updates overwrite it
|
||||
@ -99,24 +99,30 @@ export class Level1 implements Level {
|
||||
const cameraRig = new TransformNode("xrCameraRig", DefaultScene.MainScene);
|
||||
cameraRig.parent = this._ship.transformNode;
|
||||
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
|
||||
xr.baseExperience.camera.parent = cameraRig;
|
||||
xr.baseExperience.camera.position = new Vector3(0, .8, 0);
|
||||
debugLog('[Level1] XR camera parented to cameraRig at position (0, 1.2, 0)');
|
||||
xr.baseExperience.camera.position = new Vector3(0, 1.2, 0);
|
||||
log.debug('[Level1] XR camera parented to cameraRig at position (0, 1.2, 0)');
|
||||
|
||||
// Show the canvas now that camera is properly positioned in ship
|
||||
const canvas = document.getElementById('gameCanvas');
|
||||
if (canvas) {
|
||||
canvas.style.display = 'block';
|
||||
}
|
||||
|
||||
// Ensure render loop is running
|
||||
const engine = DefaultScene.MainScene.getEngine();
|
||||
engine.runRenderLoop(() => {
|
||||
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
|
||||
if (this._ship.keyboardInput) {
|
||||
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
|
||||
@ -126,9 +132,9 @@ export class Level1 implements Level {
|
||||
if (pointerFeature) {
|
||||
const inputManager = InputControlManager.getInstance();
|
||||
inputManager.registerPointerFeature(pointerFeature);
|
||||
debugLog('[Level1] Pointer selection feature registered');
|
||||
log.debug('[Level1] Pointer selection feature registered');
|
||||
} else {
|
||||
debugLog('[Level1] WARNING: Pointer selection feature not available');
|
||||
log.debug('[Level1] WARNING: Pointer selection feature not available');
|
||||
}
|
||||
|
||||
// Track WebXR session start
|
||||
@ -139,16 +145,16 @@ export class Level1 implements Level {
|
||||
isImmersive: true
|
||||
});
|
||||
} catch (error) {
|
||||
debugLog('[Level1] Analytics tracking failed:', error);
|
||||
log.debug('[Level1] Analytics tracking failed:', error);
|
||||
}
|
||||
|
||||
// Setup controller observer
|
||||
xr.input.onControllerAddedObservable.add((controller) => {
|
||||
debugLog('[Level1] 🎮 Controller added:', controller.inputSource.handedness);
|
||||
log.debug('[Level1] 🎮 Controller added:', controller.inputSource.handedness);
|
||||
this._ship.addController(controller);
|
||||
});
|
||||
|
||||
debugLog('[Level1] ========== setupXRCamera COMPLETE ==========');
|
||||
log.debug('[Level1] ========== setupXRCamera COMPLETE ==========');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -158,12 +164,12 @@ export class Level1 implements Level {
|
||||
public async showMissionBrief(): Promise<void> {
|
||||
// Prevent showing twice
|
||||
if (this._missionBriefShown) {
|
||||
console.log('[Level1] Mission brief already shown, skipping');
|
||||
log.info('[Level1] Mission brief already shown, skipping');
|
||||
return;
|
||||
}
|
||||
|
||||
this._missionBriefShown = true;
|
||||
console.log('[Level1] showMissionBrief() called');
|
||||
log.info('[Level1] showMissionBrief() called');
|
||||
|
||||
let directoryEntry: CloudLevelEntry | null = null;
|
||||
|
||||
@ -171,18 +177,18 @@ export class Level1 implements Level {
|
||||
if (this._levelId) {
|
||||
try {
|
||||
const registry = LevelRegistry.getInstance();
|
||||
console.log('[Level1] ======================================');
|
||||
console.log('[Level1] Getting all levels from registry...');
|
||||
log.info('[Level1] ======================================');
|
||||
log.info('[Level1] Getting all levels from registry...');
|
||||
const allLevels = registry.getAllLevels();
|
||||
console.log('[Level1] Total levels in registry:', allLevels.size);
|
||||
console.log('[Level1] Looking for level ID:', this._levelId);
|
||||
log.info('[Level1] Total levels in registry:', allLevels.size);
|
||||
log.info('[Level1] Looking for level ID:', this._levelId);
|
||||
|
||||
const registryEntry = allLevels.get(this._levelId);
|
||||
console.log('[Level1] Registry entry found:', !!registryEntry);
|
||||
log.info('[Level1] Registry entry found:', !!registryEntry);
|
||||
|
||||
if (registryEntry) {
|
||||
directoryEntry = registryEntry;
|
||||
console.log('[Level1] Level entry data:', {
|
||||
log.info('[Level1] Level entry data:', {
|
||||
id: directoryEntry?.id,
|
||||
slug: directoryEntry?.slug,
|
||||
name: directoryEntry?.name,
|
||||
@ -193,39 +199,39 @@ export class Level1 implements Level {
|
||||
});
|
||||
|
||||
if (directoryEntry?.missionBrief) {
|
||||
console.log('[Level1] Mission brief objectives:');
|
||||
log.info('[Level1] Mission brief objectives:');
|
||||
directoryEntry.missionBrief.forEach((item, i) => {
|
||||
console.log(` ${i + 1}. ${item}`);
|
||||
log.info(` ${i + 1}. ${item}`);
|
||||
});
|
||||
} else {
|
||||
console.warn('[Level1] ⚠️ No missionBrief found in level entry!');
|
||||
log.warn('[Level1] ⚠️ No missionBrief found in level entry!');
|
||||
}
|
||||
} else {
|
||||
console.error('[Level1] ❌ No registry entry found for level ID:', this._levelId);
|
||||
console.log('[Level1] Available level IDs:', Array.from(allLevels.keys()));
|
||||
log.error('[Level1] ❌ No registry entry found for level ID:', this._levelId);
|
||||
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) {
|
||||
console.error('[Level1] ❌ Exception while getting directory entry:', error);
|
||||
debugLog('[Level1] Failed to get directory entry:', error);
|
||||
log.error('[Level1] ❌ Exception while getting directory entry:', error);
|
||||
log.debug('[Level1] Failed to get directory entry:', error);
|
||||
}
|
||||
} else {
|
||||
console.warn('[Level1] ⚠️ No level ID available, using config-only mission brief');
|
||||
debugLog('[Level1] No level ID available, using config-only mission brief');
|
||||
log.warn('[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
|
||||
debugLog('[Level1] Disabling ship controls for mission brief');
|
||||
log.debug('[Level1] Disabling ship controls for mission brief');
|
||||
const inputManager = InputControlManager.getInstance();
|
||||
inputManager.disableShipControls("MissionBrief");
|
||||
|
||||
// Show mission brief with trigger observable
|
||||
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");
|
||||
this.startGameplay();
|
||||
});
|
||||
@ -237,19 +243,19 @@ export class Level1 implements Level {
|
||||
*/
|
||||
private startGameplay(): void {
|
||||
if (this._gameStarted) {
|
||||
debugLog('[Level1] startGameplay called but game already started');
|
||||
log.debug('[Level1] startGameplay called but game already started');
|
||||
return;
|
||||
}
|
||||
|
||||
this._gameStarted = true;
|
||||
debugLog('[Level1] Starting gameplay');
|
||||
log.debug('[Level1] Starting gameplay');
|
||||
|
||||
// Enable game end condition checking on ship
|
||||
this._ship.startGameplay();
|
||||
|
||||
// Start game timer
|
||||
this._ship.gameStats.startTimer();
|
||||
debugLog('Game timer started');
|
||||
log.debug('Game timer started');
|
||||
}
|
||||
|
||||
public async play() {
|
||||
@ -266,55 +272,55 @@ export class Level1 implements Level {
|
||||
playCount: 1 // TODO: Get actual play count from progression system
|
||||
});
|
||||
} catch (error) {
|
||||
debugLog('Analytics tracking failed:', error);
|
||||
log.debug('Analytics tracking failed:', error);
|
||||
}
|
||||
|
||||
// Play background music (already loaded during initialization)
|
||||
if (this._backgroundMusic) {
|
||||
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 (DefaultScene.XR && DefaultScene.XR.baseExperience.state === WebXRState.IN_XR) {
|
||||
// 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) => {
|
||||
debugLog(`Controller ${index} - handedness: ${controller.inputSource.handedness}`);
|
||||
log.debug(`Controller ${index} - handedness: ${controller.inputSource.handedness}`);
|
||||
this._ship.addController(controller);
|
||||
});
|
||||
|
||||
// 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(() => {
|
||||
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) => {
|
||||
debugLog(` Late controller ${index} - handedness: ${controller.inputSource.handedness}`);
|
||||
log.debug(` Late controller ${index} - handedness: ${controller.inputSource.handedness}`);
|
||||
});
|
||||
}, 2000);
|
||||
|
||||
// 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) {
|
||||
// XR available but not entered yet, try to enter
|
||||
try {
|
||||
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
|
||||
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);
|
||||
});
|
||||
// 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) {
|
||||
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
|
||||
this.startGameplay();
|
||||
}
|
||||
} else {
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
@ -341,9 +347,9 @@ export class Level1 implements Level {
|
||||
}
|
||||
|
||||
public async initialize() {
|
||||
debugLog('Initializing level from config:', this._levelConfig.difficulty);
|
||||
log.debug('Initializing level from config:', this._levelConfig.difficulty);
|
||||
if (this._initialized) {
|
||||
console.error('Initialize called twice');
|
||||
log.error('Initialize called twice');
|
||||
return;
|
||||
}
|
||||
await this._ship.initialize();
|
||||
@ -380,7 +386,7 @@ export class Level1 implements Level {
|
||||
|
||||
// Initialize scoreboard with total asteroid count
|
||||
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
|
||||
setLoadingMessage("Creating starfield...");
|
||||
@ -407,7 +413,7 @@ export class Level1 implements Level {
|
||||
if (!this._isReplayMode) {
|
||||
setLoadingMessage("Initializing physics recorder...");
|
||||
//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
|
||||
@ -417,39 +423,39 @@ export class Level1 implements Level {
|
||||
loop: true,
|
||||
volume: 0.5
|
||||
});
|
||||
debugLog('Background music loaded successfully');
|
||||
log.debug('Background music loaded successfully');
|
||||
}
|
||||
|
||||
// Initialize mission brief (will be shown when entering XR)
|
||||
setLoadingMessage("Initializing mission brief...");
|
||||
console.log('[Level1] ========== ABOUT TO INITIALIZE MISSION BRIEF ==========');
|
||||
console.log('[Level1] _missionBrief object:', this._missionBrief);
|
||||
console.log('[Level1] Ship exists:', !!this._ship);
|
||||
console.log('[Level1] Ship ID in scene:', DefaultScene.MainScene.getNodeById('Ship') !== null);
|
||||
log.info('[Level1] ========== ABOUT TO INITIALIZE MISSION BRIEF ==========');
|
||||
log.info('[Level1] _missionBrief object:', this._missionBrief);
|
||||
log.info('[Level1] Ship exists:', !!this._ship);
|
||||
log.info('[Level1] Ship ID in scene:', DefaultScene.MainScene.getNodeById('Ship') !== null);
|
||||
this._missionBrief.initialize();
|
||||
console.log('[Level1] ========== MISSION BRIEF INITIALIZATION COMPLETE ==========');
|
||||
debugLog('Mission brief initialized');
|
||||
log.info('[Level1] ========== MISSION BRIEF INITIALIZATION COMPLETE ==========');
|
||||
log.debug('Mission brief initialized');
|
||||
|
||||
this._initialized = true;
|
||||
|
||||
// Set par time and level info for score calculation and results recording
|
||||
const parTime = this.getParTimeForDifficulty(this._levelConfig.difficulty);
|
||||
const statusScreen = this._ship.statusScreen;
|
||||
console.log('[Level1] StatusScreen reference:', statusScreen);
|
||||
console.log('[Level1] Level config metadata:', this._levelConfig.metadata);
|
||||
console.log('[Level1] Asteroids count:', entities.asteroids.length);
|
||||
log.info('[Level1] StatusScreen reference:', statusScreen);
|
||||
log.info('[Level1] Level config metadata:', this._levelConfig.metadata);
|
||||
log.info('[Level1] Asteroids count:', entities.asteroids.length);
|
||||
if (statusScreen) {
|
||||
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
|
||||
const levelId = this._levelId || 'unknown';
|
||||
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);
|
||||
console.log('[Level1] setCurrentLevel called successfully');
|
||||
log.info('[Level1] setCurrentLevel called successfully');
|
||||
} else {
|
||||
console.error('[Level1] StatusScreen is null/undefined!');
|
||||
log.error('[Level1] StatusScreen is null/undefined!');
|
||||
}
|
||||
|
||||
// Notify that initialization is complete
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import {LevelConfig} from "../config/levelConfig";
|
||||
import log from "../../core/logger";
|
||||
|
||||
const LEGACY_STORAGE_KEY = 'space-game-levels';
|
||||
const ARCHIVE_STORAGE_KEY = 'space-game-levels-archive';
|
||||
@ -64,7 +65,7 @@ export class LegacyMigration {
|
||||
}
|
||||
return status;
|
||||
} catch (error) {
|
||||
console.error('Failed to parse migration status:', error);
|
||||
log.error('Failed to parse migration status:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -127,10 +128,10 @@ export class LegacyMigration {
|
||||
|
||||
result.success = true;
|
||||
|
||||
console.log('Migration completed:', result);
|
||||
log.info('Migration completed:', result);
|
||||
} catch (error) {
|
||||
result.error = error instanceof Error ? error.message : 'Unknown error';
|
||||
console.error('Migration failed:', error);
|
||||
log.error('Migration failed:', error);
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -162,7 +163,7 @@ export class LegacyMigration {
|
||||
const archive = JSON.parse(stored);
|
||||
return archive.data || null;
|
||||
} catch (error) {
|
||||
console.error('Failed to parse archived data:', error);
|
||||
log.error('Failed to parse archived data:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -186,7 +187,7 @@ export class LegacyMigration {
|
||||
|
||||
return JSON.stringify(exportData, null, 2);
|
||||
} catch (error) {
|
||||
console.error('Failed to export legacy data:', error);
|
||||
log.error('Failed to export legacy data:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -197,7 +198,7 @@ export class LegacyMigration {
|
||||
public static downloadLegacyData(): void {
|
||||
const jsonString = this.exportLegacyData();
|
||||
if (!jsonString) {
|
||||
console.warn('No legacy data to download');
|
||||
log.warn('No legacy data to download');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -224,7 +225,7 @@ export class LegacyMigration {
|
||||
*/
|
||||
public static resetMigration(): void {
|
||||
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(CUSTOM_LEVELS_KEY);
|
||||
localStorage.removeItem(MIGRATION_STATUS_KEY);
|
||||
console.log('Full migration reset completed');
|
||||
log.info('Full migration reset completed');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { LevelConfig } from "../config/levelConfig";
|
||||
import { CloudLevelService, CloudLevelEntry } from "../../services/cloudLevelService";
|
||||
import log from "../../core/logger";
|
||||
|
||||
/**
|
||||
* Singleton registry for managing levels from cloud (Supabase)
|
||||
@ -36,7 +37,7 @@ export class LevelRegistry {
|
||||
}
|
||||
|
||||
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()));
|
||||
}
|
||||
|
||||
@ -74,6 +75,6 @@ export class LevelRegistry {
|
||||
public reset(): void {
|
||||
this.levels.clear();
|
||||
this.initialized = false;
|
||||
console.log('[LevelRegistry] Reset complete. Call initialize() to reload.');
|
||||
log.info('[LevelRegistry] Reset complete. Call initialize() to reload.');
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ import '@babylonjs/loaders';
|
||||
|
||||
import { DefaultScene } from "./core/defaultScene";
|
||||
import Level from "./levels/level";
|
||||
import debugLog from './core/debug';
|
||||
import log from './core/logger';
|
||||
|
||||
import { initializeAnalytics } from './analytics/initAnalytics';
|
||||
import { createLevelSelectedHandler, LevelSelectedContext } from './core/handlers/levelSelectedHandler';
|
||||
@ -59,7 +59,7 @@ export class Main implements LevelSelectedContext, CleanupContext {
|
||||
|
||||
public async initializeEngine(): Promise<void> {
|
||||
if (this._initialized) return;
|
||||
debugLog('[Main] Starting engine initialization');
|
||||
log.debug('[Main] Starting engine initialization');
|
||||
this.reportProgress(0, 'Initializing 3D engine...');
|
||||
const result = await setupScene(canvas, this);
|
||||
this._engine = result.engine;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { createAuth0Client, Auth0Client, User } from '@auth0/auth0-spa-js';
|
||||
import log from '../core/logger';
|
||||
|
||||
/**
|
||||
* Singleton service for managing Auth0 authentication
|
||||
@ -26,22 +27,22 @@ export class AuthService {
|
||||
* Call this early in the application lifecycle
|
||||
*/
|
||||
public async initialize(): Promise<void> {
|
||||
console.log('[AuthService] ========== INITIALIZE CALLED ==========');
|
||||
log.info('[AuthService] ========== INITIALIZE CALLED ==========');
|
||||
const domain = import.meta.env.VITE_AUTH0_DOMAIN;
|
||||
const clientId = import.meta.env.VITE_AUTH0_CLIENT_ID;
|
||||
|
||||
console.log('[AuthService] Config:', {
|
||||
log.info('[AuthService] Config:', {
|
||||
domain,
|
||||
clientId: clientId ? clientId.substring(0, 10) + '...' : 'missing',
|
||||
redirectUri: window.location.origin
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
console.log('[AuthService] Creating Auth0 client...');
|
||||
log.info('[AuthService] Creating Auth0 client...');
|
||||
const audience = import.meta.env.VITE_AUTH0_AUDIENCE;
|
||||
this._client = await createAuth0Client({
|
||||
domain,
|
||||
@ -53,58 +54,58 @@ export class AuthService {
|
||||
cacheLocation: 'localstorage', // Persist tokens across page reloads
|
||||
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
|
||||
const hasCallback = window.location.search.includes('code=') ||
|
||||
window.location.search.includes('state=');
|
||||
console.log('[AuthService] Checking for Auth0 callback:', hasCallback);
|
||||
console.log('[AuthService] Current URL:', window.location.href);
|
||||
log.info('[AuthService] Checking for Auth0 callback:', hasCallback);
|
||||
log.info('[AuthService] Current URL:', window.location.href);
|
||||
|
||||
if (hasCallback) {
|
||||
console.log('[AuthService] ========== PROCESSING AUTH0 CALLBACK ==========');
|
||||
log.info('[AuthService] ========== PROCESSING AUTH0 CALLBACK ==========');
|
||||
try {
|
||||
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
|
||||
window.history.replaceState({}, document.title, '/');
|
||||
console.log('[AuthService] URL cleaned, redirected to home');
|
||||
log.info('[AuthService] URL cleaned, redirected to home');
|
||||
} catch (error) {
|
||||
console.error('[AuthService] !!!!! CALLBACK ERROR !!!!!', error);
|
||||
console.error('[AuthService] Error details:', error?.message, error?.stack);
|
||||
log.error('[AuthService] !!!!! CALLBACK ERROR !!!!!', error);
|
||||
log.error('[AuthService] Error details:', error?.message, error?.stack);
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
console.log('[AuthService] Is authenticated:', isAuth);
|
||||
log.info('[AuthService] Is authenticated:', isAuth);
|
||||
|
||||
if (isAuth) {
|
||||
console.log('[AuthService] Loading user info...');
|
||||
log.info('[AuthService] Loading user info...');
|
||||
this._user = await this._client.getUser() ?? null;
|
||||
console.log('[AuthService] User loaded:', {
|
||||
log.info('[AuthService] User loaded:', {
|
||||
name: this._user?.name,
|
||||
email: this._user?.email,
|
||||
sub: this._user?.sub
|
||||
});
|
||||
} 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
|
||||
*/
|
||||
public async login(): Promise<void> {
|
||||
console.log('[AuthService] ========== LOGIN CALLED ==========');
|
||||
log.info('[AuthService] ========== LOGIN CALLED ==========');
|
||||
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.');
|
||||
}
|
||||
console.log('[AuthService] Redirecting to Auth0 login...');
|
||||
log.info('[AuthService] Redirecting to Auth0 login...');
|
||||
await this._client.loginWithRedirect();
|
||||
}
|
||||
|
||||
@ -112,13 +113,13 @@ export class AuthService {
|
||||
* Log out the current user and redirect to home
|
||||
*/
|
||||
public async logout(): Promise<void> {
|
||||
console.log('[AuthService] ========== LOGOUT CALLED ==========');
|
||||
log.info('[AuthService] ========== LOGOUT CALLED ==========');
|
||||
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.');
|
||||
}
|
||||
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({
|
||||
logoutParams: {
|
||||
returnTo: window.location.origin
|
||||
@ -150,7 +151,7 @@ export class AuthService {
|
||||
try {
|
||||
return await this._client.getTokenSilently();
|
||||
} catch (error) {
|
||||
console.error('Error getting access token:', error);
|
||||
log.error('Error getting access token:', error);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { SupabaseService } from './supabaseService';
|
||||
import { AuthService } from './authService';
|
||||
import type { GameResult } from './gameResultsService';
|
||||
import log from '../core/logger';
|
||||
|
||||
/**
|
||||
* Represents a leaderboard entry from Supabase
|
||||
@ -69,7 +70,7 @@ export class CloudLeaderboardService {
|
||||
const client = await supabase.getAuthenticatedClient();
|
||||
|
||||
if (!client) {
|
||||
console.warn('[CloudLeaderboardService] Not authenticated - cannot sync user');
|
||||
log.warn('[CloudLeaderboardService] Not authenticated - cannot sync user');
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -84,11 +85,11 @@ export class CloudLeaderboardService {
|
||||
});
|
||||
|
||||
if (error) {
|
||||
console.error('[CloudLeaderboardService] Failed to sync user:', error);
|
||||
log.error('[CloudLeaderboardService] Failed to sync user:', error);
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log('[CloudLeaderboardService] User synced:', userId);
|
||||
log.info('[CloudLeaderboardService] User synced:', userId);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -100,7 +101,7 @@ export class CloudLeaderboardService {
|
||||
const supabase = SupabaseService.getInstance();
|
||||
|
||||
if (!supabase.isConfigured()) {
|
||||
console.warn('[CloudLeaderboardService] Supabase not configured');
|
||||
log.warn('[CloudLeaderboardService] Supabase not configured');
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -109,16 +110,16 @@ export class CloudLeaderboardService {
|
||||
const user = authService.getUser();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
const client = await supabase.getAuthenticatedClient();
|
||||
if (!client) {
|
||||
console.warn('[CloudLeaderboardService] Not authenticated - cannot submit score');
|
||||
log.warn('[CloudLeaderboardService] Not authenticated - cannot submit score');
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -141,7 +142,7 @@ export class CloudLeaderboardService {
|
||||
star_rating: result.starRating
|
||||
};
|
||||
|
||||
console.log('[CloudLeaderboardService] Inserting entry:', entry);
|
||||
log.info('[CloudLeaderboardService] Inserting entry:', entry);
|
||||
|
||||
const { data, error } = await client
|
||||
.from('leaderboard')
|
||||
@ -149,12 +150,12 @@ export class CloudLeaderboardService {
|
||||
.select();
|
||||
|
||||
if (error) {
|
||||
console.error('[CloudLeaderboardService] Failed to submit score:', error);
|
||||
console.error('[CloudLeaderboardService] Error details:', JSON.stringify(error, null, 2));
|
||||
log.error('[CloudLeaderboardService] Failed to submit score:', error);
|
||||
log.error('[CloudLeaderboardService] Error details:', JSON.stringify(error, null, 2));
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log('[CloudLeaderboardService] Score submitted successfully:', data);
|
||||
log.info('[CloudLeaderboardService] Score submitted successfully:', data);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -167,7 +168,7 @@ export class CloudLeaderboardService {
|
||||
const client = supabase.getClient();
|
||||
|
||||
if (!client) {
|
||||
console.warn('[CloudLeaderboardService] Supabase not configured');
|
||||
log.warn('[CloudLeaderboardService] Supabase not configured');
|
||||
return [];
|
||||
}
|
||||
|
||||
@ -178,7 +179,7 @@ export class CloudLeaderboardService {
|
||||
.range(offset, offset + limit - 1);
|
||||
|
||||
if (error) {
|
||||
console.error('[CloudLeaderboardService] Failed to fetch leaderboard:', error);
|
||||
log.error('[CloudLeaderboardService] Failed to fetch leaderboard:', error);
|
||||
return [];
|
||||
}
|
||||
|
||||
@ -193,7 +194,7 @@ export class CloudLeaderboardService {
|
||||
const client = supabase.getClient();
|
||||
|
||||
if (!client) {
|
||||
console.warn('[CloudLeaderboardService] Supabase not configured');
|
||||
log.warn('[CloudLeaderboardService] Supabase not configured');
|
||||
return [];
|
||||
}
|
||||
|
||||
@ -205,7 +206,7 @@ export class CloudLeaderboardService {
|
||||
.limit(limit);
|
||||
|
||||
if (error) {
|
||||
console.error('[CloudLeaderboardService] Failed to fetch user scores:', error);
|
||||
log.error('[CloudLeaderboardService] Failed to fetch user scores:', error);
|
||||
return [];
|
||||
}
|
||||
|
||||
@ -220,7 +221,7 @@ export class CloudLeaderboardService {
|
||||
const client = supabase.getClient();
|
||||
|
||||
if (!client) {
|
||||
console.warn('[CloudLeaderboardService] Supabase not configured');
|
||||
log.warn('[CloudLeaderboardService] Supabase not configured');
|
||||
return [];
|
||||
}
|
||||
|
||||
@ -232,7 +233,7 @@ export class CloudLeaderboardService {
|
||||
.limit(limit);
|
||||
|
||||
if (error) {
|
||||
console.error('[CloudLeaderboardService] Failed to fetch level leaderboard:', error);
|
||||
log.error('[CloudLeaderboardService] Failed to fetch level leaderboard:', error);
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { SupabaseService } from './supabaseService';
|
||||
import { AuthService } from './authService';
|
||||
import type { LevelConfig } from '../levels/config/levelConfig';
|
||||
import log from '../core/logger';
|
||||
|
||||
/**
|
||||
* Level entry from the cloud database
|
||||
@ -66,7 +67,7 @@ function rowToEntry(row: LevelRow): CloudLevelEntry {
|
||||
try {
|
||||
config = JSON.parse(row.config);
|
||||
} 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[]> {
|
||||
const client = SupabaseService.getInstance().getClient();
|
||||
if (!client) {
|
||||
console.warn('[CloudLevelService] Supabase not configured');
|
||||
log.warn('[CloudLevelService] Supabase not configured');
|
||||
return [];
|
||||
}
|
||||
|
||||
console.log('[CloudLevelService] Fetching official levels...');
|
||||
log.info('[CloudLevelService] Fetching official levels...');
|
||||
const { data, error } = await client
|
||||
.from('levels')
|
||||
.select('*')
|
||||
.eq('level_type', 'official')
|
||||
.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) {
|
||||
console.log('[CloudLevelService] Raw rows:', JSON.stringify(data, null, 2));
|
||||
log.info('[CloudLevelService] Raw rows:', JSON.stringify(data, null, 2));
|
||||
}
|
||||
|
||||
if (error) {
|
||||
console.error('[CloudLevelService] Failed to fetch official levels:', error);
|
||||
log.error('[CloudLevelService] Failed to fetch official levels:', error);
|
||||
return [];
|
||||
}
|
||||
|
||||
@ -160,7 +161,7 @@ export class CloudLevelService {
|
||||
public async getPublishedLevels(limit: number = 20, offset: number = 0): Promise<CloudLevelEntry[]> {
|
||||
const client = SupabaseService.getInstance().getClient();
|
||||
if (!client) {
|
||||
console.warn('[CloudLevelService] Supabase not configured');
|
||||
log.warn('[CloudLevelService] Supabase not configured');
|
||||
return [];
|
||||
}
|
||||
|
||||
@ -172,7 +173,7 @@ export class CloudLevelService {
|
||||
.range(offset, offset + limit - 1);
|
||||
|
||||
if (error) {
|
||||
console.error('[CloudLevelService] Failed to fetch published levels:', error);
|
||||
log.error('[CloudLevelService] Failed to fetch published levels:', error);
|
||||
return [];
|
||||
}
|
||||
|
||||
@ -186,14 +187,14 @@ export class CloudLevelService {
|
||||
const supabaseService = SupabaseService.getInstance();
|
||||
const client = await supabaseService.getAuthenticatedClient();
|
||||
if (!client) {
|
||||
console.warn('[CloudLevelService] Not authenticated');
|
||||
log.warn('[CloudLevelService] Not authenticated');
|
||||
return [];
|
||||
}
|
||||
|
||||
// Get internal user ID (UUID)
|
||||
const internalUserId = await supabaseService.ensureUserExists();
|
||||
if (!internalUserId) {
|
||||
console.warn('[CloudLevelService] No internal user ID available');
|
||||
log.warn('[CloudLevelService] No internal user ID available');
|
||||
return [];
|
||||
}
|
||||
|
||||
@ -204,7 +205,7 @@ export class CloudLevelService {
|
||||
.order('updated_at', { ascending: false });
|
||||
|
||||
if (error) {
|
||||
console.error('[CloudLevelService] Failed to fetch user levels:', error);
|
||||
log.error('[CloudLevelService] Failed to fetch user levels:', error);
|
||||
return [];
|
||||
}
|
||||
|
||||
@ -217,7 +218,7 @@ export class CloudLevelService {
|
||||
public async getLevelById(id: string): Promise<CloudLevelEntry | null> {
|
||||
const client = SupabaseService.getInstance().getClient();
|
||||
if (!client) {
|
||||
console.warn('[CloudLevelService] Supabase not configured');
|
||||
log.warn('[CloudLevelService] Supabase not configured');
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -229,7 +230,7 @@ export class CloudLevelService {
|
||||
|
||||
if (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;
|
||||
}
|
||||
@ -243,7 +244,7 @@ export class CloudLevelService {
|
||||
public async getLevelBySlug(slug: string): Promise<CloudLevelEntry | null> {
|
||||
const client = SupabaseService.getInstance().getClient();
|
||||
if (!client) {
|
||||
console.warn('[CloudLevelService] Supabase not configured');
|
||||
log.warn('[CloudLevelService] Supabase not configured');
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -255,7 +256,7 @@ export class CloudLevelService {
|
||||
|
||||
if (error) {
|
||||
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;
|
||||
}
|
||||
@ -279,7 +280,7 @@ export class CloudLevelService {
|
||||
});
|
||||
|
||||
if (error) {
|
||||
console.error('[CloudLevelService] Failed to check slug availability:', error);
|
||||
log.error('[CloudLevelService] Failed to check slug availability:', error);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -307,14 +308,14 @@ export class CloudLevelService {
|
||||
const supabaseService = SupabaseService.getInstance();
|
||||
const client = await supabaseService.getAuthenticatedClient();
|
||||
if (!client) {
|
||||
console.warn('[CloudLevelService] Not authenticated');
|
||||
log.warn('[CloudLevelService] Not authenticated');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get internal user ID (UUID)
|
||||
const internalUserId = await supabaseService.ensureUserExists();
|
||||
if (!internalUserId) {
|
||||
console.warn('[CloudLevelService] No internal user ID available');
|
||||
log.warn('[CloudLevelService] No internal user ID available');
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -335,7 +336,7 @@ export class CloudLevelService {
|
||||
.single();
|
||||
|
||||
if (error) {
|
||||
console.error('[CloudLevelService] Failed to create level:', error);
|
||||
log.error('[CloudLevelService] Failed to create level:', error);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -359,7 +360,7 @@ export class CloudLevelService {
|
||||
): Promise<CloudLevelEntry | null> {
|
||||
const client = await SupabaseService.getInstance().getAuthenticatedClient();
|
||||
if (!client) {
|
||||
console.warn('[CloudLevelService] Not authenticated');
|
||||
log.warn('[CloudLevelService] Not authenticated');
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -380,7 +381,7 @@ export class CloudLevelService {
|
||||
.single();
|
||||
|
||||
if (error) {
|
||||
console.error('[CloudLevelService] Failed to update level:', error);
|
||||
log.error('[CloudLevelService] Failed to update level:', error);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -393,7 +394,7 @@ export class CloudLevelService {
|
||||
public async deleteLevel(id: string): Promise<boolean> {
|
||||
const client = await SupabaseService.getInstance().getAuthenticatedClient();
|
||||
if (!client) {
|
||||
console.warn('[CloudLevelService] Not authenticated');
|
||||
log.warn('[CloudLevelService] Not authenticated');
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -403,7 +404,7 @@ export class CloudLevelService {
|
||||
.eq('id', id);
|
||||
|
||||
if (error) {
|
||||
console.error('[CloudLevelService] Failed to delete level:', error);
|
||||
log.error('[CloudLevelService] Failed to delete level:', error);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -420,7 +421,7 @@ export class CloudLevelService {
|
||||
public async submitForReview(id: string): Promise<boolean> {
|
||||
const client = await SupabaseService.getInstance().getAuthenticatedClient();
|
||||
if (!client) {
|
||||
console.warn('[CloudLevelService] Not authenticated');
|
||||
log.warn('[CloudLevelService] Not authenticated');
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -429,7 +430,7 @@ export class CloudLevelService {
|
||||
});
|
||||
|
||||
if (error) {
|
||||
console.error('[CloudLevelService] Failed to submit for review:', error);
|
||||
log.error('[CloudLevelService] Failed to submit for review:', error);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -442,7 +443,7 @@ export class CloudLevelService {
|
||||
public async withdrawSubmission(id: string): Promise<boolean> {
|
||||
const client = await SupabaseService.getInstance().getAuthenticatedClient();
|
||||
if (!client) {
|
||||
console.warn('[CloudLevelService] Not authenticated');
|
||||
log.warn('[CloudLevelService] Not authenticated');
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -453,7 +454,7 @@ export class CloudLevelService {
|
||||
.eq('level_type', 'pending_review');
|
||||
|
||||
if (error) {
|
||||
console.error('[CloudLevelService] Failed to withdraw submission:', error);
|
||||
log.error('[CloudLevelService] Failed to withdraw submission:', error);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -470,7 +471,7 @@ export class CloudLevelService {
|
||||
public async getPendingReviews(): Promise<CloudLevelEntry[]> {
|
||||
const client = await SupabaseService.getInstance().getAuthenticatedClient();
|
||||
if (!client) {
|
||||
console.warn('[CloudLevelService] Not authenticated');
|
||||
log.warn('[CloudLevelService] Not authenticated');
|
||||
return [];
|
||||
}
|
||||
|
||||
@ -481,7 +482,7 @@ export class CloudLevelService {
|
||||
.order('submitted_at', { ascending: true });
|
||||
|
||||
if (error) {
|
||||
console.error('[CloudLevelService] Failed to fetch pending reviews:', error);
|
||||
log.error('[CloudLevelService] Failed to fetch pending reviews:', error);
|
||||
return [];
|
||||
}
|
||||
|
||||
@ -494,7 +495,7 @@ export class CloudLevelService {
|
||||
public async approveLevel(id: string, notes?: string): Promise<boolean> {
|
||||
const client = await SupabaseService.getInstance().getAuthenticatedClient();
|
||||
if (!client) {
|
||||
console.warn('[CloudLevelService] Not authenticated');
|
||||
log.warn('[CloudLevelService] Not authenticated');
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -504,7 +505,7 @@ export class CloudLevelService {
|
||||
});
|
||||
|
||||
if (error) {
|
||||
console.error('[CloudLevelService] Failed to approve level:', error);
|
||||
log.error('[CloudLevelService] Failed to approve level:', error);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -517,7 +518,7 @@ export class CloudLevelService {
|
||||
public async rejectLevel(id: string, notes: string): Promise<boolean> {
|
||||
const client = await SupabaseService.getInstance().getAuthenticatedClient();
|
||||
if (!client) {
|
||||
console.warn('[CloudLevelService] Not authenticated');
|
||||
log.warn('[CloudLevelService] Not authenticated');
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -527,7 +528,7 @@ export class CloudLevelService {
|
||||
});
|
||||
|
||||
if (error) {
|
||||
console.error('[CloudLevelService] Failed to reject level:', error);
|
||||
log.error('[CloudLevelService] Failed to reject level:', error);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -550,7 +551,7 @@ export class CloudLevelService {
|
||||
});
|
||||
|
||||
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) {
|
||||
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> {
|
||||
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;
|
||||
}
|
||||
|
||||
const supabaseService = SupabaseService.getInstance();
|
||||
const client = await supabaseService.getAuthenticatedClient();
|
||||
if (!client) {
|
||||
console.warn('[CloudLevelService] Not authenticated');
|
||||
log.warn('[CloudLevelService] Not authenticated');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get internal user ID (UUID)
|
||||
const internalUserId = await supabaseService.ensureUserExists();
|
||||
if (!internalUserId) {
|
||||
console.warn('[CloudLevelService] No internal user ID available');
|
||||
log.warn('[CloudLevelService] No internal user ID available');
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -604,7 +605,7 @@ export class CloudLevelService {
|
||||
});
|
||||
|
||||
if (error) {
|
||||
console.error('[CloudLevelService] Failed to rate level:', error);
|
||||
log.error('[CloudLevelService] Failed to rate level:', error);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import log from '../core/logger';
|
||||
|
||||
/**
|
||||
* Facebook Share Integration
|
||||
* Handles sharing game results to Facebook when user is authenticated via Facebook
|
||||
@ -37,7 +39,7 @@ export class FacebookShare {
|
||||
}
|
||||
|
||||
if (!this._appId) {
|
||||
console.warn('Facebook App ID not configured');
|
||||
log.warn('Facebook App ID not configured');
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -82,7 +84,7 @@ export class FacebookShare {
|
||||
};
|
||||
|
||||
script.onerror = () => {
|
||||
console.error('Failed to load Facebook SDK');
|
||||
log.error('Failed to load Facebook SDK');
|
||||
resolve(false);
|
||||
};
|
||||
|
||||
@ -103,13 +105,13 @@ export class FacebookShare {
|
||||
*/
|
||||
public async shareResults(shareData: ShareData): Promise<boolean> {
|
||||
if (!this._fbInitialized) {
|
||||
console.warn('Facebook SDK not initialized');
|
||||
log.warn('Facebook SDK not initialized');
|
||||
return false;
|
||||
}
|
||||
|
||||
const FB = (window as any).FB;
|
||||
if (!FB) {
|
||||
console.error('Facebook SDK not available');
|
||||
log.error('Facebook SDK not available');
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -126,10 +128,10 @@ export class FacebookShare {
|
||||
hashtag: '#SpaceCombatVR'
|
||||
}, (response: any) => {
|
||||
if (response && !response.error_message) {
|
||||
console.log('Successfully shared to Facebook');
|
||||
log.info('Successfully shared to Facebook');
|
||||
resolve(true);
|
||||
} 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);
|
||||
}
|
||||
});
|
||||
@ -142,7 +144,7 @@ export class FacebookShare {
|
||||
*/
|
||||
public async shareWithWebAPI(shareData: ShareData): Promise<boolean> {
|
||||
if (!navigator.share) {
|
||||
console.warn('Web Share API not supported');
|
||||
log.warn('Web Share API not supported');
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -158,7 +160,7 @@ export class FacebookShare {
|
||||
return true;
|
||||
} catch (error) {
|
||||
// User cancelled or error occurred
|
||||
console.log('Share cancelled or failed:', error);
|
||||
log.info('Share cancelled or failed:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -200,7 +202,7 @@ export class FacebookShare {
|
||||
await navigator.clipboard.writeText(message);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Failed to copy to clipboard:', error);
|
||||
log.error('Failed to copy to clipboard:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { AuthService } from './authService';
|
||||
import { CloudLeaderboardService } from './cloudLeaderboardService';
|
||||
import { GameStats } from '../game/gameStats';
|
||||
import debugLog from '../core/debug';
|
||||
import log from '../core/logger';
|
||||
|
||||
/**
|
||||
* Represents a completed game session result
|
||||
@ -53,13 +53,12 @@ export class GameResultsService {
|
||||
* Save a game result to storage (local + cloud)
|
||||
*/
|
||||
public saveResult(result: GameResult): void {
|
||||
console.log('[GameResultsService] saveResult called with:', result);
|
||||
log.info('[GameResultsService] saveResult called with:', result);
|
||||
const results = this.getAllResults();
|
||||
console.log('[GameResultsService] Existing results count:', results.length);
|
||||
log.info('[GameResultsService] Existing results count:', results.length);
|
||||
results.push(result);
|
||||
this.saveToStorage(results);
|
||||
console.log('[GameResultsService] Saved result:', result.id, result.finalScore);
|
||||
debugLog('[GameResultsService] Saved result:', result.id, result.finalScore);
|
||||
log.debug('[GameResultsService] Saved result:', result.id, result.finalScore);
|
||||
|
||||
// Submit to cloud leaderboard (non-blocking)
|
||||
this.submitToCloud(result);
|
||||
@ -74,14 +73,14 @@ export class GameResultsService {
|
||||
if (cloudService.isAvailable()) {
|
||||
const success = await cloudService.submitScore(result);
|
||||
if (success) {
|
||||
console.log('[GameResultsService] Cloud submission successful');
|
||||
log.info('[GameResultsService] Cloud submission successful');
|
||||
} else {
|
||||
console.log('[GameResultsService] Cloud submission skipped (not authenticated or failed)');
|
||||
log.info('[GameResultsService] Cloud submission skipped (not authenticated or failed)');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// 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[];
|
||||
} catch (error) {
|
||||
debugLog('[GameResultsService] Error loading results:', error);
|
||||
log.debug('[GameResultsService] Error loading results:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@ -116,7 +115,7 @@ export class GameResultsService {
|
||||
*/
|
||||
public clearAll(): void {
|
||||
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 {
|
||||
try {
|
||||
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);
|
||||
console.log('[GameResultsService] Successfully saved to localStorage');
|
||||
log.info('[GameResultsService] Successfully saved to localStorage');
|
||||
// Verify it was saved
|
||||
const verify = localStorage.getItem(STORAGE_KEY);
|
||||
console.log('[GameResultsService] Verification - stored data exists:', !!verify);
|
||||
log.info('[GameResultsService] Verification - stored data exists:', !!verify);
|
||||
} catch (error) {
|
||||
console.error('[GameResultsService] Error saving results:', error);
|
||||
debugLog('[GameResultsService] Error saving results:', error);
|
||||
log.error('[GameResultsService] Error saving results:', error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { createClient, SupabaseClient } from '@supabase/supabase-js';
|
||||
import { AuthService } from './authService';
|
||||
import log from '../core/logger';
|
||||
|
||||
const SUPABASE_URL = import.meta.env.VITE_SUPABASE_PROJECT;
|
||||
const SUPABASE_ANON_KEY = import.meta.env.VITE_SUPABASE_KEY;
|
||||
@ -17,7 +18,7 @@ export class SupabaseService {
|
||||
if (SUPABASE_URL && SUPABASE_ANON_KEY) {
|
||||
this._client = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
|
||||
} 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> {
|
||||
if (!SUPABASE_URL || !SUPABASE_ANON_KEY) {
|
||||
console.warn('[SupabaseService] Missing Supabase URL or key');
|
||||
log.warn('[SupabaseService] Missing Supabase URL or key');
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -59,16 +60,16 @@ export class SupabaseService {
|
||||
const token = await authService.getAccessToken();
|
||||
|
||||
if (!token) {
|
||||
console.warn('[SupabaseService] No auth token available');
|
||||
log.warn('[SupabaseService] No auth token available');
|
||||
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)
|
||||
try {
|
||||
const payload = JSON.parse(atob(token.split('.')[1]));
|
||||
console.log('[SupabaseService] Token claims:', {
|
||||
log.info('[SupabaseService] Token claims:', {
|
||||
iss: payload.iss,
|
||||
sub: payload.sub,
|
||||
aud: payload.aud,
|
||||
@ -76,7 +77,7 @@ export class SupabaseService {
|
||||
role: payload.role
|
||||
});
|
||||
} 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
|
||||
@ -105,7 +106,7 @@ export class SupabaseService {
|
||||
const authService = AuthService.getInstance();
|
||||
const user = authService.getUser();
|
||||
if (!user?.sub) {
|
||||
console.warn('[SupabaseService] No user sub available');
|
||||
log.warn('[SupabaseService] No user sub available');
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -132,7 +133,7 @@ export class SupabaseService {
|
||||
.single();
|
||||
|
||||
if (insertError) {
|
||||
console.error('[SupabaseService] Failed to create user:', insertError);
|
||||
log.error('[SupabaseService] Failed to create user:', insertError);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -140,7 +141,7 @@ export class SupabaseService {
|
||||
}
|
||||
|
||||
if (fetchError) {
|
||||
console.error('[SupabaseService] Failed to fetch user:', fetchError);
|
||||
log.error('[SupabaseService] Failed to fetch user:', fetchError);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@ -5,7 +5,7 @@ import {
|
||||
WebXRControllerComponent,
|
||||
WebXRInputSource,
|
||||
} from "@babylonjs/core";
|
||||
import debugLog from "../../core/debug";
|
||||
import log from "../../core/logger";
|
||||
import { ControllerMappingConfig, StickAction } from "./controllerMapping";
|
||||
|
||||
const controllerComponents = [
|
||||
@ -159,17 +159,17 @@ export class ControllerInput {
|
||||
* Add a VR controller to the input system
|
||||
*/
|
||||
public addController(controller: WebXRInputSource): void {
|
||||
debugLog(
|
||||
log.debug(
|
||||
"ControllerInput.addController called for:",
|
||||
controller.inputSource.handedness
|
||||
);
|
||||
|
||||
if (controller.inputSource.handedness === "left") {
|
||||
debugLog("Adding left controller");
|
||||
log.debug("Adding left controller");
|
||||
this._leftInputSource = controller;
|
||||
this._leftInputSource.onMotionControllerInitObservable.add(
|
||||
(motionController) => {
|
||||
debugLog(
|
||||
log.debug(
|
||||
"Left motion controller initialized:",
|
||||
motionController.handness
|
||||
);
|
||||
@ -179,17 +179,17 @@ export class ControllerInput {
|
||||
|
||||
// Check if motion controller is already initialized
|
||||
if (controller.motionController) {
|
||||
debugLog("Left motion controller already initialized, mapping now");
|
||||
log.debug("Left motion controller already initialized, mapping now");
|
||||
this.mapMotionController(controller.motionController);
|
||||
}
|
||||
}
|
||||
|
||||
if (controller.inputSource.handedness === "right") {
|
||||
debugLog("Adding right controller");
|
||||
log.debug("Adding right controller");
|
||||
this._rightInputSource = controller;
|
||||
this._rightInputSource.onMotionControllerInitObservable.add(
|
||||
(motionController) => {
|
||||
debugLog(
|
||||
log.debug(
|
||||
"Right motion controller initialized:",
|
||||
motionController.handness
|
||||
);
|
||||
@ -199,7 +199,7 @@ export class ControllerInput {
|
||||
|
||||
// Check if motion controller is already initialized
|
||||
if (controller.motionController) {
|
||||
debugLog("Right motion controller already initialized, mapping now");
|
||||
log.debug("Right motion controller already initialized, mapping now");
|
||||
this.mapMotionController(controller.motionController);
|
||||
}
|
||||
}
|
||||
@ -211,7 +211,7 @@ export class ControllerInput {
|
||||
private mapMotionController(
|
||||
controller: WebXRAbstractMotionController
|
||||
): void {
|
||||
debugLog(
|
||||
log.debug(
|
||||
"Mapping motion controller:",
|
||||
controller.handness,
|
||||
"Profile:",
|
||||
@ -222,13 +222,13 @@ export class ControllerInput {
|
||||
const comp = controller.components[component];
|
||||
|
||||
if (!comp) {
|
||||
debugLog(
|
||||
log.debug(
|
||||
` Component ${component} not found on ${controller.handness} controller`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
debugLog(
|
||||
log.debug(
|
||||
` Found component ${component} on ${controller.handness} controller`
|
||||
);
|
||||
const observable = this._controllerObservable;
|
||||
@ -329,7 +329,7 @@ export class ControllerInput {
|
||||
this._onStatusScreenToggleObservable.notifyObservers();
|
||||
}
|
||||
}
|
||||
console.log(controllerEvent);
|
||||
log.info(controllerEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import debugLog from '../../core/debug';
|
||||
import log from '../../core/logger';
|
||||
|
||||
const STORAGE_KEY = 'space-game-controller-mapping';
|
||||
|
||||
@ -107,7 +107,7 @@ export class ControllerMappingConfig {
|
||||
*/
|
||||
public setMapping(mapping: ControllerMapping): void {
|
||||
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 {
|
||||
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 {
|
||||
const json = JSON.stringify(this._mapping);
|
||||
localStorage.setItem(STORAGE_KEY, json);
|
||||
debugLog('[ControllerMapping] Saved to localStorage');
|
||||
log.debug('[ControllerMapping] Saved to localStorage');
|
||||
} 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,
|
||||
};
|
||||
|
||||
debugLog('[ControllerMapping] Loaded from localStorage:', this._mapping);
|
||||
log.debug('[ControllerMapping] Loaded from localStorage:', this._mapping);
|
||||
} else {
|
||||
debugLog('[ControllerMapping] No saved configuration, using defaults');
|
||||
log.debug('[ControllerMapping] No saved configuration, using defaults');
|
||||
}
|
||||
} 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 };
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Observable } from "@babylonjs/core";
|
||||
import { KeyboardInput } from "./keyboardInput";
|
||||
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
|
||||
@ -42,7 +42,7 @@ export class InputControlManager {
|
||||
* Private constructor for singleton pattern
|
||||
*/
|
||||
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)
|
||||
*/
|
||||
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._controllerInput = controller;
|
||||
}
|
||||
@ -68,7 +68,7 @@ export class InputControlManager {
|
||||
* Register XR pointer feature (called by main.ts during XR setup)
|
||||
*/
|
||||
public registerPointerFeature(pointerFeature: any): void {
|
||||
debugLog('[InputControlManager] Registering XR pointer feature');
|
||||
log.debug('[InputControlManager] Registering XR pointer feature');
|
||||
this._xrPointerFeature = pointerFeature;
|
||||
|
||||
// Apply current state to the newly registered pointer feature
|
||||
@ -79,7 +79,7 @@ export class InputControlManager {
|
||||
* Enable ship controls, disable pointer selection
|
||||
*/
|
||||
public enableShipControls(requester: string): void {
|
||||
debugLog(`[InputControlManager] Enabling ship controls (requester: ${requester})`);
|
||||
log.debug(`[InputControlManager] Enabling ship controls (requester: ${requester})`);
|
||||
|
||||
// Update state
|
||||
this._shipControlsEnabled = true;
|
||||
@ -104,7 +104,7 @@ export class InputControlManager {
|
||||
* Disable ship controls, enable pointer selection
|
||||
*/
|
||||
public disableShipControls(requester: string): void {
|
||||
debugLog(`[InputControlManager] Disabling ship controls (requester: ${requester})`);
|
||||
log.debug(`[InputControlManager] Disabling ship controls (requester: ${requester})`);
|
||||
|
||||
// Update state
|
||||
this._shipControlsEnabled = false;
|
||||
@ -119,12 +119,12 @@ export class InputControlManager {
|
||||
}
|
||||
|
||||
// Enable pointer selection
|
||||
console.log(`[InputControlManager] About to update pointer feature...`);
|
||||
log.info(`[InputControlManager] About to update pointer feature...`);
|
||||
this.updatePointerFeature();
|
||||
|
||||
// Emit state change event
|
||||
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) {
|
||||
// Enable pointer selection (attach feature)
|
||||
this._xrPointerFeature.attach();
|
||||
debugLog('[InputControlManager] Pointer selection enabled');
|
||||
log.debug('[InputControlManager] Pointer selection enabled');
|
||||
} else {
|
||||
// Disable pointer selection (detach feature)
|
||||
this._xrPointerFeature.detach();
|
||||
debugLog('[InputControlManager] Pointer selection disabled');
|
||||
log.debug('[InputControlManager] Pointer selection disabled');
|
||||
}
|
||||
} 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);
|
||||
|
||||
debugLog('[InputControlManager] State changed:', stateChange);
|
||||
log.debug('[InputControlManager] State changed:', stateChange);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -191,7 +191,7 @@ export class InputControlManager {
|
||||
* Cleanup (for testing or hot reload)
|
||||
*/
|
||||
public dispose(): void {
|
||||
debugLog('[InputControlManager] Disposing');
|
||||
log.debug('[InputControlManager] Disposing');
|
||||
this._onStateChangedObservable.clear();
|
||||
this._keyboardInput = null;
|
||||
this._controllerInput = null;
|
||||
|
||||
@ -17,7 +17,7 @@ import type { AudioEngineV2 } from "@babylonjs/core";
|
||||
import { DefaultScene } from "../core/defaultScene";
|
||||
import { GameConfig } from "../core/gameConfig";
|
||||
import { Sight } from "./sight";
|
||||
import debugLog from "../core/debug";
|
||||
import log from "../core/logger";
|
||||
import { Scoreboard } from "../ui/hud/scoreboard";
|
||||
import loadAsset from "../utils/loadAsset";
|
||||
import { KeyboardInput } from "./input/keyboardInput";
|
||||
@ -107,7 +107,7 @@ export class Ship {
|
||||
*/
|
||||
public startGameplay(): void {
|
||||
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> {
|
||||
@ -164,7 +164,7 @@ export class Ship {
|
||||
// Create physics if enabled
|
||||
const config = GameConfig.getInstance();
|
||||
if (config.physicsEnabled) {
|
||||
console.log("Physics Enabled for Ship");
|
||||
log.info("Physics Enabled for Ship");
|
||||
if (this._ship) {
|
||||
const agg = new PhysicsAggregate(
|
||||
this._ship,
|
||||
@ -184,9 +184,9 @@ export class Ship {
|
||||
|
||||
// Debug: Log center of mass before override
|
||||
const massProps = agg.body.getMassProperties();
|
||||
console.log(`[Ship] Original center of mass (local): ${massProps.centerOfMass.toString()}`);
|
||||
console.log(`[Ship] Mass: ${massProps.mass}`);
|
||||
console.log(`[Ship] Inertia: ${massProps.inertia.toString()}`);
|
||||
log.info(`[Ship] Original center of mass (local): ${massProps.centerOfMass.toString()}`);
|
||||
log.info(`[Ship] Mass: ${massProps.mass}`);
|
||||
log.info(`[Ship] Inertia: ${massProps.inertia.toString()}`);
|
||||
|
||||
// Override center of mass to origin to prevent thrust from causing torque
|
||||
// (mesh-based physics was calculating offset center of mass from geometry)
|
||||
@ -197,7 +197,7 @@ export class Ship {
|
||||
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
|
||||
// (disabling sleep prevents abrupt stops at zero linear velocity)
|
||||
@ -243,7 +243,7 @@ export class Ship {
|
||||
// Apply damage if above minimum threshold
|
||||
if (this._scoreboard?.shipStatus && damage > 0.001) {
|
||||
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
|
||||
if (this._audio) {
|
||||
@ -253,7 +253,7 @@ export class Ship {
|
||||
}
|
||||
});
|
||||
} 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) {
|
||||
DefaultScene.MainScene.activeCamera = this._camera;
|
||||
//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
|
||||
@ -394,7 +394,7 @@ export class Ship {
|
||||
}, { sampleRate: 0.2 }); // Sample 20% of asteroid events to reduce data
|
||||
} catch (error) {
|
||||
// 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
|
||||
});
|
||||
} 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
|
||||
*/
|
||||
private handleReplayRequest(): void {
|
||||
debugLog('Replay button clicked - notifying observers');
|
||||
log.debug('Replay button clicked - notifying observers');
|
||||
this.onReplayRequestObservable.notifyObservers();
|
||||
}
|
||||
|
||||
@ -444,7 +444,7 @@ export class Ship {
|
||||
* Handle exit VR button click from status screen
|
||||
*/
|
||||
private async handleExitVR(): Promise<void> {
|
||||
debugLog('Exit VR button clicked - navigating to home');
|
||||
log.debug('Exit VR button clicked - navigating to home');
|
||||
|
||||
try {
|
||||
// Ensure the app UI is visible before navigating (safety net)
|
||||
@ -463,7 +463,7 @@ export class Ship {
|
||||
const { navigate } = await import('svelte-routing');
|
||||
navigate('/', { replace: true });
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
@ -472,7 +472,7 @@ export class Ship {
|
||||
* Handle resume button click from status screen
|
||||
*/
|
||||
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
|
||||
this._statusScreen.hide();
|
||||
}
|
||||
@ -481,7 +481,7 @@ export class Ship {
|
||||
* Handle next level button click from status screen
|
||||
*/
|
||||
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)
|
||||
window.location.hash = '#/';
|
||||
window.location.reload();
|
||||
@ -521,7 +521,7 @@ export class Ship {
|
||||
|
||||
// Check condition 1: Death by hull damage (outside landing zone)
|
||||
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
|
||||
// InputControlManager will handle disabling controls when status screen shows
|
||||
this._statusScreenAutoShown = true;
|
||||
@ -530,7 +530,7 @@ export class Ship {
|
||||
|
||||
// Check condition 2: Stranded (outside landing zone, no fuel, low velocity)
|
||||
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
|
||||
// InputControlManager will handle disabling controls when status screen shows
|
||||
this._statusScreenAutoShown = true;
|
||||
@ -540,7 +540,7 @@ export class Ship {
|
||||
// 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)
|
||||
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!
|
||||
// InputControlManager will handle disabling controls when status screen shows
|
||||
this._statusScreenAutoShown = true;
|
||||
@ -633,9 +633,9 @@ export class Ship {
|
||||
|
||||
// Log zone transitions
|
||||
if (this._isInLandingZone && !wasInZone) {
|
||||
debugLog("Ship entered landing zone - resupply active");
|
||||
log.debug("Ship entered landing zone - resupply active");
|
||||
} 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
|
||||
@ -669,7 +669,7 @@ export class Ship {
|
||||
private handleShoot(): void {
|
||||
// If controls are disabled, fire mission brief trigger observable instead of shooting
|
||||
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();
|
||||
return;
|
||||
}
|
||||
@ -703,7 +703,7 @@ export class Ship {
|
||||
landingAggregate.body.getCollisionObservable().add((collisionEvent) => {
|
||||
// Check if the collision is with our ship
|
||||
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
|
||||
*/
|
||||
public addController(controller: WebXRInputSource) {
|
||||
debugLog(
|
||||
log.debug(
|
||||
"Ship.addController called for:",
|
||||
controller.inputSource.handedness
|
||||
);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { AudioEngineV2, StaticSound, SoundState } from "@babylonjs/core";
|
||||
import debugLog from "../core/debug";
|
||||
import log from "../core/logger";
|
||||
import { ShipStatus, ShipStatusChangeEvent } from "./shipStatus";
|
||||
|
||||
/**
|
||||
@ -66,7 +66,7 @@ export class VoiceAudioSystem {
|
||||
public async initialize(audioEngine: AudioEngineV2): Promise<void> {
|
||||
this._audioEngine = audioEngine;
|
||||
|
||||
debugLog('VoiceAudioSystem: Loading voice clips...');
|
||||
log.debug('VoiceAudioSystem: Loading voice clips...');
|
||||
|
||||
// Load all voice files as non-spatial sounds
|
||||
for (const fileName of this.VOICE_FILES) {
|
||||
@ -82,11 +82,11 @@ export class VoiceAudioSystem {
|
||||
);
|
||||
this._sounds.set(fileName, sound);
|
||||
} 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);
|
||||
});
|
||||
|
||||
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 maxValue = 1;
|
||||
const percentage = maxValue > 0 ? newValue / maxValue : 0;
|
||||
debugLog(event);
|
||||
log.debug(event);
|
||||
|
||||
// Clear warning states if resources increase above thresholds
|
||||
if (delta > 0) {
|
||||
@ -126,7 +126,7 @@ export class VoiceAudioSystem {
|
||||
|
||||
|
||||
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}`);
|
||||
// Clear warning state if it exists (danger supersedes warning)
|
||||
this.clearWarningState(`warning_${statusType}`);
|
||||
@ -134,13 +134,13 @@ export class VoiceAudioSystem {
|
||||
}
|
||||
// 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}`)) {
|
||||
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.queueMessage(['warning', statusType], VoiceMessagePriority.NORMAL, false, 4000, `warning_${statusType}`);
|
||||
}
|
||||
// Empty (= 0) - no repeat
|
||||
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.queueMessage([statusType, 'empty'], VoiceMessagePriority.HIGH, false, 0, `empty_${statusType}`);
|
||||
}
|
||||
@ -158,7 +158,7 @@ export class VoiceAudioSystem {
|
||||
stateKey?: string
|
||||
): void {
|
||||
if (!this._audioEngine) {
|
||||
debugLog('VoiceAudioSystem: Cannot queue message - audio not initialized');
|
||||
log.debug('VoiceAudioSystem: Cannot queue message - audio not initialized');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -185,7 +185,7 @@ export class VoiceAudioSystem {
|
||||
}
|
||||
|
||||
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();
|
||||
} else {
|
||||
// Sequence complete
|
||||
debugLog('VoiceAudioSystem: Sequence complete');
|
||||
log.debug('VoiceAudioSystem: Sequence complete');
|
||||
|
||||
// Check if this message should repeat
|
||||
if (this._currentMessage.repeatInterval && this._currentMessage.repeatInterval > 0) {
|
||||
@ -233,9 +233,9 @@ export class VoiceAudioSystem {
|
||||
|
||||
// Re-queue the message for repeat
|
||||
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 {
|
||||
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._currentSoundIndex = 0;
|
||||
this._isPlaying = true;
|
||||
debugLog(`VoiceAudioSystem: Starting sequence [${this._currentMessage.sounds.join(' → ')}]`);
|
||||
log.debug(`VoiceAudioSystem: Starting sequence [${this._currentMessage.sounds.join(' → ')}]`);
|
||||
this.playCurrentSound();
|
||||
}
|
||||
}
|
||||
@ -283,9 +283,9 @@ export class VoiceAudioSystem {
|
||||
|
||||
if (sound) {
|
||||
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 {
|
||||
debugLog(`VoiceAudioSystem: Sound ${soundName} not found, skipping`);
|
||||
log.debug(`VoiceAudioSystem: Sound ${soundName} not found, skipping`);
|
||||
// Skip to next sound
|
||||
this._currentSoundIndex++;
|
||||
}
|
||||
@ -314,7 +314,7 @@ export class VoiceAudioSystem {
|
||||
*/
|
||||
public clearQueue(): void {
|
||||
this._queue = [];
|
||||
debugLog('VoiceAudioSystem: Queue cleared');
|
||||
log.debug('VoiceAudioSystem: Queue cleared');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -330,9 +330,9 @@ export class VoiceAudioSystem {
|
||||
|
||||
const removed = originalLength - this._queue.length;
|
||||
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 {
|
||||
debugLog(`VoiceAudioSystem: Cleared warning state '${key}'`);
|
||||
log.debug(`VoiceAudioSystem: Cleared warning state '${key}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -342,7 +342,7 @@ export class VoiceAudioSystem {
|
||||
*/
|
||||
public resetWarningStates(): void {
|
||||
this._warningStates.clear();
|
||||
debugLog('VoiceAudioSystem: Warning states reset');
|
||||
log.debug('VoiceAudioSystem: Warning states reset');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -353,6 +353,6 @@ export class VoiceAudioSystem {
|
||||
this.clearQueue();
|
||||
this._sounds.clear();
|
||||
this._warningStates.clear();
|
||||
debugLog('VoiceAudioSystem: Disposed');
|
||||
log.debug('VoiceAudioSystem: Disposed');
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { writable } from 'svelte/store';
|
||||
import { AuthService } from '../services/authService';
|
||||
import log from '../core/logger';
|
||||
|
||||
interface AuthState {
|
||||
isAuthenticated: boolean;
|
||||
@ -18,34 +19,34 @@ function createAuthStore() {
|
||||
|
||||
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
|
||||
(async () => {
|
||||
console.log('[AuthStore] Checking initial auth state...');
|
||||
log.info('[AuthStore] Checking initial auth state...');
|
||||
const isAuth = await authService.isAuthenticated();
|
||||
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 });
|
||||
})();
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
login: async () => {
|
||||
console.log('[AuthStore] login() called');
|
||||
log.info('[AuthStore] login() called');
|
||||
await authService.login();
|
||||
// After redirect, page will reload and auth state will be refreshed
|
||||
},
|
||||
logout: async () => {
|
||||
console.log('[AuthStore] logout() called');
|
||||
log.info('[AuthStore] logout() called');
|
||||
await authService.logout();
|
||||
// After logout redirect, page will reload
|
||||
},
|
||||
refresh: async () => {
|
||||
console.log('[AuthStore] refresh() called');
|
||||
log.info('[AuthStore] refresh() called');
|
||||
const isAuth = await authService.isAuthenticated();
|
||||
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 }));
|
||||
},
|
||||
};
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { writable, get } from 'svelte/store';
|
||||
import type { ControllerMapping } from '../ship/input/controllerMapping';
|
||||
import { ControllerMappingConfig } from '../ship/input/controllerMapping';
|
||||
import log from '../core/logger';
|
||||
|
||||
const _STORAGE_KEY = 'space-game-controller-mapping';
|
||||
|
||||
@ -21,13 +22,13 @@ function createControllerMappingStore() {
|
||||
const mapping = get(controllerMappingStore);
|
||||
config.setMapping(mapping);
|
||||
config.save();
|
||||
console.log('[ControllerMapping Store] Saved');
|
||||
log.info('[ControllerMapping Store] Saved');
|
||||
},
|
||||
reset: () => {
|
||||
config.resetToDefault();
|
||||
config.save();
|
||||
set(config.getMapping());
|
||||
console.log('[ControllerMapping Store] Reset to defaults');
|
||||
log.info('[ControllerMapping Store] Reset to defaults');
|
||||
},
|
||||
validate: () => {
|
||||
return config.validate();
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { writable, get } from 'svelte/store';
|
||||
import log from '../core/logger';
|
||||
|
||||
const STORAGE_KEY = 'game-config';
|
||||
|
||||
@ -34,7 +35,7 @@ function loadFromStorage(): GameConfigData {
|
||||
return { ...defaultConfig, ...parsed };
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('[GameConfig Store] Failed to load from localStorage:', error);
|
||||
log.warn('[GameConfig Store] Failed to load from localStorage:', error);
|
||||
}
|
||||
return { ...defaultConfig };
|
||||
}
|
||||
@ -51,18 +52,18 @@ function createGameConfigStore() {
|
||||
const config = get(gameConfigStore);
|
||||
try {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(config));
|
||||
console.log('[GameConfig Store] Saved to localStorage');
|
||||
log.info('[GameConfig Store] Saved to localStorage');
|
||||
} catch (error) {
|
||||
console.error('[GameConfig Store] Failed to save:', error);
|
||||
log.error('[GameConfig Store] Failed to save:', error);
|
||||
}
|
||||
},
|
||||
reset: () => {
|
||||
set({ ...defaultConfig });
|
||||
try {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(defaultConfig));
|
||||
console.log('[GameConfig Store] Reset to defaults');
|
||||
log.info('[GameConfig Store] Reset to defaults');
|
||||
} catch (error) {
|
||||
console.error('[GameConfig Store] Failed to save defaults:', error);
|
||||
log.error('[GameConfig Store] Failed to save defaults:', error);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@ -2,6 +2,7 @@ import { writable } from 'svelte/store';
|
||||
import { LevelRegistry } from '../levels/storage/levelRegistry';
|
||||
import type { LevelConfig } from '../levels/config/levelConfig';
|
||||
import type { CloudLevelEntry } from '../services/cloudLevelService';
|
||||
import log from '../core/logger';
|
||||
|
||||
interface LevelRegistryState {
|
||||
isInitialized: boolean;
|
||||
@ -28,7 +29,7 @@ function createLevelRegistryStore() {
|
||||
levels: registry.getAllLevels(),
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('[LevelRegistryStore] Failed to initialize:', error);
|
||||
log.error('[LevelRegistryStore] Failed to initialize:', error);
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ import {
|
||||
} from "@babylonjs/gui";
|
||||
import { DefaultScene } from "../../core/defaultScene";
|
||||
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 { CloudLevelEntry } from "../../services/cloudLevelService";
|
||||
|
||||
@ -27,20 +27,20 @@ export class MissionBrief {
|
||||
* Initialize the mission brief as a fullscreen overlay
|
||||
*/
|
||||
public initialize(): void {
|
||||
console.log('[MissionBrief] ========== INITIALIZE CALLED ==========');
|
||||
log.info('[MissionBrief] ========== INITIALIZE CALLED ==========');
|
||||
const scene = DefaultScene.MainScene;
|
||||
console.log('[MissionBrief] Scene exists:', !!scene);
|
||||
log.info('[MissionBrief] Scene exists:', !!scene);
|
||||
|
||||
try {
|
||||
console.log('[MissionBrief] Initializing as fullscreen overlay');
|
||||
log.info('[MissionBrief] Initializing as fullscreen overlay');
|
||||
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');
|
||||
console.log('[MissionBrief] Ship node found:', !!ship);
|
||||
log.info('[MissionBrief] Ship node found:', !!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;
|
||||
}
|
||||
|
||||
@ -49,18 +49,18 @@ export class MissionBrief {
|
||||
mesh.rotation = new Vector3(0, 0, 0);
|
||||
mesh.renderingGroupId = 3; // Same as status screen for consistent rendering
|
||||
mesh.metadata = { uiPickable: true }; // TAG: VR UI - allow pointer selection
|
||||
console.log('[MissionBrief] Mesh parented to ship at position:', mesh.position);
|
||||
console.log('[MissionBrief] Mesh absolute position:', mesh.getAbsolutePosition());
|
||||
console.log('[MissionBrief] Mesh scaling:', mesh.scaling);
|
||||
console.log('[MissionBrief] Mesh isEnabled:', mesh.isEnabled());
|
||||
console.log('[MissionBrief] Mesh isVisible:', mesh.isVisible);
|
||||
log.info('[MissionBrief] Mesh parented to ship at position:', mesh.position);
|
||||
log.info('[MissionBrief] Mesh absolute position:', mesh.getAbsolutePosition());
|
||||
log.info('[MissionBrief] Mesh scaling:', mesh.scaling);
|
||||
log.info('[MissionBrief] Mesh isEnabled:', mesh.isEnabled());
|
||||
log.info('[MissionBrief] Mesh isVisible:', mesh.isVisible);
|
||||
|
||||
// Create fullscreen advanced texture (not attached to mesh)
|
||||
this._advancedTexture = AdvancedDynamicTexture.CreateForMesh(mesh);
|
||||
console.log('[MissionBrief] AdvancedDynamicTexture created for mesh');
|
||||
console.log('[MissionBrief] Texture dimensions:', this._advancedTexture.getSize());
|
||||
log.info('[MissionBrief] AdvancedDynamicTexture created for mesh');
|
||||
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
|
||||
this._container = new Rectangle("missionBriefContainer");
|
||||
@ -73,16 +73,16 @@ export class MissionBrief {
|
||||
this._container.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
|
||||
this._container.verticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
|
||||
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
|
||||
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) {
|
||||
console.error('[MissionBrief] !!!!! INITIALIZATION FAILED !!!!!', error);
|
||||
console.error('[MissionBrief] Error stack:', error?.stack);
|
||||
log.error('[MissionBrief] !!!!! INITIALIZATION FAILED !!!!!', error);
|
||||
log.error('[MissionBrief] Error stack:', error?.stack);
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,18 +94,18 @@ export class MissionBrief {
|
||||
* @param onStart - Callback when start button is pressed
|
||||
*/
|
||||
public show(levelConfig: LevelConfig, directoryEntry: CloudLevelEntry | null, triggerObservable: Observable<void>, onStart: () => void): void {
|
||||
console.log('[MissionBrief] ========== SHOW() CALLED ==========');
|
||||
console.log('[MissionBrief] Container exists:', !!this._container);
|
||||
console.log('[MissionBrief] AdvancedTexture exists:', !!this._advancedTexture);
|
||||
log.info('[MissionBrief] ========== SHOW() CALLED ==========');
|
||||
log.info('[MissionBrief] Container exists:', !!this._container);
|
||||
log.info('[MissionBrief] AdvancedTexture exists:', !!this._advancedTexture);
|
||||
|
||||
if (!this._container || !this._advancedTexture) {
|
||||
console.error('[MissionBrief] !!!!! CANNOT SHOW - NOT INITIALIZED !!!!!');
|
||||
console.error('[MissionBrief] Container:', this._container);
|
||||
console.error('[MissionBrief] AdvancedTexture:', this._advancedTexture);
|
||||
log.error('[MissionBrief] !!!!! CANNOT SHOW - NOT INITIALIZED !!!!!');
|
||||
log.error('[MissionBrief] Container:', this._container);
|
||||
log.error('[MissionBrief] AdvancedTexture:', this._advancedTexture);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[MissionBrief] Showing with config:', {
|
||||
log.info('[MissionBrief] Showing with config:', {
|
||||
difficulty: levelConfig.difficulty,
|
||||
description: levelConfig.metadata?.description,
|
||||
asteroidCount: levelConfig.asteroids?.length,
|
||||
@ -117,7 +117,7 @@ export class MissionBrief {
|
||||
|
||||
// Listen for trigger pulls to dismiss the mission brief
|
||||
this._triggerObserver = triggerObservable.add(() => {
|
||||
debugLog('[MissionBrief] Trigger pulled - dismissing mission brief');
|
||||
log.debug('[MissionBrief] Trigger pulled - dismissing mission brief');
|
||||
this.hide();
|
||||
if (this._onStartCallback) {
|
||||
this._onStartCallback();
|
||||
@ -215,7 +215,7 @@ export class MissionBrief {
|
||||
startButton.fontWeight = "bold";
|
||||
startButton.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
|
||||
startButton.onPointerClickObservable.add(() => {
|
||||
debugLog('[MissionBrief] START button clicked - dismissing mission brief');
|
||||
log.debug('[MissionBrief] START button clicked - dismissing mission brief');
|
||||
this.hide();
|
||||
if (this._onStartCallback) {
|
||||
this._onStartCallback();
|
||||
@ -232,12 +232,12 @@ export class MissionBrief {
|
||||
this._container.isVisible = true;
|
||||
this._isVisible = true;
|
||||
|
||||
console.log('[MissionBrief] ========== CONTAINER NOW VISIBLE ==========');
|
||||
console.log('[MissionBrief] Container.isVisible:', this._container.isVisible);
|
||||
console.log('[MissionBrief] _isVisible flag:', this._isVisible);
|
||||
console.log('[MissionBrief] Container children count:', this._container.children.length);
|
||||
console.log('[MissionBrief] AdvancedTexture control count:', this._advancedTexture.rootContainer.children.length);
|
||||
console.log('[MissionBrief] ========== MISSION BRIEF DISPLAY COMPLETE ==========');
|
||||
log.info('[MissionBrief] ========== CONTAINER NOW VISIBLE ==========');
|
||||
log.info('[MissionBrief] Container.isVisible:', this._container.isVisible);
|
||||
log.info('[MissionBrief] _isVisible flag:', this._isVisible);
|
||||
log.info('[MissionBrief] Container children count:', this._container.children.length);
|
||||
log.info('[MissionBrief] AdvancedTexture control count:', this._advancedTexture.rootContainer.children.length);
|
||||
log.info('[MissionBrief] ========== MISSION BRIEF DISPLAY COMPLETE ==========');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -247,7 +247,7 @@ export class MissionBrief {
|
||||
if (this._container) {
|
||||
this._container.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._triggerObserver = null;
|
||||
this._isVisible = false;
|
||||
debugLog('[MissionBrief] Disposed');
|
||||
log.debug('[MissionBrief] Disposed');
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ import {
|
||||
Observable,
|
||||
Vector3,
|
||||
} from "@babylonjs/core";
|
||||
import debugLog from '../../core/debug';
|
||||
import log from '../../core/logger';
|
||||
import { ShipStatus } from '../../ship/shipStatus';
|
||||
|
||||
export type ScoreEvent = {
|
||||
@ -101,8 +101,8 @@ export class Scoreboard {
|
||||
const scene = DefaultScene.MainScene;
|
||||
|
||||
const parent = scene.getNodeById('ship');
|
||||
debugLog('Scoreboard parent:', parent);
|
||||
debugLog('Initializing scoreboard');
|
||||
log.debug('Scoreboard parent:', parent);
|
||||
log.debug('Initializing scoreboard');
|
||||
let scoreboard: Mesh | null = null;
|
||||
|
||||
// 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
|
||||
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.parent = parent;
|
||||
|
||||
@ -249,7 +249,7 @@ export class Scoreboard {
|
||||
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
|
||||
const panel = new StackPanel('GaugesPanel');
|
||||
@ -274,7 +274,7 @@ export class Scoreboard {
|
||||
this._shipStatus.setHull(1);
|
||||
this._shipStatus.setAmmo(1);
|
||||
|
||||
debugLog('Gauges display created with initial test values');
|
||||
log.debug('Gauges display created with initial test values');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -22,7 +22,7 @@ import { FacebookShare, ShareData } from "../../services/facebookShare";
|
||||
import { InputControlManager } from "../../ship/input/inputControlManager";
|
||||
import { formatStars } from "../../game/scoreCalculator";
|
||||
import { GameResultsService } from "../../services/gameResultsService";
|
||||
import debugLog from "../../core/debug";
|
||||
import log from "../../core/logger";
|
||||
|
||||
/**
|
||||
* Status screen that displays game statistics
|
||||
@ -374,7 +374,7 @@ export class StatusScreen {
|
||||
* Set the current level info for progression tracking and results
|
||||
*/
|
||||
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._currentLevelName = levelName;
|
||||
this._totalAsteroids = totalAsteroids;
|
||||
@ -445,7 +445,7 @@ export class StatusScreen {
|
||||
if (this._shareButton.isVisible) {
|
||||
const fbShare = FacebookShare.getInstance();
|
||||
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);
|
||||
if (copied) {
|
||||
// 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
|
||||
if (this._shareButton) {
|
||||
@ -605,8 +605,8 @@ export class StatusScreen {
|
||||
* Record game result to the results service
|
||||
*/
|
||||
private recordGameResult(endReason: 'victory' | 'death' | 'stranded'): void {
|
||||
console.log('[StatusScreen] recordGameResult called with endReason:', endReason);
|
||||
console.log('[StatusScreen] Level info:', {
|
||||
log.info('[StatusScreen] recordGameResult called with endReason:', endReason);
|
||||
log.info('[StatusScreen] Level info:', {
|
||||
levelId: this._currentLevelId,
|
||||
levelName: this._currentLevelName,
|
||||
totalAsteroids: this._totalAsteroids,
|
||||
@ -615,8 +615,8 @@ export class StatusScreen {
|
||||
|
||||
// Only record if we have level info
|
||||
if (!this._currentLevelId || !this._currentLevelName) {
|
||||
console.warn('[StatusScreen] Cannot record result - missing level info');
|
||||
debugLog('[StatusScreen] Cannot record result - missing level info');
|
||||
log.warn('[StatusScreen] Cannot record result - missing level info');
|
||||
log.debug('[StatusScreen] Cannot record result - missing level info');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -630,15 +630,15 @@ export class StatusScreen {
|
||||
this._parTime
|
||||
);
|
||||
|
||||
console.log('[StatusScreen] Built result:', result);
|
||||
log.info('[StatusScreen] Built result:', result);
|
||||
|
||||
const service = GameResultsService.getInstance();
|
||||
service.saveResult(result);
|
||||
console.log('[StatusScreen] Game result saved successfully');
|
||||
debugLog('[StatusScreen] Game result recorded:', result.id, result.finalScore, result.endReason);
|
||||
log.info('[StatusScreen] Game result saved successfully');
|
||||
log.debug('[StatusScreen] Game result recorded:', result.id, result.finalScore, result.endReason);
|
||||
} catch (error) {
|
||||
console.error('[StatusScreen] Failed to record game result:', error);
|
||||
debugLog('[StatusScreen] Failed to record game result:', error);
|
||||
log.error('[StatusScreen] Failed to record game result:', error);
|
||||
log.debug('[StatusScreen] Failed to record game result:', error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -14,9 +14,6 @@ export class Preloader {
|
||||
}
|
||||
|
||||
private createUI(): void {
|
||||
const levelSelect = document.getElementById('levelSelect');
|
||||
if (!levelSelect) return;
|
||||
|
||||
// Create preloader container
|
||||
this.container = document.createElement('div');
|
||||
this.container.className = 'preloader';
|
||||
@ -45,7 +42,8 @@ export class Preloader {
|
||||
</div>
|
||||
`;
|
||||
|
||||
levelSelect.appendChild(this.container);
|
||||
// Append to body so it's visible even when other UI elements are hidden
|
||||
document.body.appendChild(this.container);
|
||||
|
||||
// Get references
|
||||
this.progressBar = document.getElementById('preloaderProgress');
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import {DefaultScene} from "../core/defaultScene";
|
||||
import {AbstractMesh, AssetContainer, LoadAssetContainerAsync} from "@babylonjs/core";
|
||||
import debugLog from "../core/debug";
|
||||
import log from "../core/logger";
|
||||
|
||||
type LoadedAsset = {
|
||||
container: AssetContainer,
|
||||
@ -8,23 +8,23 @@ type LoadedAsset = {
|
||||
}
|
||||
export default async function loadAsset(file: string, theme: string = "default"): Promise<LoadedAsset> {
|
||||
const assetPath = `/assets/themes/${theme}/models/${file}`;
|
||||
debugLog(`[loadAsset] Loading: ${assetPath}`);
|
||||
log.debug(`[loadAsset] Loading: ${assetPath}`);
|
||||
|
||||
try {
|
||||
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();
|
||||
container.addAllToScene();
|
||||
|
||||
debugLog(`[loadAsset] Root nodes count: ${container.rootNodes.length}`);
|
||||
log.debug(`[loadAsset] Root nodes count: ${container.rootNodes.length}`);
|
||||
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};
|
||||
}
|
||||
|
||||
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
|
||||
mesh.isVisible = true;
|
||||
mesh.setEnabled(true);
|
||||
@ -42,10 +42,10 @@ export default async function loadAsset(file: string, theme: string = "default")
|
||||
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};
|
||||
} catch (error) {
|
||||
console.error(`[loadAsset] FAILED to load ${assetPath}:`, error);
|
||||
log.error(`[loadAsset] FAILED to load ${assetPath}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user