Fix critical bug: Repeating voice messages now stop when resources recover
All checks were successful
Build / build (push) Successful in 1m24s
All checks were successful
Build / build (push) Successful in 1m24s
Added stateKey field to VoiceMessage interface to explicitly link messages with warning states, preventing infinite repeat loops. Bug fixes: - Messages check warning state exists before re-queuing - clearWarningState() now purges matching messages from queue - Proper cleanup when resources increase above thresholds Changes to voiceAudioSystem.ts: - VoiceMessage interface: Added stateKey field - queueMessage(): Added stateKey parameter - handleStatusChange(): Pass state keys when queueing warnings - update(): Validate state exists before re-queuing repeating messages - clearWarningState(): Filter queue to remove messages with matching stateKey Also includes explosion audio improvements: - Use onEndedObservable instead of setTimeout for audio cleanup - Adjusted explosion force and duration parameters 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
b523d5e31a
commit
9b22b06d08
0
public/levels/1.json
Normal file
0
public/levels/1.json
Normal file
0
public/levels/directory.json
Normal file
0
public/levels/directory.json
Normal file
@ -34,18 +34,19 @@ export class ExplosionManager {
|
||||
|
||||
// Default configuration
|
||||
private static readonly DEFAULT_CONFIG: Required<ExplosionConfig> = {
|
||||
duration: 1000,
|
||||
explosionForce: 5,
|
||||
duration: 2000,
|
||||
explosionForce: 10,
|
||||
frameRate: 60
|
||||
};
|
||||
|
||||
constructor(scene: Scene, config?: ExplosionConfig) {
|
||||
this.scene = scene;
|
||||
this.config = { ...ExplosionManager.DEFAULT_CONFIG, ...config };
|
||||
debugLog(this.config);
|
||||
this._debrisBaseMesh = MeshBuilder.CreateIcoSphere(
|
||||
'debrisBase',
|
||||
{
|
||||
radius: .2,
|
||||
radius: 1,
|
||||
subdivisions: 2
|
||||
}, DefaultScene.MainScene
|
||||
);
|
||||
@ -130,14 +131,13 @@ export class ExplosionManager {
|
||||
// Attach spatial sound to the node
|
||||
sound.spatial.attach(explosionNode);
|
||||
sound.play();
|
||||
|
||||
// Cleanup after explosion duration (synchronized with visual effect)
|
||||
setTimeout(() => {
|
||||
if (sound.spatial) {
|
||||
sound.spatial.detach();
|
||||
}
|
||||
sound.onEndedObservable.addOnce(() => {
|
||||
//Cleanup after sound ends.
|
||||
sound.spatial.detach();
|
||||
explosionNode.dispose();
|
||||
}, this.config.duration);
|
||||
})
|
||||
|
||||
|
||||
} catch (error) {
|
||||
debugLog("ExplosionManager: Error playing explosion audio", error);
|
||||
explosionNode.dispose();
|
||||
|
||||
@ -48,8 +48,8 @@ export class RockFactory {
|
||||
this._orbitCenter = new PhysicsAggregate(node, PhysicsShapeType.SPHERE, {radius: .1, mass: 1000}, DefaultScene.MainScene );
|
||||
this._orbitCenter.body.setMotionType(PhysicsMotionType.ANIMATED);
|
||||
this._explosionManager = new ExplosionManager(DefaultScene.MainScene, {
|
||||
duration: 800,
|
||||
explosionForce: 20.0,
|
||||
duration: 2000,
|
||||
explosionForce: 150.0,
|
||||
frameRate: 60
|
||||
});
|
||||
await this._explosionManager.initialize();
|
||||
@ -102,7 +102,6 @@ export class RockFactory {
|
||||
const body = agg.body;
|
||||
const constraint = new DistanceConstraint(Vector3.Distance(position, this._orbitCenter.body.transformNode.position), DefaultScene.MainScene);
|
||||
body.addConstraint(this._orbitCenter.body, constraint);
|
||||
|
||||
body.setLinearDamping(0)
|
||||
body.setMotionType(PhysicsMotionType.DYNAMIC);
|
||||
body.setCollisionCallbackEnabled(true);
|
||||
|
||||
@ -21,6 +21,7 @@ interface VoiceMessage {
|
||||
interrupt: boolean; // If true, interrupt current playback
|
||||
repeatInterval?: number; // Milliseconds between repeats (0 or undefined = no repeat)
|
||||
lastPlayedTime?: number; // Timestamp when message was last played (for repeat timing)
|
||||
stateKey?: string; // Warning state key (e.g., "warning_fuel") to track ownership
|
||||
}
|
||||
|
||||
/**
|
||||
@ -130,19 +131,19 @@ export class VoiceAudioSystem {
|
||||
this._warningStates.add(`danger_${statusType}`);
|
||||
// Clear warning state if it exists (danger supersedes warning)
|
||||
this.clearWarningState(`warning_${statusType}`);
|
||||
this.queueMessage(['danger', statusType], VoiceMessagePriority.HIGH, false, 2000);
|
||||
this.queueMessage(['danger', statusType], VoiceMessagePriority.HIGH, false, 2000, `danger_${statusType}`);
|
||||
}
|
||||
// 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)}%)`);
|
||||
this._warningStates.add(`warning_${statusType}`);
|
||||
this.queueMessage(['warning', statusType], VoiceMessagePriority.NORMAL, false, 4000);
|
||||
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}`);
|
||||
this._warningStates.add(`empty_${statusType}`);
|
||||
this.queueMessage([statusType, 'empty'], VoiceMessagePriority.HIGH, false);
|
||||
this.queueMessage([statusType, 'empty'], VoiceMessagePriority.HIGH, false, 0, `empty_${statusType}`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,7 +155,8 @@ export class VoiceAudioSystem {
|
||||
sounds: string[],
|
||||
priority: VoiceMessagePriority = VoiceMessagePriority.NORMAL,
|
||||
interrupt: boolean = false,
|
||||
repeatInterval: number = 0
|
||||
repeatInterval: number = 0,
|
||||
stateKey?: string
|
||||
): void {
|
||||
if (!this._audioEngine) {
|
||||
debugLog('VoiceAudioSystem: Cannot queue message - audio not initialized');
|
||||
@ -165,7 +167,8 @@ export class VoiceAudioSystem {
|
||||
sounds,
|
||||
priority,
|
||||
interrupt,
|
||||
repeatInterval: repeatInterval > 0 ? repeatInterval : undefined
|
||||
repeatInterval: repeatInterval > 0 ? repeatInterval : undefined,
|
||||
stateKey
|
||||
};
|
||||
|
||||
// If interrupt flag is set, stop current playback and clear queue
|
||||
@ -224,12 +227,17 @@ export class VoiceAudioSystem {
|
||||
|
||||
// Check if this message should repeat
|
||||
if (this._currentMessage.repeatInterval && this._currentMessage.repeatInterval > 0) {
|
||||
// Record the time this message finished
|
||||
this._currentMessage.lastPlayedTime = performance.now();
|
||||
// Only re-queue if the warning state still exists
|
||||
if (!this._currentMessage.stateKey || this._warningStates.has(this._currentMessage.stateKey)) {
|
||||
// Record the time this message finished
|
||||
this._currentMessage.lastPlayedTime = performance.now();
|
||||
|
||||
// Re-queue the message for repeat
|
||||
this._queue.push({ ...this._currentMessage });
|
||||
debugLog(`VoiceAudioSystem: Message re-queued for repeat in ${this._currentMessage.repeatInterval}ms`);
|
||||
// Re-queue the message for repeat
|
||||
this._queue.push({ ...this._currentMessage });
|
||||
debugLog(`VoiceAudioSystem: Message re-queued for repeat in ${this._currentMessage.repeatInterval}ms`);
|
||||
} else {
|
||||
debugLog(`VoiceAudioSystem: Message NOT re-queued - warning state '${this._currentMessage.stateKey}' cleared`);
|
||||
}
|
||||
}
|
||||
|
||||
this._isPlaying = false;
|
||||
@ -311,12 +319,22 @@ export class VoiceAudioSystem {
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear a specific warning state (allows warning to re-trigger)
|
||||
* Clear a specific warning state and remove matching messages from queue
|
||||
*/
|
||||
public clearWarningState(key: string): void {
|
||||
if (this._warningStates.has(key)) {
|
||||
this._warningStates.delete(key);
|
||||
debugLog(`VoiceAudioSystem: Cleared warning state '${key}'`);
|
||||
|
||||
// Remove all messages with this state key from queue
|
||||
const originalLength = this._queue.length;
|
||||
this._queue = this._queue.filter(msg => msg.stateKey !== key);
|
||||
|
||||
const removed = originalLength - this._queue.length;
|
||||
if (removed > 0) {
|
||||
debugLog(`VoiceAudioSystem: Cleared warning state '${key}' and purged ${removed} queued message(s)`);
|
||||
} else {
|
||||
debugLog(`VoiceAudioSystem: Cleared warning state '${key}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user