diff --git a/src/level1.ts b/src/level1.ts index 93fd111..05b8e56 100644 --- a/src/level1.ts +++ b/src/level1.ts @@ -109,6 +109,10 @@ export class Level1 implements Level { this._startBase = entities.startBase; // sun and planets are already created by deserializer + // Initialize scoreboard with total asteroid count + this._scoreboard.setRemainingCount(entities.asteroids.length); + console.log(`Initialized scoreboard with ${entities.asteroids.length} asteroids`); + // Position ship from config const shipConfig = this._deserializer.getShipConfig(); this._ship.position = new Vector3(shipConfig.position[0], shipConfig.position[1], shipConfig.position[2]); diff --git a/src/levelDeserializer.ts b/src/levelDeserializer.ts index f64febd..c0d7b86 100644 --- a/src/levelDeserializer.ts +++ b/src/levelDeserializer.ts @@ -201,13 +201,6 @@ export class LevelDeserializer { if (mesh) { asteroids.push(mesh); } - - // Notify scoreboard of asteroid count - scoreObservable.notifyObservers({ - score: 0, - remaining: i + 1, - message: "Loading from config" - }); } console.log(`Created ${asteroids.length} asteroids from config`); diff --git a/src/levelEditor.ts b/src/levelEditor.ts index 3bf7eb2..29cfd0d 100644 --- a/src/levelEditor.ts +++ b/src/levelEditor.ts @@ -566,129 +566,11 @@ class LevelEditor { /** * Custom level generator that allows overriding default values + * Simply extends LevelGenerator - all properties are now public on the base class */ class CustomLevelGenerator extends LevelGenerator { - public shipPosition: Vector3Array = [0, 1, 0]; - public startBasePosition: Vector3Array = [0, 0, 0]; - public startBaseDiameter = 10; - public startBaseHeight = 1; - public sunPosition: Vector3Array = [0, 0, 400]; - public sunDiameter = 50; - public planetCount = 12; - public planetMinDiameter = 100; - public planetMaxDiameter = 200; - public planetMinDistance = 1000; - public planetMaxDistance = 2000; - - private customDifficultyConfig: DifficultyConfig | null = null; - - public setDifficultyConfig(config: DifficultyConfig) { - this.customDifficultyConfig = config; - } - - public generate(): LevelConfig { - const config = super.generate(); - - // Override with custom values - config.ship.position = [...this.shipPosition]; - config.startBase.position = [...this.startBasePosition]; - config.startBase.diameter = this.startBaseDiameter; - config.startBase.height = this.startBaseHeight; - config.sun.position = [...this.sunPosition]; - config.sun.diameter = this.sunDiameter; - - // Regenerate planets with custom params - if (this.planetCount !== 12 || - this.planetMinDiameter !== 100 || - this.planetMaxDiameter !== 200 || - this.planetMinDistance !== 1000 || - this.planetMaxDistance !== 2000) { - config.planets = this.generateCustomPlanets(); - } - - // Regenerate asteroids with custom params if provided - if (this.customDifficultyConfig) { - config.asteroids = this.generateCustomAsteroids(this.customDifficultyConfig); - config.difficultyConfig = this.customDifficultyConfig; - } - - return config; - } - - private generateCustomPlanets() { - const planets = []; - const sunPosition = this.sunPosition; - - for (let i = 0; i < this.planetCount; i++) { - const diameter = this.planetMinDiameter + - Math.random() * (this.planetMaxDiameter - this.planetMinDiameter); - - const distance = this.planetMinDistance + - Math.random() * (this.planetMaxDistance - this.planetMinDistance); - - const angle = Math.random() * Math.PI * 2; - const y = (Math.random() - 0.5) * 100; - - const position: Vector3Array = [ - sunPosition[0] + distance * Math.cos(angle), - sunPosition[1] + y, - sunPosition[2] + distance * Math.sin(angle) - ]; - - planets.push({ - name: `planet-${i}`, - position, - diameter, - texturePath: this.getRandomPlanetTexture(), - rotation: [0, 0, 0] as Vector3Array - }); - } - - return planets; - } - - private generateCustomAsteroids(config: DifficultyConfig) { - const asteroids = []; - - for (let i = 0; i < config.rockCount; i++) { - const distRange = config.distanceMax - config.distanceMin; - const dist = (Math.random() * distRange) + config.distanceMin; - - const position: Vector3Array = [0, 1, dist]; - - const sizeRange = config.rockSizeMax - config.rockSizeMin; - const size = Math.random() * sizeRange + config.rockSizeMin; - const scaling: Vector3Array = [size, size, size]; - - const forceMagnitude = 50000000 * config.forceMultiplier; - const mass = 10000; - const velocityMagnitude = forceMagnitude / mass / 100; - - const linearVelocity: Vector3Array = [velocityMagnitude, 0, 0]; - - asteroids.push({ - id: `asteroid-${i}`, - position, - scaling, - linearVelocity, - angularVelocity: [0, 0, 0] as Vector3Array, - mass - }); - } - - return asteroids; - } - - private getRandomPlanetTexture(): string { - // Simple inline implementation to avoid circular dependency - const textures = [ - "/planetTextures/Arid/Arid_01-512x512.png", - "/planetTextures/Barren/Barren_01-512x512.png", - "/planetTextures/Gaseous/Gaseous_01-512x512.png", - "/planetTextures/Grassland/Grassland_01-512x512.png" - ]; - return textures[Math.floor(Math.random() * textures.length)]; - } + // No need to duplicate anything - just use the public properties from base class + // Properties like shipPosition, startBasePosition, etc. are already defined and public in LevelGenerator } // Initialize the editor when this module is loaded diff --git a/src/levelGenerator.ts b/src/levelGenerator.ts index 88372fc..5c212fa 100644 --- a/src/levelGenerator.ts +++ b/src/levelGenerator.ts @@ -14,32 +14,39 @@ import { getRandomPlanetTexture } from "./planetTextures"; * Generates procedural level configurations matching the current Level1 generation logic */ export class LevelGenerator { - private _difficulty: string; - private _difficultyConfig: DifficultyConfig; + protected _difficulty: string; + protected _difficultyConfig: DifficultyConfig; - // Constants matching Level1 defaults - private static readonly SHIP_POSITION: Vector3Array = [0, 1, 0]; - private static readonly START_BASE_POSITION: Vector3Array = [0, 0, 0]; - private static readonly START_BASE_DIAMETER = 10; - private static readonly START_BASE_HEIGHT = 1; - private static readonly START_BASE_COLOR: Vector3Array = [1, 1, 0]; // Yellow + // Configurable properties (can be overridden by subclasses or set before generate()) + public shipPosition: Vector3Array = [0, 1, 0]; + public startBasePosition: Vector3Array = [0, 0, 0]; + public startBaseDiameter = 10; + public startBaseHeight = 1; + public startBaseColor: Vector3Array = [1, 1, 0]; // Yellow - private static readonly SUN_POSITION: Vector3Array = [0, 0, 400]; - private static readonly SUN_DIAMETER = 50; - private static readonly SUN_INTENSITY = 1000000; + public sunPosition: Vector3Array = [0, 0, 400]; + public sunDiameter = 50; + public sunIntensity = 1000000; - // Planet generation constants (matching createPlanetsOrbital call in Level1) - private static readonly PLANET_COUNT = 12; - private static readonly PLANET_MIN_DIAMETER = 100; - private static readonly PLANET_MAX_DIAMETER = 200; - private static readonly PLANET_MIN_DISTANCE = 1000; - private static readonly PLANET_MAX_DISTANCE = 2000; + // Planet generation parameters + public planetCount = 12; + public planetMinDiameter = 100; + public planetMaxDiameter = 200; + public planetMinDistance = 1000; + public planetMaxDistance = 2000; constructor(difficulty: string) { this._difficulty = difficulty; this._difficultyConfig = this.getDifficultyConfig(difficulty); } + /** + * Set custom difficulty configuration + */ + public setDifficultyConfig(config: DifficultyConfig) { + this._difficultyConfig = config; + } + /** * Generate a complete level configuration */ @@ -69,7 +76,7 @@ export class LevelGenerator { private generateShip(): ShipConfig { return { - position: [...LevelGenerator.SHIP_POSITION], + position: [...this.shipPosition], rotation: [0, 0, 0], linearVelocity: [0, 0, 0], angularVelocity: [0, 0, 0] @@ -78,18 +85,18 @@ export class LevelGenerator { private generateStartBase(): StartBaseConfig { return { - position: [...LevelGenerator.START_BASE_POSITION], - diameter: LevelGenerator.START_BASE_DIAMETER, - height: LevelGenerator.START_BASE_HEIGHT, - color: [...LevelGenerator.START_BASE_COLOR] + position: [...this.startBasePosition], + diameter: this.startBaseDiameter, + height: this.startBaseHeight, + color: [...this.startBaseColor] }; } private generateSun(): SunConfig { return { - position: [...LevelGenerator.SUN_POSITION], - diameter: LevelGenerator.SUN_DIAMETER, - intensity: LevelGenerator.SUN_INTENSITY + position: [...this.sunPosition], + diameter: this.sunDiameter, + intensity: this.sunIntensity }; } @@ -98,27 +105,26 @@ export class LevelGenerator { */ private generatePlanets(): PlanetConfig[] { const planets: PlanetConfig[] = []; - const sunPosition = LevelGenerator.SUN_POSITION; - for (let i = 0; i < LevelGenerator.PLANET_COUNT; i++) { + for (let i = 0; i < this.planetCount; i++) { // Random diameter between min and max - const diameter = LevelGenerator.PLANET_MIN_DIAMETER + - Math.random() * (LevelGenerator.PLANET_MAX_DIAMETER - LevelGenerator.PLANET_MIN_DIAMETER); + const diameter = this.planetMinDiameter + + Math.random() * (this.planetMaxDiameter - this.planetMinDiameter); // Random distance from sun - const distance = LevelGenerator.PLANET_MIN_DISTANCE + - Math.random() * (LevelGenerator.PLANET_MAX_DISTANCE - LevelGenerator.PLANET_MIN_DISTANCE); + const distance = this.planetMinDistance + + Math.random() * (this.planetMaxDistance - this.planetMinDistance); // Random angle around Y axis (orbital plane) const angle = Math.random() * Math.PI * 2; // Small vertical variation (like a solar system) - const y = (Math.random() - 0.5) * 100; + const y = (Math.random() - 0.5) * 400; const position: Vector3Array = [ - sunPosition[0] + distance * Math.cos(angle), - sunPosition[1] + y, - sunPosition[2] + distance * Math.sin(angle) + this.sunPosition[0] + distance * Math.cos(angle), + this.sunPosition[1] + y, + this.sunPosition[2] + distance * Math.sin(angle) ]; planets.push({ @@ -134,7 +140,7 @@ export class LevelGenerator { } /** - * Generate asteroids matching Level1.initialize() logic + * Generate asteroids distributed evenly around the base in a circular pattern */ private generateAsteroids(): AsteroidConfig[] { const asteroids: AsteroidConfig[] = []; @@ -145,8 +151,19 @@ export class LevelGenerator { const distRange = config.distanceMax - config.distanceMin; const dist = (Math.random() * distRange) + config.distanceMin; - // Initial position (forward from start base) - const position: Vector3Array = [0, 1, dist]; + // Evenly distribute asteroids around a circle + const angle = (i / config.rockCount) * Math.PI * 2; + + // Add small random variation to angle to prevent perfect spacing + const angleVariation = (Math.random() - 0.5) * 0.3; // ±0.15 radians variation + const finalAngle = angle + angleVariation; + + // Calculate position in a circle around the base (XZ plane) + const x = dist * Math.cos(finalAngle); + const z = dist * Math.sin(finalAngle); + const y = 1; // Keep at same height as ship + + const position: Vector3Array = [x, y, z]; // Random size const sizeRange = config.rockSizeMax - config.rockSizeMin; @@ -154,14 +171,16 @@ export class LevelGenerator { const scaling: Vector3Array = [size, size, size]; // Calculate initial velocity based on force applied in Level1 - // In Level1: rock.physicsBody.applyForce(new Vector3(50000000 * config.forceMultiplier, 0, 0), rock.position) - // For a body with mass 10000, force becomes velocity over time - // Simplified: velocity ≈ force / mass (ignoring physics timestep details) + // Velocity should be tangential to the circle (perpendicular to radius) const forceMagnitude = 50000000 * config.forceMultiplier; const mass = 10000; const velocityMagnitude = forceMagnitude / mass / 100; // Approximation - const linearVelocity: Vector3Array = [velocityMagnitude, 0, 0]; + // Tangential velocity (perpendicular to the radius vector) + const vx = -velocityMagnitude * Math.sin(finalAngle); + const vz = velocityMagnitude * Math.cos(finalAngle); + + const linearVelocity: Vector3Array = [vx, 0, vz]; asteroids.push({ id: `asteroid-${i}`, diff --git a/src/scoreboard.ts b/src/scoreboard.ts index 9ff5823..e080592 100644 --- a/src/scoreboard.ts +++ b/src/scoreboard.ts @@ -33,6 +33,9 @@ export class Scoreboard { public set done(value: boolean) { this._done = value; } + public setRemainingCount(count: number) { + this._remaining = count; + } private initialize() { const scene = DefaultScene.MainScene;