Before the change campaign bonus selection screen had inconsistent
overflow behaviour for difficulty selection:
```
0 1 2 3 4 | available buttons: '-' to decrease
^ | '+' to increase
```
Before the change:
1. If we click '+' 5 times we will end up on difficulty=4 (ok, saturated).
2. If we click '-' 5 times we will end up on difficulty=1 (unexpected, wrap around).
After the change:
1. If we click '+' 5 times we will end up on difficulty=4 (saturated).
2. If we click '-' 5 times we will end up on difficulty=0 (saturated).
The inconsistency happens because `difficulty` variable has `ui8` type
and server uses `difficulty = vstd::abetween(difficulty, 0, 4)` to
implement the saturation.
For large positive values saturation works as expected:
vstd::abetween(difficulty=5, 0, 4) -> 4
For small values it does not:
vstd::abetween(difficulty=-1, 0, 4) -> 4
The change makes client to avoid using negative values.
Noticed use of uninitialized value when built vcmi with -fsanitize=undefined:
```
Established connection with
VCMI 0.99 b310f2e61e (server).
UUID: bab9a90d-7416-4566-8817-e367ffcac7c1
../vcmi-9999/client/lobby/SelectionTab.cpp:138:16:
runtime error: load of value 32717, which is not a valid value for type 'ESortBy'
/usr/lib/gcc/x86_64-pc-linux-gnu/12.0.0/include/g++-v12/tuple:190:4:
runtime error: load of value 32717, which is not a valid value for type 'ESortBy'
/usr/lib/gcc/x86_64-pc-linux-gnu/12.0.0/include/g++-v12/tuple:190:4:
runtime error: load of value 32717, which is not a valid value for type 'ESortBy'
```
Here (before the change) `SelectionTab()` used `generalSortingBy` before
first use:
```
SelectionTab::SelectionTab(ESelectionScreen Type) {
...
if(tabType != ESelectionScreen::campaignList)
{
...
ESortBy criteria = (ESortBy)i;
if(criteria == _name)
criteria = generalSortingBy;
buttonsSortBy.push_back(... std::bind(&SelectionTab::sortBy, this, criteria)));
...
}
...
switch(tabType)
{
case ESelectionScreen::newGame:
generalSortingBy = ESortBy::_name;
....
```
The change moves `generalSortingBy` initialization before first use.
Signed-off-by: Sergei Trofimovich <slyfox@gentoo.org>
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
Disabled the following (for MSVC only) that couldn't (or shouldn't) be fixed.
4003: not enough actual parameters for macro 'identifier'
4250: 'class1' : inherits 'class2::member' via dominance
4251: 'type' : class 'type1' needs to have dll-interface to be used by clients of class 'type2'
4275: non dll-interface class 'type1' used as base for dll-interface class 'type2'
Replaced const TBonusListPtr with TConstBonusListPtr where necessary
Replaced const std::shared_ptr<T> with std::shared_ptr<const T> where necessary.
Removed superfluous use of const.
Replaced const std::shared_ptr<T> with const std::shared_ptr<T> & in function parameters and ranged for-loops.
Scenario: In LoadGame Lobby screen, the difficulty toggle group is showing multiple choices, but actually only one valid is working.
The reason is, in the Lobby screen Initialization code will set the difficulty = 0, and each time the player changes a map in the selection, the toggle group control is not resetting the difficulty buttons to disabled state.
How fix:
Add a new method to ToggleGroup class: setSelectedOnly, which will disable all other buttons and then set the selected button.
Note:
During the game loading time, the client is loading the map, and send a NetPack to server: LobbySetMap, and send a NetPack to Interface: LobbyUpdateState.
In the LobbyUpdateState it sets the map difficulty.