Index: binaries/data/mods/mod/gui/gui.rnc =================================================================== --- binaries/data/mods/mod/gui/gui.rnc +++ binaries/data/mods/mod/gui/gui.rnc @@ -103,6 +103,7 @@ attribute sprite2_pressed { text }?& attribute sprite_selectarea { text }?& attribute square_side { xsd:decimal }?& + attribute step_size positive-decimal }?& attribute textcolor { ccolor }?& attribute textcolor_disabled { ccolor }?& attribute textcolor_over { ccolor }?& Index: binaries/data/mods/mod/gui/gui.rng =================================================================== --- binaries/data/mods/mod/gui/gui.rng +++ binaries/data/mods/mod/gui/gui.rng @@ -410,6 +410,11 @@ + + + + + Index: binaries/data/mods/public/gui/common/gamedescription.js =================================================================== --- binaries/data/mods/public/gui/common/gamedescription.js +++ binaries/data/mods/public/gui/common/gamedescription.js @@ -196,31 +196,25 @@ let title = translateVictoryCondition(victoryCondition.Name); if (victoryCondition.Name == "wonder") - { - let wonderDuration = Math.round(g_GameAttributes.settings.WonderDuration); title = sprintf( translatePluralWithContext( "victory condition", "Wonder (%(min)s minute)", "Wonder (%(min)s minutes)", - wonderDuration + g_GameAttributes.settings.WonderDuration ), - { "min": wonderDuration }); - } + { "min": g_GameAttributes.settings.WonderDuration }); let isCaptureTheRelic = victoryCondition.Name == "capture_the_relic"; if (isCaptureTheRelic) - { - let relicDuration = Math.round(g_GameAttributes.settings.RelicDuration); title = sprintf( translatePluralWithContext( "victory condition", "Capture the Relic (%(min)s minute)", "Capture the Relic (%(min)s minutes)", - relicDuration + g_GameAttributes.settings.RelicDuration ), - { "min": relicDuration }); - } + { "min": g_GameAttributes.settings.RelicDuration }); titles.push({ "label": title, @@ -230,7 +224,7 @@ if (isCaptureTheRelic) titles.push({ "label": translate("Relic Count"), - "value": Math.round(g_GameAttributes.settings.RelicCount) + "value": g_GameAttributes.settings.RelicCount }); if (victoryCondition.Name == "regicide") @@ -275,17 +269,16 @@ "value": translate("If one player wins, his or her allies win too. If one group of allies remains, they win.") }); - let ceasefire = Math.round(g_GameAttributes.settings.Ceasefire); titles.push({ "label": translate("Ceasefire"), "value": - ceasefire == 0 ? + g_GameAttributes.settings.Ceasefire == 0 ? translate("disabled") : sprintf(translatePlural( "For the first minute, other players will stay neutral.", "For the first %(min)s minutes, other players will stay neutral.", - ceasefire), - { "min": ceasefire }) + g_GameAttributes.settings.Ceasefire), + { "min": g_GameAttributes.settings.Ceasefire }) }); if (g_GameAttributes.map == "random") Index: binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/GameSettingControlSlider.js =================================================================== --- binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/GameSettingControlSlider.js +++ binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/GameSettingControlSlider.js @@ -19,6 +19,8 @@ if (this.MaxValue !== undefined) this.slider.max_value = this.MaxValue; + + this.slider.step_size = this.StepSize; } setControl(gameSettingControlManager) @@ -75,5 +77,4 @@ } } -GameSettingControlSlider.prototype.UnknownValue = - translateWithContext("settings value", "Unknown"); +GameSettingControlSlider.prototype.StepSize = 1; Index: binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Sliders/Ceasefire.js =================================================================== --- binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Sliders/Ceasefire.js +++ binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Sliders/Ceasefire.js @@ -34,11 +34,10 @@ onGameAttributesBatchChange() { - let value = Math.round(g_GameAttributes.settings.Ceasefire); + let value = g_GameAttributes.settings.Ceasefire; this.sprintfValue.minutes = value; - this.setSelectedValue( - g_GameAttributes.settings.Ceasefire, + value, value == 0 ? this.NoCeasefireCaption : sprintf(this.CeasefireCaption(value), this.sprintfValue)); @@ -50,11 +49,6 @@ this.gameSettingsControl.updateGameAttributes(); this.gameSettingsControl.setNetworkGameAttributes(); } - - onGameAttributesFinalize() - { - g_GameAttributes.settings.Ceasefire = Math.round(g_GameAttributes.settings.Ceasefire); - } }; GameSettingControls.Ceasefire.prototype.TitleCaption = Index: binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Sliders/RelicCount.js =================================================================== --- binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Sliders/RelicCount.js +++ binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Sliders/RelicCount.js @@ -60,10 +60,10 @@ if (this.available) { - let value = Math.round(g_GameAttributes.settings.RelicCount); + let value = g_GameAttributes.settings.RelicCount; this.sprintfValue.number = value; this.setSelectedValue( - g_GameAttributes.settings.RelicCount, + value, value == 0 ? this.InstantVictory : sprintf(this.CaptionRelicCount(value), this.sprintfValue)); } } @@ -74,12 +74,6 @@ this.gameSettingsControl.updateGameAttributes(); this.gameSettingsControl.setNetworkGameAttributes(); } - - onGameAttributesFinalize() - { - if (this.available) - g_GameAttributes.settings.RelicCount = Math.round(g_GameAttributes.settings.RelicCount); - } }; GameSettingControls.RelicCount.prototype.TitleCaption = Index: binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Sliders/RelicDuration.js =================================================================== --- binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Sliders/RelicDuration.js +++ binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Sliders/RelicDuration.js @@ -60,10 +60,10 @@ if (this.available) { - let value = Math.round(g_GameAttributes.settings.RelicDuration); + let value = g_GameAttributes.settings.RelicDuration; this.sprintfValue.min = value; this.setSelectedValue( - g_GameAttributes.settings.RelicDuration, + value, value == 0 ? this.InstantVictory : sprintf(this.CaptionVictoryTime(value), this.sprintfValue)); } } @@ -74,12 +74,6 @@ this.gameSettingsControl.updateGameAttributes(); this.gameSettingsControl.setNetworkGameAttributes(); } - - onGameAttributesFinalize() - { - if (this.available) - g_GameAttributes.settings.RelicDuration = Math.round(g_GameAttributes.settings.RelicDuration); - } }; GameSettingControls.RelicDuration.prototype.TitleCaption = Index: binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Sliders/SeaLevelRiseTime.js =================================================================== --- binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Sliders/SeaLevelRiseTime.js +++ binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Sliders/SeaLevelRiseTime.js @@ -47,11 +47,10 @@ if (!this.values) return; - let value = Math.round(g_GameAttributes.settings.SeaLevelRiseTime); + let value = g_GameAttributes.settings.SeaLevelRiseTime; this.sprintfValue.minutes = value; - this.setSelectedValue( - g_GameAttributes.settings.SeaLevelRiseTime, + value, sprintf(this.SeaLevelRiseTimeCaption(value), this.sprintfValue)); } @@ -61,12 +60,6 @@ this.gameSettingsControl.updateGameAttributes(); this.gameSettingsControl.setNetworkGameAttributes(); } - - onGameAttributesFinalize() - { - if (this.values) - g_GameAttributes.settings.SeaLevelRiseTime = Math.round(g_GameAttributes.settings.SeaLevelRiseTime); - } }; GameSettingControls.SeaLevelRiseTime.prototype.TitleCaption = Index: binaries/data/mods/public/gui/options/options.js =================================================================== --- binaries/data/mods/public/gui/options/options.js +++ binaries/data/mods/public/gui/options/options.js @@ -151,19 +151,24 @@ "configToValue": value => +value, "valueToGui": (value, control) => { control.value = +value; + control.children[0].caption = value; }, "guiToValue": control => control.value, "guiSetter": "onValueChange", "initGUI": (option, control) => { - control.max_value = option.max; control.min_value = option.min; + control.max_value = option.max; + control.step_size = option.step_size; }, "tooltip": (value, option) => sprintf(translateWithContext("slider number", "Value: %(val)s (min: %(min)s, max: %(max)s)"), { - "val": value.toFixed(2), - "min": option.min.toFixed(2), - "max": option.max.toFixed(2) - }) + "val": value, + "min": option.min, + "max": option.max + }), + "onGUISet": (value, control, option) => { + control.children[0].caption = value; + } } }; @@ -241,6 +246,9 @@ if (optionType.sanitizeValue) optionType.sanitizeValue(value, control, option); + if (optionType.onGUISet) + optionType.onGUISet(value, control, option); + control.tooltip = option.tooltip + (optionType.tooltip ? "\n" + optionType.tooltip(value, option) : ""); Engine.ConfigDB_CreateValue("user", option.config, String(value)); Index: binaries/data/mods/public/gui/options/options.json =================================================================== --- binaries/data/mods/public/gui/options/options.json +++ binaries/data/mods/public/gui/options/options.json @@ -69,12 +69,13 @@ ] }, { - "type": "number", + "type": "slider", "label": "Observer limit", "tooltip": "Prevent further observers from joining if the limit is reached.", "config": "network.observerlimit", "min": 0, - "max": 32 + "max": 32, + "step_size": 1 }, { "type": "boolean", @@ -123,7 +124,8 @@ "tooltip": "Number of shader effects. REQUIRES GAME RESTART", "config": "materialmgr.quality", "min": 0, - "max": 10 + "max": 10, + "step_size": 1 }, { "type": "boolean", @@ -242,7 +244,8 @@ "tooltip": "To save CPU workload, throttle render frequency in all menus. Set to maximum to disable throttling.", "config": "adaptivefps.menu", "min": 20, - "max": 100 + "max": 100, + "step_size": 1 }, { "type": "slider", @@ -250,7 +253,8 @@ "tooltip": "To save CPU workload, throttle render frequency in running games. Set to maximum to disable throttling.", "config": "adaptivefps.session", "min": 20, - "max": 100 + "max": 100, + "step_size": 1 } ] }, @@ -265,7 +269,8 @@ "config": "sound.mastergain", "function": "SetMasterGain", "min": 0, - "max": 2 + "max": 200, + "step_size": 10 }, { "type": "slider", @@ -274,7 +279,8 @@ "config": "sound.musicgain", "function": "SetMusicGain", "min": 0, - "max": 2 + "max": 2, + "step_size": 0.01 }, { "type": "slider", @@ -283,7 +289,8 @@ "config": "sound.ambientgain", "function": "SetAmbientGain", "min": 0, - "max": 2 + "max": 2, + "step_size": 0.01 }, { "type": "slider", @@ -292,7 +299,8 @@ "config": "sound.actiongain", "function": "SetActionGain", "min": 0, - "max": 2 + "max": 2, + "step_size": 0.01 }, { "type": "slider", @@ -301,7 +309,8 @@ "config": "sound.uigain", "function": "SetUIGain", "min": 0, - "max": 2 + "max": 2, + "step_size": 0.01 }, { "type": "boolean", @@ -427,15 +436,17 @@ "tooltip": "The wounded unit hotkey considers the selected units as wounded if their health percentage falls below this number.", "config": "gui.session.woundedunithotkeythreshold", "min": 0, - "max": 100 + "max": 100, + "step_size": 1 }, { - "type": "number", + "type": "slider", "label": "Batch training size", "tooltip": "Number of units trained per batch by default.", "config": "gui.session.batchtrainingsize", "min": 1, - "max": 20 + "max": 20, + "step_size": 1 }, { "type": "slider", @@ -443,7 +454,8 @@ "tooltip": "Number of times you have to scroll to increase/decrease the batchsize by 1.", "config": "gui.session.scrollbatchratio", "min": 0.1, - "max": 30 + "max": 30, + "step_size": 0.1 }, { "type": "boolean", Index: binaries/data/mods/public/gui/options/options.xml =================================================================== --- binaries/data/mods/public/gui/options/options.xml +++ binaries/data/mods/public/gui/options/options.xml @@ -26,14 +26,16 @@ - + - + - + + + Index: source/gui/GUISettingTypes.h =================================================================== --- source/gui/GUISettingTypes.h +++ source/gui/GUISettingTypes.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -31,6 +31,7 @@ TYPE(i32) TYPE(u32) TYPE(float) +TYPE(double) TYPE(EAlign) TYPE(EVAlign) TYPE(CPos) Index: source/gui/GUIStringConversions.cpp =================================================================== --- source/gui/GUIStringConversions.cpp +++ source/gui/GUIStringConversions.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -59,6 +59,13 @@ } template <> +bool CGUI::ParseString(const CGUI* UNUSED(pGUI), const CStrW& Value, double& Output) +{ + Output = Value.ToDouble(); + return true; +} + +template <> bool CGUI::ParseString(const CGUI* UNUSED(pGUI), const CStrW& Value, CRect& Output) { const unsigned int NUM_COORDS = 4; Index: source/gui/ObjectTypes/CSlider.h =================================================================== --- source/gui/ObjectTypes/CSlider.h +++ source/gui/ObjectTypes/CSlider.h @@ -48,28 +48,21 @@ /** * Change settings and send the script event */ - void UpdateValue(); + void UpdateValue(const double value); CRect GetButtonRect() const; - /** - * @return ratio between the value of the slider and its actual size in the GUI - */ - float GetSliderRatio() const; - - void IncrementallyChangeValue(const float value); + void IncrementallyChangeValue(const double value); // Settings - float m_ButtonSide; + double m_ButtonSide; i32 m_CellID; - float m_MinValue; - float m_MaxValue; + double m_MinValue; + double m_MaxValue; CGUISpriteInstance m_Sprite; CGUISpriteInstance m_SpriteBar; - float m_Value; - -private: - CPos m_Mouse; + double m_Value; + double m_StepSize; }; #endif // INCLUDED_CSLIDER Index: source/gui/ObjectTypes/CSlider.cpp =================================================================== --- source/gui/ObjectTypes/CSlider.cpp +++ source/gui/ObjectTypes/CSlider.cpp @@ -31,6 +31,7 @@ m_CellID(), m_MaxValue(), m_MinValue(), + m_StepSize(), m_Sprite(), m_SpriteBar(), m_Value() @@ -39,6 +40,7 @@ RegisterSetting("cell_id", m_CellID); RegisterSetting("max_value", m_MaxValue); RegisterSetting("min_value", m_MinValue); + RegisterSetting("step_size", m_StepSize); RegisterSetting("sprite", m_Sprite); RegisterSetting("sprite_bar", m_SpriteBar); RegisterSetting("value", m_Value); @@ -56,15 +58,9 @@ IGUIButtonBehavior::ResetStates(); } -float CSlider::GetSliderRatio() const +void CSlider::IncrementallyChangeValue(const double difference) { - return (m_MaxValue - m_MinValue) / (m_CachedActualSize.GetWidth() - m_ButtonSide); -} - -void CSlider::IncrementallyChangeValue(const float difference) -{ - m_Value = Clamp(m_Value + difference, m_MinValue, m_MaxValue); - UpdateValue(); + UpdateValue(Clamp(m_Value + difference, m_MinValue, m_MaxValue)); } void CSlider::HandleMessage(SGUIMessage& Message) @@ -83,14 +79,14 @@ { if (m_Pressed) break; - IncrementallyChangeValue(-0.01f); + IncrementallyChangeValue(-m_StepSize); break; } case GUIM_MOUSE_WHEEL_UP: { if (m_Pressed) break; - IncrementallyChangeValue(0.01f); + IncrementallyChangeValue(+m_StepSize); break; } case GUIM_MOUSE_PRESS_LEFT: @@ -99,8 +95,10 @@ { if (m_Pressed) { - m_Mouse = m_pGUI.GetMousePos(); - IncrementallyChangeValue((m_Mouse.x - GetButtonRect().CenterPoint().x) * GetSliderRatio()); + const double ratio = (m_MaxValue - m_MinValue) / (m_CachedActualSize.GetWidth() - m_ButtonSide); + const double value = ((m_pGUI.GetMousePos().x - m_CachedActualSize.left - m_ButtonSide / 2.f) * ratio) + m_MinValue; + const double valueStep = round(value / m_StepSize) * m_StepSize; + UpdateValue(Clamp(valueStep, m_MinValue, m_MaxValue)); } break; } @@ -119,16 +117,21 @@ m_pGUI.DrawSprite(m_Sprite, m_CellID, bz, GetButtonRect()); } -void CSlider::UpdateValue() +void CSlider::UpdateValue(const double value) { - SetSetting("value", m_Value, true); + if (value == m_Value) + return; + + const float numPlaces = -round(log10(m_StepSize)); + const float num = std::max(1.f, pow(10.f, numPlaces)); + SetSetting("value", round(value * num) / num, true); ScriptEvent(EventNameValueChange); } CRect CSlider::GetButtonRect() const { - float ratio = m_MaxValue > m_MinValue ? (m_Value - m_MinValue) / (m_MaxValue - m_MinValue) : 0.0f; - float x = m_CachedActualSize.left + ratio * (m_CachedActualSize.GetWidth() - m_ButtonSide); - float y = m_CachedActualSize.top + (m_CachedActualSize.GetHeight() - m_ButtonSide) / 2.0; + const double ratio = m_MaxValue > m_MinValue ? (m_Value - m_MinValue) / (m_MaxValue - m_MinValue) : 0.0f; + const double x = m_CachedActualSize.left + ratio * (m_CachedActualSize.GetWidth() - m_ButtonSide); + const double y = m_CachedActualSize.top + (m_CachedActualSize.GetHeight() - m_ButtonSide) / 2.0; return CRect(x, y, x + m_ButtonSide, y + m_ButtonSide); }