mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-30 22:02:03 +00:00
initial commit
This commit is contained in:
@@ -6,6 +6,7 @@ import { GameUpdateType } from "../../../core/game/GameUpdates";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { UserSettings } from "../../../core/game/UserSettings";
|
||||
import { AlternateViewEvent, RefreshGraphicsEvent } from "../../InputHandler";
|
||||
import { soundManager } from "../../SoundManager";
|
||||
import { PauseGameEvent } from "../../Transport";
|
||||
import { translateText } from "../../Utils";
|
||||
import { Layer } from "./Layer";
|
||||
@@ -75,9 +76,7 @@ export class OptionsMenu extends LitElement implements Layer {
|
||||
private onExitButtonClick() {
|
||||
const isAlive = this.game.myPlayer()?.isAlive();
|
||||
if (isAlive) {
|
||||
const isConfirmed = confirm(
|
||||
translateText("help_modal.exit_confirmation"),
|
||||
);
|
||||
const isConfirmed = confirm("Are you sure you want to exit the game?");
|
||||
if (!isConfirmed) return;
|
||||
}
|
||||
// redirect to the home page
|
||||
@@ -137,6 +136,21 @@ export class OptionsMenu extends LitElement implements Layer {
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
private onMuteButtonClick() {
|
||||
if (soundManager.isMuted()) {
|
||||
soundManager.unmute();
|
||||
} else {
|
||||
soundManager.mute();
|
||||
}
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
private onVolumeChange(e: Event) {
|
||||
const volume = parseFloat((e.target as HTMLInputElement).value);
|
||||
soundManager.setMasterVolume(volume);
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
init() {
|
||||
console.log("init called from OptionsMenu");
|
||||
this.showPauseButton =
|
||||
@@ -260,6 +274,21 @@ export class OptionsMenu extends LitElement implements Layer {
|
||||
? "Focus locked"
|
||||
: "Hover focus"),
|
||||
})} -->
|
||||
|
||||
${button({
|
||||
onClick: this.onMuteButtonClick,
|
||||
title: "Mute",
|
||||
children: soundManager.isMuted() ? "🔇" : "🔊",
|
||||
})}
|
||||
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.01"
|
||||
.value=${soundManager.getMasterVolume()}
|
||||
@input=${this.onVolumeChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
import { UserSettings } from "../core/game/UserSettings";
|
||||
|
||||
class SoundManager {
|
||||
private static instance: SoundManager;
|
||||
private audioContext: AudioContext;
|
||||
private soundBuffers: Map<string, AudioBuffer> = new Map();
|
||||
private musicSource: AudioBufferSourceNode | null = null;
|
||||
private userSettings: UserSettings;
|
||||
private masterVolume: GainNode;
|
||||
private musicVolume: GainNode;
|
||||
private soundEffectsVolume: GainNode;
|
||||
private muted: boolean;
|
||||
private activeSources: Map<string, AudioBufferSourceNode[]> = new Map();
|
||||
|
||||
private constructor() {
|
||||
this.audioContext = new (window.AudioContext ||
|
||||
(window as any).webkitAudioContext)();
|
||||
this.userSettings = new UserSettings();
|
||||
this.masterVolume = this.audioContext.createGain();
|
||||
this.musicVolume = this.audioContext.createGain();
|
||||
this.soundEffectsVolume = this.audioContext.createGain();
|
||||
this.musicVolume.connect(this.masterVolume);
|
||||
this.soundEffectsVolume.connect(this.masterVolume);
|
||||
this.masterVolume.connect(this.audioContext.destination);
|
||||
this.muted = this.userSettings.getMuted();
|
||||
this.setMasterVolume(this.userSettings.getVolume());
|
||||
|
||||
if (this.muted) {
|
||||
this.mute();
|
||||
}
|
||||
|
||||
if (this.userSettings.getMuteMusic()) {
|
||||
this.muteMusic();
|
||||
}
|
||||
|
||||
if (this.userSettings.getMuteSoundEffects()) {
|
||||
this.muteSoundEffects();
|
||||
}
|
||||
}
|
||||
|
||||
public static getInstance(): SoundManager {
|
||||
if (!SoundManager.instance) {
|
||||
SoundManager.instance = new SoundManager();
|
||||
}
|
||||
return SoundManager.instance;
|
||||
}
|
||||
|
||||
public async loadSounds(
|
||||
sounds: { name: string; path: string }[],
|
||||
): Promise<void> {
|
||||
const promises = sounds.map((sound) =>
|
||||
this.loadSound(sound.name, sound.path),
|
||||
);
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
private async loadSound(name: string, path: string): Promise<void> {
|
||||
try {
|
||||
const response = await fetch(path);
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
const audioBuffer = await this.audioContext.decodeAudioData(arrayBuffer);
|
||||
this.soundBuffers.set(name, audioBuffer);
|
||||
} catch (error) {
|
||||
console.error(`Failed to load sound: ${path}`, error);
|
||||
}
|
||||
}
|
||||
|
||||
public playSound(name: string, loop: boolean = false): void {
|
||||
const soundBuffer = this.soundBuffers.get(name);
|
||||
if (soundBuffer) {
|
||||
const source = this.audioContext.createBufferSource();
|
||||
source.buffer = soundBuffer;
|
||||
source.loop = loop;
|
||||
source.connect(this.soundEffectsVolume);
|
||||
source.start(0);
|
||||
|
||||
if (!this.activeSources.has(name)) {
|
||||
this.activeSources.set(name, []);
|
||||
}
|
||||
this.activeSources.get(name)?.push(source);
|
||||
|
||||
source.onended = () => {
|
||||
const sources = this.activeSources.get(name);
|
||||
if (sources) {
|
||||
const index = sources.indexOf(source);
|
||||
if (index > -1) {
|
||||
sources.splice(index, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public stopSound(name: string): void {
|
||||
const sources = this.activeSources.get(name);
|
||||
if (sources) {
|
||||
sources.forEach((source) => source.stop());
|
||||
this.activeSources.set(name, []);
|
||||
}
|
||||
}
|
||||
|
||||
public playMusic(name: string): void {
|
||||
if (this.musicSource) {
|
||||
this.musicSource.stop();
|
||||
}
|
||||
const musicBuffer = this.soundBuffers.get(name);
|
||||
if (musicBuffer) {
|
||||
this.musicSource = this.audioContext.createBufferSource();
|
||||
this.musicSource.buffer = musicBuffer;
|
||||
this.musicSource.loop = true;
|
||||
this.musicSource.connect(this.musicVolume);
|
||||
this.musicSource.start(0);
|
||||
}
|
||||
}
|
||||
|
||||
public setMasterVolume(volume: number): void {
|
||||
this.masterVolume.gain.setValueAtTime(
|
||||
volume,
|
||||
this.audioContext.currentTime,
|
||||
);
|
||||
this.userSettings.setVolume(volume);
|
||||
}
|
||||
|
||||
public getMasterVolume(): number {
|
||||
return this.masterVolume.gain.value;
|
||||
}
|
||||
|
||||
public mute(): void {
|
||||
this.masterVolume.gain.setValueAtTime(0, this.audioContext.currentTime);
|
||||
this.muted = true;
|
||||
this.userSettings.setMuted(true);
|
||||
}
|
||||
|
||||
public unmute(): void {
|
||||
this.masterVolume.gain.setValueAtTime(
|
||||
this.userSettings.getVolume(),
|
||||
this.audioContext.currentTime,
|
||||
);
|
||||
this.muted = false;
|
||||
this.userSettings.setMuted(false);
|
||||
}
|
||||
|
||||
public isMuted(): boolean {
|
||||
return this.muted;
|
||||
}
|
||||
|
||||
public muteMusic(): void {
|
||||
this.musicVolume.gain.setValueAtTime(0, this.audioContext.currentTime);
|
||||
this.userSettings.setMuteMusic(true);
|
||||
}
|
||||
|
||||
public unmuteMusic(): void {
|
||||
this.musicVolume.gain.setValueAtTime(1, this.audioContext.currentTime);
|
||||
this.userSettings.setMuteMusic(false);
|
||||
}
|
||||
|
||||
public muteSoundEffects(): void {
|
||||
this.soundEffectsVolume.gain.setValueAtTime(0, this.audioContext.currentTime);
|
||||
this.userSettings.setMuteSoundEffects(true);
|
||||
}
|
||||
|
||||
public unmuteSoundEffects(): void {
|
||||
this.soundEffectsVolume.gain.setValueAtTime(1, this.audioContext.currentTime);
|
||||
this.userSettings.setMuteSoundEffects(false);
|
||||
}
|
||||
|
||||
public playSpatialSound(name: string, x: number, y: number, z: number): void {
|
||||
const soundBuffer = this.soundBuffers.get(name);
|
||||
if (soundBuffer) {
|
||||
const source = this.audioContext.createBufferSource();
|
||||
source.buffer = soundBuffer;
|
||||
|
||||
const panner = this.audioContext.createPanner();
|
||||
panner.panningModel = "HRTF";
|
||||
panner.distanceModel = "inverse";
|
||||
panner.refDistance = 1;
|
||||
panner.maxDistance = 10000;
|
||||
panner.rolloffFactor = 1;
|
||||
panner.coneInnerAngle = 360;
|
||||
panner.coneOuterAngle = 0;
|
||||
panner.coneOuterGain = 0;
|
||||
panner.setPosition(x, y, z);
|
||||
|
||||
source.connect(panner);
|
||||
panner.connect(this.masterVolume);
|
||||
source.start(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const soundManager = SoundManager.getInstance();
|
||||
@@ -87,6 +87,42 @@ export class UserSettings {
|
||||
}
|
||||
}
|
||||
|
||||
getVolume(): number {
|
||||
const value = localStorage.getItem("volume");
|
||||
if (value) {
|
||||
return parseFloat(value);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
setVolume(volume: number): void {
|
||||
localStorage.setItem("volume", volume.toString());
|
||||
}
|
||||
|
||||
getMuted(): boolean {
|
||||
return this.get("muted", false);
|
||||
}
|
||||
|
||||
setMuted(muted: boolean): void {
|
||||
this.set("muted", muted);
|
||||
}
|
||||
|
||||
getMuteMusic(): boolean {
|
||||
return this.get("muteMusic", false);
|
||||
}
|
||||
|
||||
setMuteMusic(mute: boolean): void {
|
||||
this.set("muteMusic", mute);
|
||||
}
|
||||
|
||||
getMuteSoundEffects(): boolean {
|
||||
return this.get("muteSoundEffects", false);
|
||||
}
|
||||
|
||||
setMuteSoundEffects(mute: boolean): void {
|
||||
this.set("muteSoundEffects", mute);
|
||||
}
|
||||
|
||||
getSelectedPattern(): string | undefined {
|
||||
return localStorage.getItem(PATTERN_KEY) ?? undefined;
|
||||
}
|
||||
|
||||
Vendored
+4
@@ -40,3 +40,7 @@ declare module "*.xml" {
|
||||
const value: string;
|
||||
export default value;
|
||||
}
|
||||
declare module "*.mp3" {
|
||||
const value: string;
|
||||
export default value;
|
||||
}
|
||||
|
||||
@@ -93,6 +93,13 @@ export default async (env, argv) => {
|
||||
filename: "fonts/[name].[contenthash][ext]", // Added content hash and fixed path
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.mp3$/,
|
||||
type: "asset/resource",
|
||||
generator: {
|
||||
filename: "sound/[name].[contenthash][ext]",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
|
||||
Reference in New Issue
Block a user