Implemented automatic resource replenishment when the ship is inside the base landing zone. **Modified: src/starBase.ts** - Added StarBaseResult interface with baseMesh and landingAggregate properties - Changed buildStarBase() return type from AbstractMesh to StarBaseResult - Now returns both the base mesh and landing aggregate for resupply system access - Landing aggregate already configured as trigger for collision detection **Modified: src/levelDeserializer.ts** - Added PhysicsAggregate import - Updated deserialize() return type to include landingAggregate field - Changed createStartBase() to return full StarBaseResult - Updated return statement to destructure baseResult into baseMesh and landingAggregate **Modified: src/level1.ts** - Added PhysicsAggregate import and _landingAggregate property - Stored landingAggregate from deserializer result - Called ship.setLandingZone() to configure resupply system **Modified: src/ship.ts** - Added resupply system properties: _landingAggregate, _resupplyTimer, _isInLandingZone - Added setLandingZone() method to configure landing zone for resupply - Added updateResupply() method called every physics update (6 times per second) - Distance-based detection: checks if ship is within 20 units of landing zone center - Resupply rate: 0.1 per second for all resources (fuel, hull, ammo) - Automatically replenishes until resources reach 1.0 maximum - Debug logging for enter/exit landing zone events Resupply System Mechanics: - Activates when ship is within landing zone (distance < 20 units) - Replenishes at 0.1 per second (~0.01666 per update at 6 updates/second) - Repairs all three resources simultaneously: fuel, hull, ammo - Stops automatically when each resource reaches maximum (1.0) - Integrated with existing ShipStatus observable system for automatic gauge updates 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
8605946fab
commit
827dd2d359
@ -3,6 +3,7 @@ import type {AudioEngineV2} from "@babylonjs/core";
|
||||
import {
|
||||
AbstractMesh,
|
||||
Observable,
|
||||
PhysicsAggregate,
|
||||
Vector3
|
||||
} from "@babylonjs/core";
|
||||
import {Ship} from "./ship";
|
||||
@ -18,6 +19,7 @@ export class Level1 implements Level {
|
||||
private _onReadyObservable: Observable<Level> = new Observable<Level>();
|
||||
private _initialized: boolean = false;
|
||||
private _startBase: AbstractMesh | null;
|
||||
private _landingAggregate: PhysicsAggregate | null;
|
||||
private _endBase: AbstractMesh;
|
||||
private _levelConfig: LevelConfig;
|
||||
private _audioEngine: AudioEngineV2;
|
||||
@ -103,6 +105,13 @@ export class Level1 implements Level {
|
||||
const entities = await this._deserializer.deserialize(this._ship.scoreboard.onScoreObservable);
|
||||
|
||||
this._startBase = entities.startBase;
|
||||
this._landingAggregate = entities.landingAggregate;
|
||||
|
||||
// Setup resupply system if landing aggregate exists
|
||||
if (this._landingAggregate) {
|
||||
this._ship.setLandingZone(this._landingAggregate);
|
||||
}
|
||||
|
||||
// sun and planets are already created by deserializer
|
||||
|
||||
// Initialize scoreboard with total asteroid count
|
||||
|
||||
@ -3,6 +3,7 @@ import {
|
||||
MeshBuilder,
|
||||
Observable,
|
||||
PBRMaterial,
|
||||
PhysicsAggregate,
|
||||
Texture,
|
||||
Vector3
|
||||
} from "@babylonjs/core";
|
||||
@ -43,6 +44,7 @@ export class LevelDeserializer {
|
||||
*/
|
||||
public async deserialize(scoreObservable: Observable<ScoreEvent>): Promise<{
|
||||
startBase: AbstractMesh | null;
|
||||
landingAggregate: PhysicsAggregate | null;
|
||||
sun: AbstractMesh;
|
||||
planets: AbstractMesh[];
|
||||
asteroids: AbstractMesh[];
|
||||
@ -50,7 +52,7 @@ export class LevelDeserializer {
|
||||
debugLog('Deserializing level:', this.config.difficulty);
|
||||
|
||||
// Create entities
|
||||
const startBase = await this.createStartBase();
|
||||
const baseResult = await this.createStartBase();
|
||||
const sun = this.createSun();
|
||||
const planets = this.createPlanets();
|
||||
const asteroids = await this.createAsteroids(scoreObservable);
|
||||
@ -63,7 +65,8 @@ export class LevelDeserializer {
|
||||
light2.intensity = .5;
|
||||
*/
|
||||
return {
|
||||
startBase,
|
||||
startBase: baseResult.baseMesh,
|
||||
landingAggregate: baseResult.landingAggregate,
|
||||
sun,
|
||||
planets,
|
||||
asteroids
|
||||
@ -73,7 +76,7 @@ export class LevelDeserializer {
|
||||
/**
|
||||
* Create the start base from config
|
||||
*/
|
||||
private async createStartBase(): Promise<AbstractMesh> {
|
||||
private async createStartBase() {
|
||||
return await StarBase.buildStarBase();
|
||||
}
|
||||
|
||||
|
||||
73
src/ship.ts
73
src/ship.ts
@ -42,6 +42,11 @@ export class Ship {
|
||||
// Frame counter for physics updates
|
||||
private _frameCount: number = 0;
|
||||
|
||||
// Resupply system
|
||||
private _landingAggregate: PhysicsAggregate | null = null;
|
||||
private _resupplyTimer: number = 0;
|
||||
private _isInLandingZone: boolean = false;
|
||||
|
||||
constructor(audioEngine?: AudioEngineV2) {
|
||||
this._audioEngine = audioEngine;
|
||||
}
|
||||
@ -233,6 +238,58 @@ export class Ship {
|
||||
forceMagnitudes.angularMagnitude
|
||||
);
|
||||
}
|
||||
|
||||
// Handle resupply when in landing zone
|
||||
this.updateResupply();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update resupply system - replenishes resources at 0.1 per second when in landing zone
|
||||
*/
|
||||
private updateResupply(): void {
|
||||
if (!this._landingAggregate || !this._ship?.physicsBody) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if ship is still in the landing zone by checking distance
|
||||
// Since it's a trigger, we need to track position
|
||||
const shipPos = this._ship.physicsBody.transformNode.position;
|
||||
const landingPos = this._landingAggregate.transformNode.position;
|
||||
const distance = Vector3.Distance(shipPos, landingPos);
|
||||
|
||||
// Assume landing zone radius is approximately 20 units (adjust as needed)
|
||||
const wasInZone = this._isInLandingZone;
|
||||
this._isInLandingZone = distance < 20;
|
||||
|
||||
if (this._isInLandingZone && !wasInZone) {
|
||||
debugLog("Ship entered landing zone - resupply active");
|
||||
} else if (!this._isInLandingZone && wasInZone) {
|
||||
debugLog("Ship exited landing zone - resupply inactive");
|
||||
}
|
||||
|
||||
// Resupply at 0.1 per second if in zone
|
||||
if (this._isInLandingZone && this._scoreboard?.shipStatus) {
|
||||
// Physics update runs every 10 frames at 60fps = 6 times per second
|
||||
// 0.1 per second / 6 updates per second = 0.01666... per update
|
||||
const resupplyRate = 0.1 / 6;
|
||||
|
||||
const status = this._scoreboard.shipStatus;
|
||||
|
||||
// Replenish fuel
|
||||
if (status.fuel < 1.0) {
|
||||
status.addFuel(resupplyRate);
|
||||
}
|
||||
|
||||
// Repair hull
|
||||
if (status.hull < 1.0) {
|
||||
status.repairHull(resupplyRate);
|
||||
}
|
||||
|
||||
// Replenish ammo
|
||||
if (status.ammo < 1.0) {
|
||||
status.addAmmo(resupplyRate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -258,6 +315,22 @@ export class Ship {
|
||||
return this._ship;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the landing zone for resupply
|
||||
*/
|
||||
public setLandingZone(landingAggregate: PhysicsAggregate): void {
|
||||
this._landingAggregate = landingAggregate;
|
||||
|
||||
// Listen for trigger events to detect when ship enters/exits landing zone
|
||||
landingAggregate.body.getCollisionObservable().add((collisionEvent) => {
|
||||
// Check if the collision is with our ship
|
||||
if (collisionEvent.collider === this._ship.physicsBody) {
|
||||
this._isInLandingZone = true;
|
||||
debugLog("Ship entered landing zone - resupply active");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a VR controller to the input system
|
||||
*/
|
||||
|
||||
@ -11,13 +11,18 @@ import {GameConfig} from "./gameConfig";
|
||||
import debugLog from "./debug";
|
||||
import loadAsset from "./utils/loadAsset";
|
||||
|
||||
export interface StarBaseResult {
|
||||
baseMesh: AbstractMesh;
|
||||
landingAggregate: PhysicsAggregate | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and load the star base mesh
|
||||
* @param position - Position for the star base
|
||||
* @returns Promise resolving to the loaded star base mesh
|
||||
* @returns Promise resolving to the loaded star base mesh and landing aggregate
|
||||
*/
|
||||
export default class StarBase {
|
||||
public static async buildStarBase(): Promise<AbstractMesh> {
|
||||
public static async buildStarBase(): Promise<StarBaseResult> {
|
||||
const config = GameConfig.getInstance();
|
||||
const scene = DefaultScene.MainScene;
|
||||
const importMeshes = await loadAsset('base.glb');
|
||||
@ -25,7 +30,7 @@ export default class StarBase {
|
||||
const baseMesh = importMeshes.meshes.get('Base');
|
||||
const landingMesh = importMeshes.meshes.get('BaseLandingZone');
|
||||
|
||||
|
||||
let landingAgg: PhysicsAggregate | null = null;
|
||||
|
||||
if (config.physicsEnabled) {
|
||||
const agg2 = new PhysicsAggregate(baseMesh, PhysicsShapeType.MESH, {
|
||||
@ -37,7 +42,7 @@ export default class StarBase {
|
||||
debugLog('collidedBody', collidedBody);
|
||||
})
|
||||
|
||||
const landingAgg = new PhysicsAggregate(landingMesh, PhysicsShapeType.MESH);
|
||||
landingAgg = new PhysicsAggregate(landingMesh, PhysicsShapeType.MESH);
|
||||
landingAgg.body.setMotionType(PhysicsMotionType.ANIMATED);
|
||||
/*landingAgg.body.getCollisionObservable().add((collidedCollidedBody) => {
|
||||
|
||||
@ -50,7 +55,10 @@ export default class StarBase {
|
||||
landingAgg.body.setCollisionCallbackEnabled(true);
|
||||
}
|
||||
//importMesh.rootNodes[0].dispose();
|
||||
return baseMesh;
|
||||
return {
|
||||
baseMesh,
|
||||
landingAggregate: landingAgg
|
||||
};
|
||||
}
|
||||
}
|
||||
function clearParent (meshes: Map<string, AbstractMesh>, position?: Vector3) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user