Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/client/ClientGameRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import { createRenderer, GameRenderer } from "./hud/GameRenderer";
import { GameView as WebGLGameView } from "./render/gl";
import { ALL_UNIT_TYPES, UnitState } from "./render/types";
import { SoundManager } from "./sound/SoundManager";
import { SoundUpdateEvent } from "./sound/Sounds";

export interface LobbyConfig {
cosmetics: PlayerCosmeticRefs;
Expand Down Expand Up @@ -654,6 +655,9 @@ export class ClientGameRunner {
gu.updates[GameUpdateType.Hash].forEach((hu: HashUpdate) => {
this.eventBus.emit(new SendHashEvent(hu.tick, hu.hash));
});

this.eventBus.emit(new SoundUpdateEvent(gu));

this.gameView.update(gu);
this.webglBuilder?.update(this.gameView);
this.renderer.tick();
Expand Down
93 changes: 93 additions & 0 deletions src/client/controllers/SoundController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { EventBus } from "../../core/EventBus";
import { UnitType } from "../../core/game/Game";
import {
ConquestUpdate,
GameUpdateType,
UnitUpdate,
} from "../../core/game/GameUpdates";
import { GameView } from "../../core/game/GameView";
import { Controller } from "../Controller";
import { PlaySoundEffectEvent, SoundUpdateEvent } from "../sound/Sounds";

export class SoundController implements Controller {
constructor(
private eventBus: EventBus,
private view: GameView,
) {}

init() {
this.eventBus.on(SoundUpdateEvent, (e) => this.handleGameUpdate(e));
}

private handleGameUpdate(e: SoundUpdateEvent) {
const gu = e.gu;
if ((gu.pendingTurns ?? 0) > 1 || this.view.ticks() <= 0) return;

const myPlayer = this.view.myPlayer();

// 1. Process Conquests
if (myPlayer) {
gu.updates[GameUpdateType.ConquestEvent]?.forEach(
(cu: ConquestUpdate) => {
if (cu.conquerorId === myPlayer.id()) {
this.eventBus.emit(new PlaySoundEffectEvent("ka-ching"));
}
},
);
}

// 2. Process Units
gu.updates[GameUpdateType.Unit]?.forEach((u: UnitUpdate) => {
const existingUnit = this.view.unit(u.id);
const isMine = myPlayer ? u.ownerID === myPlayer.smallID() : false;

if (!existingUnit) {
this.handleNewUnitSounds(u.unitType, isMine);
} else if (existingUnit.isActive() && !u.isActive && u.reachedTarget) {
this.handleImpactSounds(u.unitType);
}
});
}

private handleNewUnitSounds(unitType: UnitType, isMine: boolean) {
switch (unitType) {
case UnitType.AtomBomb:
this.eventBus.emit(new PlaySoundEffectEvent("atom-launch"));
break;
case UnitType.HydrogenBomb:
this.eventBus.emit(new PlaySoundEffectEvent("hydrogen-launch"));
break;
case UnitType.MIRV:
this.eventBus.emit(new PlaySoundEffectEvent("mirv-launch"));
break;
case UnitType.Warship:
if (isMine)
this.eventBus.emit(new PlaySoundEffectEvent("build-warship"));
break;
case UnitType.City:
if (isMine) this.eventBus.emit(new PlaySoundEffectEvent("build-city"));
break;
case UnitType.Port:
if (isMine) this.eventBus.emit(new PlaySoundEffectEvent("build-port"));
break;
case UnitType.DefensePost:
if (isMine)
this.eventBus.emit(new PlaySoundEffectEvent("build-defense-post"));
break;
case UnitType.SAMLauncher:
if (isMine) this.eventBus.emit(new PlaySoundEffectEvent("sam-built"));
break;
case UnitType.MissileSilo:
if (isMine) this.eventBus.emit(new PlaySoundEffectEvent("silo-built"));
break;
}
}

private handleImpactSounds(unitType: UnitType) {
if (unitType === UnitType.HydrogenBomb) {
this.eventBus.emit(new PlaySoundEffectEvent("hydrogen-hit"));
} else if (unitType === UnitType.AtomBomb || unitType === UnitType.MIRV) {
this.eventBus.emit(new PlaySoundEffectEvent("atom-hit"));
}
}
}
2 changes: 2 additions & 0 deletions src/client/hud/GameRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { UIState } from "../UIState";
import { AttackingTroopsController } from "../controllers/AttackingTroopsController";
import { BuildPreviewController } from "../controllers/BuildPreviewController";
import { HoverHighlightController } from "../controllers/HoverHighlightController";
import { SoundController } from "../controllers/SoundController";
import { StructureHighlightController } from "../controllers/StructureHighlightController";
import { ViewModeController } from "../controllers/ViewModeController";
import { WarshipSelectionController } from "../controllers/WarshipSelectionController";
Expand Down Expand Up @@ -283,6 +284,7 @@ export function createRenderer(
new HoverHighlightController(game, eventBus, transformHandler, view),
new StructureHighlightController(eventBus, view),
new ViewModeController(eventBus, view),
new SoundController(eventBus, game),
new AttackingTroopsController(game, eventBus, userSettings, view),
eventsDisplay,
actionableEvents,
Expand Down
7 changes: 7 additions & 0 deletions src/client/sound/Sounds.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { assetUrl } from "../../core/AssetUrls";
import { GameEvent } from "../../core/EventBus";
import { GameUpdateViewData } from "../../core/game/GameUpdates";

export type SoundEffect =
| "ka-ching"
Expand All @@ -15,6 +16,7 @@ export type SoundEffect =
| "build-defense-post"
| "build-warship"
| "sam-built"
| "silo-built"
| "message"
| "click";

Expand All @@ -32,6 +34,7 @@ export const soundEffectUrls: ReadonlyMap<SoundEffect, string> = new Map([
["build-defense-post", assetUrl("sounds/effects/build-defense-post.mp3")],
["build-warship", assetUrl("sounds/effects/build-warship.mp3")],
["sam-built", assetUrl("sounds/effects/sam-built.mp3")],
["silo-built", assetUrl("sounds/effects/silo-built.mp3")],
["message", assetUrl("sounds/effects/message.mp3")],
["click", assetUrl("sounds/effects/click.mp3")],
]);
Expand All @@ -40,6 +43,10 @@ export class PlaySoundEffectEvent implements GameEvent {
constructor(public readonly effect: SoundEffect) {}
}

export class SoundUpdateEvent implements GameEvent {
constructor(public readonly gu: GameUpdateViewData) {}
}

export class SetSoundEffectsVolumeEvent implements GameEvent {
constructor(public readonly volume: number) {}
}
Expand Down
Loading