diff --git a/frontend/src/custom.d.ts b/frontend/src/custom.d.ts
new file mode 100644
index 0000000..3f6ebdd
--- /dev/null
+++ b/frontend/src/custom.d.ts
@@ -0,0 +1,4 @@
+declare module '*.mp3' {
+ const content: string;
+ export default content;
+}
diff --git a/frontend/src/index.html b/frontend/src/index.html
index 7492db1..81e35e0 100644
--- a/frontend/src/index.html
+++ b/frontend/src/index.html
@@ -89,6 +89,11 @@
/>
+
+
Renderer | undefined) => {
);
};
-const toggleSettings = () =>
- (settings.className = settings.className === 'open' ? '' : 'open');
+const toggleSettings = () => {
+ settings.className = settings.className === 'open' ? '' : 'open';
+ SoundHandler.play(Sounds.click);
+};
const applyServerContainerShadows = () => {
const { scrollHeight, clientHeight, scrollTop } = serverContainer;
@@ -104,6 +108,7 @@ const applyServerContainerShadows = () => {
const main = async () => {
try {
let game: Game;
+ SoundHandler.initialize();
handleFullScreen(minimize, maximize);
toggleSettingsButton.addEventListener('click', toggleSettings);
@@ -115,6 +120,7 @@ const main = async () => {
relativeMovementEnabled: enableRelativeMovement,
soundsEnabled: enableSounds,
vibrationEnabled: enableVibration,
+ musicEnabled: enableMusic,
});
logoutButton.addEventListener('click', () => {
diff --git a/frontend/src/scripts/options-handler.ts b/frontend/src/scripts/options-handler.ts
index 1806322..b67b50e 100644
--- a/frontend/src/scripts/options-handler.ts
+++ b/frontend/src/scripts/options-handler.ts
@@ -1,7 +1,10 @@
+import { SoundHandler, Sounds } from './sound-handler';
+
interface Options {
vibrationEnabled: boolean;
soundsEnabled: boolean;
relativeMovementEnabled: boolean;
+ musicEnabled: boolean;
}
export abstract class OptionsHandler {
@@ -10,6 +13,7 @@ export abstract class OptionsHandler {
vibrationEnabled: true,
soundsEnabled: true,
relativeMovementEnabled: false,
+ musicEnabled: true,
};
public static initialize(
@@ -23,6 +27,14 @@ export abstract class OptionsHandler {
...this._options,
...stored,
};
+
+ if (this._options.musicEnabled) {
+ const firstClickListener = () => {
+ document.removeEventListener('click', firstClickListener);
+ SoundHandler.playAmbient();
+ };
+ document.addEventListener('click', firstClickListener);
+ }
}
for (const k in inputElements) {
@@ -30,6 +42,21 @@ export abstract class OptionsHandler {
element.checked = OptionsHandler._options[k as keyof Options];
element.addEventListener('change', function () {
OptionsHandler._options[k as keyof Options] = this.checked;
+ if (!this.checked && k === 'soundsEnabled') {
+ OptionsHandler._options.musicEnabled = false;
+ inputElements.musicEnabled.checked = false;
+ SoundHandler.stopAmbient();
+ }
+
+ if (k === 'musicEnabled') {
+ this.checked ? SoundHandler.playAmbient() : SoundHandler.stopAmbient();
+ }
+
+ if (this.checked && k === 'vibrationEnabled') {
+ navigator.vibrate(100);
+ }
+
+ SoundHandler.play(Sounds.click);
OptionsHandler.save();
});
}
diff --git a/frontend/src/scripts/sound-handler.ts b/frontend/src/scripts/sound-handler.ts
new file mode 100644
index 0000000..97112ac
--- /dev/null
+++ b/frontend/src/scripts/sound-handler.ts
@@ -0,0 +1,44 @@
+import hitSound from '../../static/hit.mp3';
+import shootSound from '../../static/shoot.mp3';
+import clickSound from '../../static/click.mp3';
+import ambientSound from '../../static/ambient.mp3';
+import { OptionsHandler } from './options-handler';
+
+export enum Sounds {
+ hit = 'hit',
+ shoot = 'shoot',
+ click = 'click',
+ ambient = 'ambient',
+}
+
+export abstract class SoundHandler {
+ private static sounds: { [key in Sounds]: HTMLAudioElement };
+
+ public static initialize() {
+ this.sounds = {
+ [Sounds.hit]: new Audio(hitSound),
+ [Sounds.shoot]: new Audio(shootSound),
+ [Sounds.click]: new Audio(clickSound),
+ [Sounds.ambient]: new Audio(ambientSound),
+ };
+ this.sounds.ambient.volume = 0.5;
+ }
+
+ public static play(sound: Sounds) {
+ if (OptionsHandler.options.soundsEnabled) {
+ if (this.sounds[sound].currentTime > 0) {
+ (this.sounds[sound].cloneNode() as HTMLAudioElement).play();
+ } else {
+ this.sounds[sound].play();
+ }
+ }
+ }
+
+ public static playAmbient() {
+ this.sounds.ambient.play();
+ }
+
+ public static stopAmbient() {
+ this.sounds.ambient.pause();
+ }
+}
diff --git a/frontend/static/ambient.mp3 b/frontend/static/ambient.mp3
new file mode 100644
index 0000000..218cc96
Binary files /dev/null and b/frontend/static/ambient.mp3 differ
diff --git a/frontend/static/click.mp3 b/frontend/static/click.mp3
new file mode 100644
index 0000000..50a022c
Binary files /dev/null and b/frontend/static/click.mp3 differ
diff --git a/frontend/static/hit.mp3 b/frontend/static/hit.mp3
new file mode 100644
index 0000000..d088388
Binary files /dev/null and b/frontend/static/hit.mp3 differ
diff --git a/frontend/static/music.svg b/frontend/static/music.svg
new file mode 100644
index 0000000..5f68ba1
--- /dev/null
+++ b/frontend/static/music.svg
@@ -0,0 +1,7 @@
+
\ No newline at end of file
diff --git a/frontend/static/shoot.mp3 b/frontend/static/shoot.mp3
new file mode 100644
index 0000000..435a99e
Binary files /dev/null and b/frontend/static/shoot.mp3 differ
diff --git a/frontend/webpack.config.js b/frontend/webpack.config.js
index d6b7efa..d189f31 100644
--- a/frontend/webpack.config.js
+++ b/frontend/webpack.config.js
@@ -80,7 +80,17 @@ module.exports = {
},
},
{
- test: /\.png$/,
+ test: /\.mp3$/,
+ use: {
+ loader: 'file-loader',
+ query: {
+ outputPath: '/static',
+ name: '[name].[ext]',
+ },
+ },
+ },
+ {
+ test: /og-image.png$/,
use: {
loader: 'file-loader',
query: {