import { TestCase } from "./framework/test_case.js"; import { BombardmentSfx, AirStrikeSfx, zoomVolume, BOMB_VOLUME, BOMB_CLIP_S, BOMB_GAP_MS, BOMB_IDLE_MS, AIR_BOMB_BURST, } from "../client/js/game_screen/bombing.js"; export class BombingTest extends TestCase { test_volume_grows_with_zoom() { this.assertLess(zoomVolume(0.2), zoomVolume(1.0), "closer is louder"); this.assertLess(zoomVolume(1.0), zoomVolume(2.0), "and louder still up close"); this.assertApprox(zoomVolume(0.2), BOMB_VOLUME.min); this.assertApprox(zoomVolume(2.0), BOMB_VOLUME.max); // Out-of-range zooms clamp rather than going silent or overdriven. this.assertApprox(zoomVolume(-5), BOMB_VOLUME.min); this.assertApprox(zoomVolume(50), BOMB_VOLUME.max); } test_silent_while_nothing_is_bombing() { const plays = []; const sfx = new BombardmentSfx((volume, duration) => plays.push({ volume, duration })); for (let i = 0; i < 50; i++) sfx.update(100, false, 0.5); this.assertEqual(plays.length, 0); } test_bursts_play_short_clips_at_the_zoom_volume() { const plays = []; let now = 0; const sfx = new BombardmentSfx( (volume, duration) => plays.push({ volume, duration, at: now }), () => 0.5 ); // Ten seconds of frames, 100 ms apart, with a bombardment in view. for (let i = 0; i < 100; i++) { now += 100; sfx.update(100, true, 0.4); } this.assertGreater(plays.length, 0, "impacts play"); this.assertLess(plays.length, 100, "the impacts arrive in bursts, not every frame"); for (const play of plays) { this.assertApprox(play.volume, 0.4, 1e-9, "the impact uses the zoom volume"); this.assertGreaterOrEqual(play.duration, BOMB_CLIP_S.min); this.assertLess(play.duration, BOMB_CLIP_S.max + 1e-9, "each clip is cut short"); } } test_impacts_keep_their_spacing() { const times = []; let now = 0; const sfx = new BombardmentSfx(() => times.push(now), () => 0.99); // Forty seconds of frames, 100 ms apart. for (let i = 0; i < 400; i++) { now += 100; sfx.update(100, true, 0.5); } this.assertGreater(times.length, 3, "impacts play"); const gaps = []; for (let i = 1; i < times.length; i++) gaps.push(times[i] - times[i - 1]); for (const gap of gaps) { this.assertGreaterOrEqual(gap, BOMB_GAP_MS.min, "impacts never bunch up"); } this.assertTrue( gaps.some((gap) => gap >= BOMB_IDLE_MS.min), "and there are quiet gaps between bursts" ); } test_air_strike_is_one_large_one_shot_burst() { const plays = []; let now = 0; const sfx = new AirStrikeSfx( (volume, duration) => plays.push({ volume, duration, at: now }), () => 0.99 ); sfx.trigger(0.5); // Two seconds of frames, 50 ms apart, cover the whole burst. for (let i = 0; i < 40; i++) { now += 50; sfx.update(50); } this.assertGreaterOrEqual(plays.length, AIR_BOMB_BURST.min, "at least seven explosions"); for (const play of plays) this.assertApprox(play.volume, 0.5, 1e-9); // It is a one-shot: no further impacts once the burst has run out. const after = plays.length; for (let i = 0; i < 20; i++) { now += 50; sfx.update(50); } this.assertEqual(plays.length, after, "the burst does not keep repeating"); } test_stopping_the_bombing_stops_the_sound() { const plays = []; const sfx = new BombardmentSfx((volume, duration) => plays.push(duration), () => 0); for (let i = 0; i < 10; i++) sfx.update(100, true, 0.5); const before = plays.length; this.assertGreater(before, 0, "something played while active"); for (let i = 0; i < 10; i++) sfx.update(100, false, 0.5); this.assertEqual(plays.length, before, "no impacts once the bombing stops"); for (let i = 0; i < 5; i++) sfx.update(100, true, 0.5); this.assertGreater(plays.length, before, "the ambience resumes with the next barrage"); } }