mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 09:30:45 +00:00
Implement a "ka-ching" sound effect on kill (#2097)
## Description: Building off of [this PR](https://github.com/openfrontio/OpenFrontIO/pull/2090) which implemented music, I extend this functionality to add sound effects. Diff will be reduced if and when that PR gets merged! I think the game would benefit from more sound effects, and adding a "ka-ching" sound effect on kill seems like an easy place to start. The ka-ching sound effect was found [here](https://freesound.org/people/AKkingStudio/sounds/684165/) and is licensed under Creative Commons. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced Demo video with sound: https://github.com/user-attachments/assets/18c857a4-a741-492a-bbc1-68d4f3ba38da ## Please put your Discord username so you can be contacted if a bug or regression is found: basedgob --------- Co-authored-by: icslucas <carolinacarazolli@gmail.com>
This commit is contained in:
@@ -380,7 +380,8 @@
|
||||
"terrain_disabled": "Terrain view disabled",
|
||||
"exit_game_label": "Exit Game",
|
||||
"exit_game_info": "Return to main menu",
|
||||
"background_music_volume": "Background Music Volume"
|
||||
"background_music_volume": "Background Music Volume",
|
||||
"sound_effects_volume": "Sound Effects Volume"
|
||||
},
|
||||
"chat": {
|
||||
"title": "Quick Chat",
|
||||
|
||||
Binary file not shown.
@@ -7,6 +7,7 @@ import {
|
||||
RailroadUpdate,
|
||||
} from "../../../core/game/GameUpdates";
|
||||
import { GameView, UnitView } from "../../../core/game/GameView";
|
||||
import SoundManager, { SoundEffect } from "../../sound/SoundManager";
|
||||
import { renderNumber } from "../../Utils";
|
||||
import { AnimatedSpriteLoader } from "../AnimatedSpriteLoader";
|
||||
import { conquestFxFactory } from "../fx/ConquestFx";
|
||||
@@ -216,6 +217,8 @@ export class FxLayer implements Layer {
|
||||
return;
|
||||
}
|
||||
|
||||
SoundManager.playSoundEffect(SoundEffect.KaChing);
|
||||
|
||||
const conquestFx = conquestFxFactory(
|
||||
this.animatedSpriteLoader,
|
||||
conquest,
|
||||
|
||||
@@ -50,6 +50,7 @@ export class SettingsModal extends LitElement implements Layer {
|
||||
SoundManager.setBackgroundMusicVolume(
|
||||
this.userSettings.backgroundMusicVolume(),
|
||||
);
|
||||
SoundManager.setSoundEffectsVolume(this.userSettings.soundEffectsVolume());
|
||||
this.eventBus.on(ShowSettingsModalEvent, (event) => {
|
||||
this.isVisible = event.isVisible;
|
||||
this.shouldPause = event.shouldPause;
|
||||
@@ -162,6 +163,13 @@ export class SettingsModal extends LitElement implements Layer {
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
private onSoundEffectsVolumeChange(event: Event) {
|
||||
const volume = parseFloat((event.target as HTMLInputElement).value) / 100;
|
||||
this.userSettings.setSoundEffectsVolume(volume);
|
||||
SoundManager.setSoundEffectsVolume(volume);
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.isVisible) {
|
||||
return null;
|
||||
@@ -221,6 +229,33 @@ export class SettingsModal extends LitElement implements Layer {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded text-white transition-colors"
|
||||
>
|
||||
<img
|
||||
src=${musicIcon}
|
||||
alt="soundEffectsIcon"
|
||||
width="20"
|
||||
height="20"
|
||||
/>
|
||||
<div class="flex-1">
|
||||
<div class="font-medium">
|
||||
${translateText("user_setting.sound_effects_volume")}
|
||||
</div>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="100"
|
||||
.value=${this.userSettings.soundEffectsVolume() * 100}
|
||||
@input=${this.onSoundEffectsVolumeChange}
|
||||
class="w-full border border-slate-500 rounded-lg"
|
||||
/>
|
||||
</div>
|
||||
<div class="text-sm text-slate-400">
|
||||
${Math.round(this.userSettings.soundEffectsVolume() * 100)}%
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded text-white transition-colors"
|
||||
@click="${this.onTerrainButtonClick}"
|
||||
|
||||
@@ -1,22 +1,42 @@
|
||||
import { Howl, Howler } from "howler";
|
||||
import { Howl } from "howler";
|
||||
import of4 from "../../../proprietary/sounds/music/of4.mp3";
|
||||
import openfront from "../../../proprietary/sounds/music/openfront.mp3";
|
||||
import war from "../../../proprietary/sounds/music/war.mp3";
|
||||
import kaChingSound from "../../../resources/sounds/effects/ka-ching.mp3";
|
||||
|
||||
export enum SoundEffect {
|
||||
KaChing = "ka-ching",
|
||||
}
|
||||
|
||||
class SoundManager {
|
||||
private backgroundMusic: Howl[] = [];
|
||||
private currentTrack: number = 0;
|
||||
private soundEffects: Map<SoundEffect, Howl> = new Map();
|
||||
private soundEffectsVolume: number = 1;
|
||||
private backgroundMusicVolume: number = 0;
|
||||
|
||||
constructor() {
|
||||
this.backgroundMusic = [
|
||||
new Howl({ src: [of4], loop: false, onend: this.playNext.bind(this) }),
|
||||
new Howl({
|
||||
src: [of4],
|
||||
loop: false,
|
||||
onend: this.playNext.bind(this),
|
||||
volume: 0,
|
||||
}),
|
||||
new Howl({
|
||||
src: [openfront],
|
||||
loop: false,
|
||||
onend: this.playNext.bind(this),
|
||||
volume: 0,
|
||||
}),
|
||||
new Howl({
|
||||
src: [war],
|
||||
loop: false,
|
||||
onend: this.playNext.bind(this),
|
||||
volume: 0,
|
||||
}),
|
||||
new Howl({ src: [war], loop: false, onend: this.playNext.bind(this) }),
|
||||
];
|
||||
this.setBackgroundMusicVolume(0);
|
||||
this.loadSoundEffect(SoundEffect.KaChing, kaChingSound);
|
||||
}
|
||||
|
||||
public playBackgroundMusic(): void {
|
||||
@@ -35,14 +55,55 @@ class SoundManager {
|
||||
}
|
||||
|
||||
public setBackgroundMusicVolume(volume: number): void {
|
||||
const newVolume = Math.max(0, Math.min(1, volume));
|
||||
Howler.volume(newVolume);
|
||||
this.backgroundMusicVolume = Math.max(0, Math.min(1, volume));
|
||||
this.backgroundMusic.forEach((track) => {
|
||||
track.volume(this.backgroundMusicVolume);
|
||||
});
|
||||
}
|
||||
|
||||
private playNext(): void {
|
||||
this.currentTrack = (this.currentTrack + 1) % this.backgroundMusic.length;
|
||||
this.playBackgroundMusic();
|
||||
}
|
||||
|
||||
public loadSoundEffect(name: SoundEffect, src: string): void {
|
||||
if (!this.soundEffects.has(name)) {
|
||||
const sound = new Howl({
|
||||
src: [src],
|
||||
volume: this.soundEffectsVolume,
|
||||
});
|
||||
this.soundEffects.set(name, sound);
|
||||
}
|
||||
}
|
||||
|
||||
public playSoundEffect(name: SoundEffect): void {
|
||||
const sound = this.soundEffects.get(name);
|
||||
if (sound) {
|
||||
sound.play();
|
||||
}
|
||||
}
|
||||
|
||||
public setSoundEffectsVolume(volume: number): void {
|
||||
this.soundEffectsVolume = Math.max(0, Math.min(1, volume));
|
||||
this.soundEffects.forEach((sound) => {
|
||||
sound.volume(this.soundEffectsVolume);
|
||||
});
|
||||
}
|
||||
|
||||
public stopSoundEffect(name: SoundEffect): void {
|
||||
const sound = this.soundEffects.get(name);
|
||||
if (sound) {
|
||||
sound.stop();
|
||||
}
|
||||
}
|
||||
|
||||
public unloadSoundEffect(name: SoundEffect): void {
|
||||
const sound = this.soundEffects.get(name);
|
||||
if (sound) {
|
||||
sound.unload();
|
||||
this.soundEffects.delete(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new SoundManager();
|
||||
|
||||
@@ -176,4 +176,12 @@ export class UserSettings {
|
||||
setBackgroundMusicVolume(volume: number): void {
|
||||
this.setFloat("settings.backgroundMusicVolume", volume);
|
||||
}
|
||||
|
||||
soundEffectsVolume(): number {
|
||||
return this.getFloat("settings.soundEffectsVolume", 1);
|
||||
}
|
||||
|
||||
setSoundEffectsVolume(volume: number): void {
|
||||
this.setFloat("settings.soundEffectsVolume", volume);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user