From 1a0a3dbf2dead0311ea03047e50e8e2cb81ce9f6 Mon Sep 17 00:00:00 2001 From: Yoshie2000 Date: Sun, 30 Aug 2020 21:08:30 +0200 Subject: [PATCH] Added music & sound volume sliders in the settings menu --- src/css/common.scss | 112 +++++++++++-------------- src/js/platform/browser/sound.js | 11 ++- src/js/platform/sound.js | 60 +++++++++++-- src/js/profile/application_settings.js | 92 ++++++++++++-------- src/js/profile/setting_types.js | 64 +++++++++++++- translations/base-de.yaml | 10 +++ translations/base-en.yaml | 10 +++ 7 files changed, 249 insertions(+), 110 deletions(-) diff --git a/src/css/common.scss b/src/css/common.scss index c768b117..d44d7f63 100644 --- a/src/css/common.scss +++ b/src/css/common.scss @@ -1,5 +1,4 @@ // Common classes and style - * { margin: 0; padding: 0; @@ -14,7 +13,6 @@ body { overflow: hidden; font-family: $mainFont; font-synthesis: none; - position: fixed; top: 0; left: 0; @@ -26,19 +24,15 @@ html { position: fixed; // scroll-behavior: smooth; background: $mainBgColor; - // Disable zooming and thus -ms-touch-action: pan-x, pan-y; touch-action: pan-x, pan-y; -ms-content-zooming: none; - top: 0; left: 0; bottom: 0; right: 0; - background: #dee1ea; - @include DarkThemeOverride { background: $darkModeGameBackground; } @@ -50,7 +44,6 @@ body { -moz-user-select: none; -ms-user-select: none; background: inherit !important; - text-transform: none; white-space: normal; word-break: normal; @@ -66,19 +59,17 @@ body { scrollbar-width: 6px; -webkit-font-smoothing: antialiased; // -webkit-overflow-scrolling: touch; /* stop scrolling immediately */ - -webkit-touch-callout: none; /* prevent callout to copy image, etc when tap to hold */ - -webkit-text-size-adjust: none; /* prevent webkit from resizing text to fit */ - + -webkit-touch-callout: none; + /* prevent callout to copy image, etc when tap to hold */ + -webkit-text-size-adjust: none; + /* prevent webkit from resizing text to fit */ // Internet explorer scrollbar-face-color: #888; scrollbar-track-color: rgba(255, 255, 255, 0.1); - // Firefox scrollbar-color: #cdd0d4 rgba(#000, 0.05); - overflow: hidden; @include Text; - &.externalAdOpen { &::before { text-transform: uppercase; @@ -97,7 +88,6 @@ body { justify-content: center; align-items: center; color: #fff; - @include InlineAnimation(1s ease-in-out infinite) { 50% { transform: scale(1.05); @@ -105,9 +95,7 @@ body { } } } - // For recording the bg video - // filter: blur(5px); // &::after { // position: fixed; @@ -122,20 +110,24 @@ body { } img { - -webkit-touch-callout: none; /* prevent callout to copy image, etc when tap to hold */ + -webkit-touch-callout: none; + /* prevent callout to copy image, etc when tap to hold */ } i { font-style: normal; } + b, strong { font-weight: normal; } + u, a { text-decoration: none; } + input, textarea, select { @@ -152,7 +144,6 @@ button { cursor: pointer; position: relative; @include TextShadow3D; - &.prefab_BuyButtonWithResources { display: flex; box-sizing: border-box; @@ -163,27 +154,23 @@ button { justify-content: center; align-items: center; @include S(width, 85px); - &.tooExpensive { color: $colorRedBright; background-color: #555; cursor: default; } - .cost_entry { display: flex; flex-grow: 1; justify-content: center; align-items: center; } - b { display: flex; flex-grow: 1; justify-content: center; align-items: center; } - &.tooExpensive { cursor: default !important; background-color: #565859 !important; @@ -214,7 +201,6 @@ button { // color: $accentColorDark; letter-spacing: 0.05em !important; // box-shadow: 0 #{D(1px)} #{D(2px)} 0 rgba(0, 10, 20, 0.2); - .keybinding { @include S(bottom, -2.5px); @include S(right, -2px); @@ -226,10 +212,13 @@ button { } ::selection { - background: $colorGreenBright; /* WebKit/Blink Browsers */ + background: $colorGreenBright; + /* WebKit/Blink Browsers */ } + ::-moz-selection { - background: $colorGreenBright; /* Gecko Browsers */ + background: $colorGreenBright; + /* Gecko Browsers */ } input[type="text"], @@ -244,35 +233,27 @@ input[type="email"] { background: lighten($mainBgColor, 8); color: #eee; text-align: left; - user-select: text !important; pointer-events: all !important; - @include Text; @include IncreasedClickArea(15px); @include S(border-radius, $globalBorderRadius); - &::placeholder { color: #fff; opacity: 0.4; } - transition: background-color 0.1s ease-in-out !important; @include TextShadow3D(#fff); @include BoxShadow3D(lighten($mainBgColor, 30)); - &:focus { @include BoxShadow3D(lighten($mainBgColor, 35)); } - &.errored { @include BoxShadow3D(mix(lighten($mainBgColor, 30), #f77, 25%)); - &:focus { @include BoxShadow3D(mix(lighten($mainBgColor, 50), #f77, 25%)); } } - &.input-token { @include SuperHeading; text-align: center; @@ -324,7 +305,6 @@ canvas { // &.unsmoothed { // } letter-spacing: 0 !important; - transform: translateZ(0); backface-visibility: hidden; -webkit-backface-visibility: hidden; @@ -404,13 +384,11 @@ canvas { align-items: center; justify-content: center; text-transform: uppercase; - @include Text; @include TextShadow3D; opacity: 1; z-index: 20; color: #393747; - &::after { content: " "; background: uiResource("loading.svg") center center / contain no-repeat; @@ -421,7 +399,6 @@ canvas { display: inline-block; vertical-align: middle; } - @include DarkThemeOverride { color: #fff; } @@ -443,7 +420,6 @@ canvas { .prefab_FeatureComingSoon { position: relative; - &::after { @include S(top, -5px); @include S(left, -5px); @@ -461,10 +437,8 @@ canvas { @include PlainText; text-transform: uppercase; } - opacity: 0.6; - - > * { + >* { opacity: 0.5 !important; } } @@ -488,14 +462,12 @@ canvas { align-items: center; justify-content: center; flex-direction: column; - .loadingImage { background: uiResource("loading.svg") center center / #{D(60px)} no-repeat; width: 100%; display: flex; flex-grow: 1; } - .loadingStatus { position: absolute; @include S(left, 20px); @@ -503,13 +475,11 @@ canvas { @include S(bottom, 30px); @include Text; @include TextShadow3D(#aaa); - display: flex; flex-direction: column; justify-content: center; align-items: center; - - > .bar { + >.bar { display: none; @include S(margin-top, 15px); width: 80vw; @@ -517,7 +487,6 @@ canvas { position: relative; @include TextShadow3D(#fff); height: 2px; - .inner { position: absolute !important; top: 0; @@ -526,9 +495,7 @@ canvas { z-index: 1; @include BoxShadow3D($themeColor, $size: 1px); @include S(border-radius, $globalBorderRadius); - transform-origin: 0% 50%; - @include InlineAnimation(1.3s ease-in-out infinite) { 0% { background-color: darken($themeColor, 5); @@ -544,7 +511,6 @@ canvas { } } } - .status { display: none; position: relative; @@ -579,11 +545,9 @@ canvas { &.loading { opacity: 0.2; } - &:hover { background-color: darken($bgColor, 5); } - .knob { @include S(width, 20px); @include S(height, 17px); @@ -594,20 +558,51 @@ canvas { @include BorderRadius(20px); @include BoxShadow3D(#fff, $size: 1px); } - &.checked { background-color: $themeColor; @include BoxShadow3D($themeColor, $size: 2px); .knob { @include S(margin-left, 15px); } - &:hover { background-color: lighten($themeColor, 15); } } } +.range { + display: flex; + align-items: center; + justify-content: center; +} + +.range-input { + cursor: pointer; + background-color: transparent; + width: 100px; + height: 10px; + transform: translate(7px, 2px); + &::-webkit-slider-runnable-track { + background-color: darken($mainBgColor, 3); + color: darken($mainBgColor, 3); + height: 16px; + border-radius: 8px; + } + &::-webkit-slider-thumb { + appearance: none; + -webkit-appearance: none; + box-shadow: inset 0 0 0 10px $themeColor; + background-color: transparent; + width: 20px; + height: 20px; + border-radius: 50%; + transition: 0.3s; + } + &:hover::-webkit-slider-thumb { + box-shadow: inset 0 0 0 10px lighten($themeColor, 15); + } +} + .keybinding { background: #fff; text-transform: uppercase; @@ -615,14 +610,13 @@ canvas { @include PlainText; @include S(border-radius, $globalBorderRadius); &, - > span { + >span { @include S(font-size, 9px); @include S(line-height, 11px); font-weight: bold !important; text-shadow: none !important; // font-family: Arial, sans-serif !important; } - font-weight: bold; color: $accentColorDark; text-align: center; @@ -638,7 +632,6 @@ canvas { @include S(height, 12px); overflow: hidden; border: #{D(0px)} solid $accentColorDark; - .keybinding_space { @include S(font-size, 17px); @include S(line-height, 11px); @@ -651,7 +644,6 @@ canvas { .xpaystation-widget-lightbox-overlay { background: rgba($mainBgColor, 0.94); } - &, iframe { pointer-events: all; @@ -692,9 +684,7 @@ iframe { * { pointer-events: all; } - background: rgba($mainBgColor, 0.94) !important; - .cpmsvideoclosebanner { font-family: GameFont !important; font-size: 16px !important; @@ -707,4 +697,4 @@ iframe { transform: translateY(2px); } } -} +} \ No newline at end of file diff --git a/src/js/platform/browser/sound.js b/src/js/platform/browser/sound.js index 508dcf8c..84fcf948 100644 --- a/src/js/platform/browser/sound.js +++ b/src/js/platform/browser/sound.js @@ -146,9 +146,10 @@ class MusicInstance extends MusicInstanceInterface { return this.playing; } - play() { + play(volume) { if (this.howl) { this.playing = true; + this.howl.volume(volume); if (this.instance) { this.howl.play(this.instance); } else { @@ -157,6 +158,12 @@ class MusicInstance extends MusicInstanceInterface { } } + setVolume(volume) { + if (this.howl) { + this.howl.volume(volume); + } + } + deinitialize() { if (this.howl) { this.howl.unload(); @@ -204,4 +211,4 @@ export class SoundImplBrowser extends SoundInterface { deinitialize() { return super.deinitialize().then(() => Howler.unload()); } -} +} \ No newline at end of file diff --git a/src/js/platform/sound.js b/src/js/platform/sound.js index 0d509bee..a2050e67 100644 --- a/src/js/platform/sound.js +++ b/src/js/platform/sound.js @@ -61,7 +61,11 @@ export class MusicInstanceInterface { abstract; } - play() { + play(volume) { + abstract; + } + + setVolume(volume) { abstract; } @@ -101,6 +105,9 @@ export class SoundInterface { this.musicMuted = false; this.soundsMuted = false; + + this.musicVolume = 1.0; + this.soundVolume = 1.0; } /** @@ -122,6 +129,8 @@ export class SoundInterface { this.musicMuted = this.app.settings.getAllSettings().musicMuted; this.soundsMuted = this.app.settings.getAllSettings().soundsMuted; + this.musicVolume = this.app.settings.getAllSettings().musicVolume; + this.soundVolume = this.app.settings.getAllSettings().soundsVolume; if (G_IS_DEV && globalConfig.debug.disableMusic) { this.musicMuted = true; @@ -189,7 +198,7 @@ export class SoundInterface { } } else { if (this.currentMusic) { - this.currentMusic.play(); + this.currentMusic.play(this.musicVolume); } } } @@ -202,6 +211,41 @@ export class SoundInterface { this.soundsMuted = muted; } + /** + * Returns the music volume + * @returns {number} + */ + getMusicVolume() { + return this.musicVolume; + } + + /** + * Returns the sound volume + * @returns {number} + */ + getSoundVolume() { + return this.soundVolume; + } + + /** + * Sets the music volume + * @param {number} volume + */ + setMusicVolume(volume) { + this.musicVolume = clamp(volume, 0, 1);; + if (this.currentMusic) { + this.currentMusic.setVolume(this.musicVolume); + } + } + + /** + * Sets the sound volume + * @param {number} volume + */ + setSoundVolume(volume) { + this.soundVolume = clamp(volume, 0, 1); + } + /** * Focus change handler, called by the pap * @param {boolean} pageIsVisible @@ -211,7 +255,7 @@ export class SoundInterface { if (this.currentMusic) { if (pageIsVisible) { if (!this.currentMusic.isPlaying() && !this.musicMuted) { - this.currentMusic.play(); + this.currentMusic.play(this.musicVolume); } } else { this.currentMusic.stop(); @@ -230,7 +274,7 @@ export class SoundInterface { logger.warn("Sound", key, "not found, probably not loaded yet"); return; } - this.sounds[key].play(1.0); + this.sounds[key].play(this.soundVolume); } /** @@ -253,9 +297,9 @@ export class SoundInterface { return; } - let volume = 1.0; + let volume = this.soundVolume; if (!root.camera.isWorldPointOnScreen(worldPosition)) { - volume = 0.2; + volume = this.soundVolume / 5; // In the old implementation this value was fixed to 0.2 => 20% of 1.0 } volume *= clamp(root.camera.zoomLevel / 3); this.sounds[key].play(clamp(volume)); @@ -277,8 +321,8 @@ export class SoundInterface { this.currentMusic = music; if (music && this.pageIsVisible && !this.musicMuted) { logger.log("Starting", this.currentMusic.key); - music.play(); + music.play(this.musicVolume); } } } -} +} \ No newline at end of file diff --git a/src/js/profile/application_settings.js b/src/js/profile/application_settings.js index 84467b5b..def60b6f 100644 --- a/src/js/profile/application_settings.js +++ b/src/js/profile/application_settings.js @@ -3,7 +3,7 @@ import { Application } from "../application"; /* typehints:end */ import { ReadWriteProxy } from "../core/read_write_proxy"; -import { BoolSetting, EnumSetting, BaseSetting } from "./setting_types"; +import { BoolSetting, EnumSetting, RangeSetting, BaseSetting } from "./setting_types"; import { createLogger } from "../core/logging"; import { ExplainedResult } from "../core/explained_result"; import { THEMES, THEME, applyGameTheme } from "../game/theme"; @@ -23,8 +23,7 @@ export const enumCategories = { advanced: "advanced", }; -export const uiScales = [ - { +export const uiScales = [{ id: "super_small", size: 0.6, }, @@ -46,8 +45,7 @@ export const uiScales = [ }, ]; -export const scrollWheelSensitivities = [ - { +export const scrollWheelSensitivities = [{ id: "super_slow", scale: 0.25, }, @@ -69,8 +67,7 @@ export const scrollWheelSensitivities = [ }, ]; -export const movementSpeeds = [ - { +export const movementSpeeds = [{ id: "super_slow", multiplier: 0.25, }, @@ -96,8 +93,7 @@ export const movementSpeeds = [ }, ]; -export const autosaveIntervals = [ - { +export const autosaveIntervals = [{ id: "one_minute", seconds: 60, }, @@ -154,9 +150,9 @@ export const allApplicationSettings = [ category: enumCategories.userInterface, restartRequired: false, changeCb: - /** - * @param {Application} app - */ + /** + * @param {Application} app + */ (app, id) => app.updateAfterUiScaleChanged(), }), @@ -176,6 +172,22 @@ export const allApplicationSettings = [ */ (app, value) => app.sound.setMusicMuted(value) ), + new RangeSetting( + "soundVolume", + enumCategories.general, + /** + * @param {Application} app + */ + (app, value) => app.sound.setSoundVolume(value / 100.0) + ), + new RangeSetting( + "musicVolume", + enumCategories.general, + /** + * @param {Application} app + */ + (app, value) => app.sound.setMusicVolume(value / 100.0) + ), new BoolSetting( "fullscreen", @@ -187,8 +199,7 @@ export const allApplicationSettings = [ if (app.platformWrapper.getSupportsFullscreen()) { app.platformWrapper.setFullscreen(value); } - }, - !IS_DEMO + }, !IS_DEMO ), new BoolSetting( @@ -209,13 +220,13 @@ export const allApplicationSettings = [ category: enumCategories.userInterface, restartRequired: false, changeCb: - /** - * @param {Application} app - */ + /** + * @param {Application} app + */ (app, id) => { - applyGameTheme(id); - document.documentElement.setAttribute("data-theme", id); - }, + applyGameTheme(id); + document.documentElement.setAttribute("data-theme", id); + }, enabled: !IS_DEMO, }), @@ -226,9 +237,9 @@ export const allApplicationSettings = [ category: enumCategories.advanced, restartRequired: false, changeCb: - /** - * @param {Application} app - */ + /** + * @param {Application} app + */ (app, id) => null, }), @@ -239,9 +250,9 @@ export const allApplicationSettings = [ category: enumCategories.advanced, restartRequired: false, changeCb: - /** - * @param {Application} app - */ + /** + * @param {Application} app + */ (app, id) => app.updateAfterUiScaleChanged(), }), @@ -289,6 +300,9 @@ class SettingsStorage { this.soundsMuted = false; this.musicMuted = false; + this.soundVolume = 1.0; + this.musicVolume = 1.0; + this.theme = "light"; this.refreshRate = "60"; this.scrollWheelSensitivity = "regular"; @@ -336,7 +350,7 @@ export class ApplicationSettings extends ReadWriteProxy { } }) - .then(() => this.writeAsync()); + .then(() => this.writeAsync()); } save() { @@ -437,7 +451,7 @@ export class ApplicationSettings extends ReadWriteProxy { /** * @param {string} key - * @param {string|boolean} value + * @param {string|boolean|number} value */ updateSetting(key, value) { for (let i = 0; i < allApplicationSettings.length; ++i) { @@ -472,12 +486,12 @@ export class ApplicationSettings extends ReadWriteProxy { * @param {string} id */ resetKeybindingOverride(id) { - delete this.getAllSettings().keybindingOverrides[id]; - return this.writeAsync(); - } - /** - * Resets all keybinding overrides - */ + delete this.getAllSettings().keybindingOverrides[id]; + return this.writeAsync(); + } + /** + * Resets all keybinding overrides + */ resetKeybindingOverrides() { this.getAllSettings().keybindingOverrides = {}; return this.writeAsync(); @@ -511,7 +525,7 @@ export class ApplicationSettings extends ReadWriteProxy { } getCurrentVersion() { - return 23; + return 24; } /** @param {{settings: SettingsStorage, version: number}} data */ @@ -614,6 +628,12 @@ export class ApplicationSettings extends ReadWriteProxy { data.version = 23; } + if (data.version < 24) { + data.settings.musicVolume = 1.0; + data.settings.soundVolume = 1.0; + data.version = 24; + } + return ExplainedResult.good(); } -} +} \ No newline at end of file diff --git a/src/js/profile/setting_types.js b/src/js/profile/setting_types.js index 9e361f66..141c36ff 100644 --- a/src/js/profile/setting_types.js +++ b/src/js/profile/setting_types.js @@ -85,8 +85,7 @@ export class BaseSetting { export class EnumSetting extends BaseSetting { constructor( - id, - { + id, { options, valueGetter, textGetter, @@ -111,7 +110,7 @@ export class EnumSetting extends BaseSetting { } getHtml() { - return ` + return `
${this.enabled ? "" : `${T.demo.settingNotAvailable}`}
@@ -220,3 +219,62 @@ export class BoolSetting extends BaseSetting { return typeof value === "boolean"; } } + +export class RangeSetting extends BaseSetting { + constructor(id, category, changeCb = null, enabled = true, defaultValue = 100, minValue = 0, maxValue = 100, stepSize = 1) { + super(id, category, changeCb, enabled); + + this.defaultValue = defaultValue; + this.minValue = minValue; + this.maxValue = maxValue; + this.stepSize = stepSize; + } + + getHtml() { + return ` +
+ ${this.enabled ? "" : `${T.demo.settingNotAvailable}`} + +
+ +
+ + +
+
+
+ ${T.settings.labels[this.id].description} +
+
`; + } + + bind(app, element, dialogs) { + this.app = app; + this.element = element; + this.dialogs = dialogs; + + this.element.querySelector(".range-input").addEventListener("input", () => { + this.modify(); + }); + } + + syncValueToElement() { + const value = this.app.settings.getSetting(this.id); + this.element.querySelector(".range-input").value = value; + this.element.querySelector(".range-label").innerText = value; + } + + modify() { + const newValue = Number(this.element.querySelector(".range-input").value); + this.app.settings.updateSetting(this.id, newValue); + this.syncValueToElement(); + + if (this.changeCb) { + this.changeCb(this.app, newValue); + } + } + + validate(value) { + return typeof value === "number"; + } +} \ No newline at end of file diff --git a/translations/base-de.yaml b/translations/base-de.yaml index f74ac50f..cdc23a2e 100644 --- a/translations/base-de.yaml +++ b/translations/base-de.yaml @@ -732,6 +732,16 @@ settings: description: >- Bei der Aktivierung wird die Musik stummgeschaltet. + soundVolume: + title: Geräuschlautstärke + description: >- + Ändert die Lautstärke von Geräuschen. + + musicVolume: + title: Musiklautstärke + description: >- + Ändert die Lautstärke der Musik. + theme: title: Farbmodus description: >- diff --git a/translations/base-en.yaml b/translations/base-en.yaml index 8ef3e080..a6566994 100644 --- a/translations/base-en.yaml +++ b/translations/base-en.yaml @@ -801,6 +801,16 @@ settings: description: >- If enabled, mutes all music. + soundVolume: + title: Sound Volume + description: >- + Set the volume for sound effects + + musicVolume: + title: Music Volume + description: >- + Set the volume for music + theme: title: Game theme description: >-