Compare commits

...

346 Commits

Author SHA1 Message Date
Jonathan G Rennison 8710e9b8c8 Fix #12608: SDL keycode to vkey mapping 4 weeks ago
Tyler Trahan 019f93a65f
Doc: Clarify how to update version numbers in release guide (#12634) 4 weeks ago
translators ab7e2a1883 Update: Translations from eints
english (au): 2 changes by krysclarke
english (us): 2 changes by 2TallTyler
greek: 4 changes by gh658804
russian: 2 changes by Ln-Wolf
finnish: 4 changes by hpiirai
danish: 2 changes by bscargo
lithuanian: 170 changes by khamper
french: 1 change by ben20471
portuguese: 2 changes by azulcosta
portuguese (brazilian): 2 changes by pasantoro
polish: 2 changes by pAter-exe
4 weeks ago
Rubidium 1a3cbaec56 Cleanup: remove warning about server administrators being able to read passwords 4 weeks ago
Rubidium 849d1fa1b3 Cleanup: remove UI for asking user for company password 4 weeks ago
Rubidium 457d51fc49 Cleanup: remove company password hashing and anything related to it 4 weeks ago
Rubidium a9318cf653 Cleanup: remove UI for changing the password 4 weeks ago
Rubidium 9dc1fdc385 Cleanup: remove client side password checks when moving to a different company 4 weeks ago
Rubidium 16639939e9 Cleanup: remove command line option for company password 4 weeks ago
Rubidium ca4bef1504 Cleanup: remove company password related network packets 4 weeks ago
Rubidium 71fc907584 Change: remove company passwords over client allow lists 4 weeks ago
Rubidium a002803d1c Remove: autoclean_unprotected settings; all companies will be protected 4 weeks ago
Rubidium 4f3db8eeaf Feature: authorize specific clients to join network company without password 4 weeks ago
Rubidium 66354ab9eb Codechange: introduce allow list infrastructure for companies 4 weeks ago
Rubidium 1250ce8fdc Codechange: support storing std::string vectors/deques in the savegame 4 weeks ago
translators 4e9a280ef8 Update: Translations from eints
swedish: 2 changes by sereneavatar
norwegian (bokmal): 2 changes by eriksorngard
welsh: 19 changes by Ansbaradigeidfran
english (us): 2 changes by 2TallTyler
czech: 1 change by JsSusenka
lithuanian: 97 changes by khamper
french: 2 changes by Lishouuu
portuguese (brazilian): 1 change by pasantoro
polish: 2 changes by pAter-exe
4 weeks ago
Rubidium b9c894b717 Fix: for GUI network servers, name the first company the same as any other company 4 weeks ago
Rubidium 455e202e03 Fix: server's client is shown incorrectly in some cases 4 weeks ago
Peter Nelson 4740eeaa43
Add: 'Get Content' buttons next to base set dropdowns in Game Options. (#12627)
This allows base set content to be downloaded more easily, filtered by type and next to where it is set up.
1 month ago
Koen Bussemaker d2f98440bb Codechange: Make assert in follow_track easier to understand and debug 1 month ago
Peter Nelson 84ebae0bf5
Codechange: Tweak layout of network client list. (#12624)
Use PIP spacing instead of spacers and more used of standard WidgetDimensions.
1 month ago
Peter Nelson cf94bd321d
Codechange: Remove redundant SetMinimalSize of preview buttons. (#12622)
These widgets are also sized in UpdateWidgetSize where the current bevel width is accounted for as well.
1 month ago
Peter Nelson 72b2840a97
Codechange: Use PIP spacing instead of spacers in tree picker window. (#12623) 1 month ago
translators 99f497cb08 Update: Translations from eints
english (au): 2 changes by krysclarke
korean: 2 changes by telk5093
russian: 2 changes by Ln-Wolf
finnish: 2 changes by hpiirai
danish: 2 changes by bscargo
lithuanian: 4 changes by dziugas1959
portuguese: 2 changes by azulcosta
portuguese (brazilian): 4 changes by pasantoro
1 month ago
Rubidium 9fe9e4d398 Codechange: use vector instead of manual memory management for decompressing text files 1 month ago
Peter Nelson d074ab909c
Codechange: Replace Array/FixedSizeArray with std::deque. (#12409)
Array/FixedSizeArray is actually a resizeable container that allocates space in chunks and allows resizing without invalidating pointers.

This is also a behaviour of std::deque, so use that instead.
1 month ago
Tyler Trahan cb26b0e92d
Doc: Re-order release instructions to not tell the world prematurely (#12620) 1 month ago
Peter Nelson f629d3c921
Codechange: Use vectors instead of CallocT/free for cache checks. (#12619)
Vectors are reused for each vehicle chain to save on reallocations.
1 month ago
Peter Nelson 115ac2629b
Cleanup: Remove TileMatrix that hasn't been used for 4+ years. (#12621)
This 'nice' structure was left around from #8258 just in case it might be used again.

Spoiler alert: it hasn't.

This removes manual memory management. And otherwise unused and untested code.
1 month ago
translators 60cf37e0d1 Update: Translations from eints 1 month ago
Peter Nelson 9a7c30a109 Codechange: Let ClickSliderWidget handle rounding to nearest mark. 1 month ago
Peter Nelson 9d2efd4c96 Codechange: Use callback function to set labels of slider widget marks.
Slider widgets can only use a predefined list of values and strings to draw labels. This makes it difficult to vary the display by context.

Instead of providing a predefined list as a std::map, use a callback function instead. This function can decide what text to display, and can call SetDParam to dynamically set up strings.
1 month ago
Rubidium 8b6661d486 Codefix 90029be: build failure for SDL 1.2 1 month ago
Rubidium 0b50834f81 Fix 2955ff3: CMake atomic check fails due to chosen compiler 1 month ago
translators e0048d798f Update: Translations from eints
hungarian: 5 changes by egri-nagy
1 month ago
Peter Nelson 90029beb49
Codechange: Ensure SDLK mappings stay in the expected order. (#12608)
Add a constexpr constructor that ensures at compile-time that the source SDLK range matches the target range.
1 month ago
James Addison c17fa6032b
Doc: Add Apache 2.0 licensing info for CheckAtomic.cmake (#12603) 1 month ago
Koen Bussemaker ef99aa81a3 Codechange: Store validity of water regions in separate vector 1 month ago
Peter Nelson cf96d49ced
Codechange: Use vector for airport tile layouts. (#12607)
Simplify AirportSpec data by storing layout information together in a vector, instead of separate arrays.

This removes manual memory management and separate count members.

The default layouts will be copied instead of always referring to the originals.
1 month ago
translators 65c9df49d9 Update: Translations from eints
chinese (simplified): 8 changes by XiaoJi-Game
catalan: 3 changes by J0anJosep
1 month ago
Fen 2559bdfa6f
Fix 2d27e8e: Update numpad keycodes for SDL2, making it usable (#12596)
* Fix 2d27e8e: Update numpad keycodes for SDL2, making it usable

* Cleanup: List sdl2 numpad mappings individually
1 month ago
Jonathan G Rennison 9647fe1d05 Change: [Linkgraph] Improve distance scaling algorithm in demand scaler
Improve scaling at values other than 0% and 100%
Fix erratic scaling and increase effect size at large setting values
1 month ago
Peter Nelson f146680121
Codechange: Use vector for industry random sounds. (#12606)
Use a vector to store the list of random sounds played for an industry.

The removes manual memory allocation, flags to control memory management, a separate count member, and a try/catch block.
1 month ago
Peter Nelson 7147fe9e7a
Codechange: Use range-for when loading NewGRF deterministic sprite groups. (#12605) 1 month ago
Peter Nelson 532ce1a907
Codechange: Use Recv/Send_bytes for md5sum. (#12602)
Use existing functions to handle serialisation of arrays instead of indexed for-loop.
1 month ago
Peter Nelson 1424a184d8
Codechange: Use vector/span when loading wagon overrides. (#12604)
Replaces manual memory allocation and passing pointer with size.
1 month ago
translators 96d82b4363 Update: Translations from eints
chinese (simplified): 1 change by WenSimEHRP
greek: 52 changes by KyriakosMich
german: 3 changes by Wuzzy2
basque: 36 changes by Porrumentzio
danish: 3 changes by bscargo
1 month ago
translators 4df44fea38 Update: Translations from eints
swedish: 3 changes by joeax910
galician: 63 changes by pvillaverde
1 month ago
Peter Nelson cc6e4768a9
Fix: Out-of-order window set up due to deferred window resize. (#12592)
Deferred window resize was being applied to the initial window resize event, resulting in some window state (e.g. scroll bar capacity) not being initialised when expected.
1 month ago
translators b852a3f408 Update: Translations from eints
english (us): 3 changes by 2TallTyler
korean: 3 changes by telk5093
hungarian: 2 changes by meskobalazs
1 month ago
rubidium42 fd4cf699e5
Codefix 37a03b5: the return value of maxdim should always be assigned (#12590) 1 month ago
Rubidium 98d37338df Codechange: use ranged for loop instead of one with lengthof 1 month ago
Rubidium 24b6ec80a9 Codechange: use ranged for loop and vector to determine the disaster to deploy 1 month ago
Rubidium 87dbd4a833 Codechange: use ranged for loop to determine widest title 1 month ago
Rubidium d183d8e587 Codechange: remove INVALID_STRING_ID now drop down uses spans 1 month ago
Rubidium 37a03b513f Codechange: refactor string list dimension finding into a separate function 1 month ago
Rubidium 546a996d95 Codechange: pass options to ShowDropDownMenu using a span 1 month ago
Rubidium ad50c4f298 Codechange: inline sorter name definitions and use proper static accessor instead of (implying) this-> 1 month ago
Peter Nelson ef55cc7979
Codechange: Remove support for links in tar files. (#12571)
This was a feature to support the original 32bpp sprite system and is no longer relevant.
1 month ago
Loïc Guilloux 99b74c1064
Fix #12584: Improved error handling during tar scan (#12586) 1 month ago
Rubidium 826deaee57 Codechange: refactor CalcHeightdiff to remove some magic numbers 1 month ago
Rubidium 97a34bf06e Codechange: use C++ containers for the colour schemes 1 month ago
translators ee9895a970 Update: Translations from eints
norwegian (bokmal): 3 changes by eriksorngard
french: 3 changes by ottdfevr
portuguese: 3 changes by azulcosta
1 month ago
Peter Nelson 440a633fcc Codechange: Remove shrink_to_fit for more lists.
A comment about "will actually do nothing" is out of date as that is not the case with std::vector.

These lists are always short lived (either within a command handler or in a window) so don't shrink_to_fit.
1 month ago
Peter Nelson 33aedc43a5 Codechange: Shrink GUIList vectors less often, reserve before use.
After sorting and filter lists for GUI, we often shirnk them to reduce size. However this has very little benefit:

1) The memory has already been allocated, so it doesn't prevent that memory being required.
2) It causes a new allocation and copy when the vector is shrunk, actually using more memory.
3) The list is in window state, so the lifetime is only while the window is open.
4) When a filter is clearer, the original size will be needed again, which will cause another allocation.

In fact it is beneficial to reserve to the known maximum in most cases, so do that instead.
1 month ago
translators 8308998388 Update: Translations from eints
english (au): 3 changes by krysclarke
russian: 3 changes by Ln-Wolf
finnish: 6 changes by hpiirai
dutch: 6 changes by Afoklala
portuguese (brazilian): 4 changes by pasantoro
polish: 3 changes by pAter-exe
1 month ago
Peter Nelson bf8de188ec
Codechange: Use member initialization of GRFFilePropsBase. (#12581)
Don't blame compilers for our sloppy initialisation.

Removes memset, and lengthof.
1 month ago
Peter Nelson 72c55128d2
Codechange: Remove write-only spec_id from RoadStopSpec. (#12582)
Comment is incorrect about its value too.
1 month ago
Peter Nelson a6d401debf
Fix: Properly test for presence of waypoint in NewGRF resolver. (#12579)
Test whether the BaseStation itself a Station or Waypoint, instead of by the station class ID assigned to it.
1 month ago
translators a60a81f34e Update: Translations from eints
swedish: 7 changes by joeax910
vietnamese: 15 changes by anmatngu
greek: 31 changes by gh658804, 2 changes by KyriakosMich
hungarian: 2 changes by egri-nagy
portuguese (brazilian): 2 changes by pasantoro
1 month ago
Rubidium 0fdabca605 Codechange: use std::span instead of custom span in TGP 1 month ago
Peter Nelson f44d8fa2e4
Codechange: Remove CDECL from filter functions. (#12578)
These functions are not passed to qsort()...
1 month ago
Peter Nelson 0075a95278
Codefix: Make three _filter_funcs definitions distinct. (#12573)
Rename the GUIList function lists defined as the same symbol.
1 month ago
Peter Nelson 5bc9854be2
Codechange: Make sort list function lists safer. (#12574)
GUIList has a pointer only to the start of each sort/filter func list, which has the potential for UB as it is unable to validate that the selected sort or filter type is in range.

Use a std::span instead and check if the selected type is in range before using it.
1 month ago
Jonathan G Rennison 9b747a173d Fix #12509: Maintain timer sort invariants when changing period 1 month ago
Jonathan G Rennison 11ec156b64 Codechange: Add a priority field to TimerGameTick::TPeriod
Use this as the primary sort key for TimerGameTick::TPeriod,
to avoid container sort order changes on timer period saveload.
See: #12509
1 month ago
André Cheng 57f5d27427 Doc: Fix documentation of GetDefaultValueCallback 1 month ago
André Cheng ceb0053dd9 Codechange: Correct return type of GetDefaultValueCallback 1 month ago
André Cheng 339b0ea0ff Change: Show correct default value and unit for vehicle service interval setting 1 month ago
André Cheng fd80a1ec66 Fix #11345: Use correct default button value for vehicle service interval setting 1 month ago
André Cheng a4071b78d7 Codechange: Add callback to IntSettingDesc to support more default values 1 month ago
translators e8d25d68b9 Update: Translations from eints
russian: 2 changes by George-VB
1 month ago
Peter Nelson e8249e9075
Codechange: Pass buffers for TarFile's ExtractString as span. (#12567)
ExtractString does not need to find a string terminator as StrMakeValid already does this, so simply pass the full bounds of the buffer.

Removes lengthof, array indices, and needs only the buffer as a parameter.
1 month ago
Peter Nelson 5159aa81d4
Codechange: Use iterators when enabling industries. (#12569)
Removes lengthof and array indices.
1 month ago
Peter Nelson 26bb87ebf1
Codechange: Replace SaveLoad var length arrays with switch block and sizeof. (#12570)
SlCalcConvMemLen(), SlCalcConfFileLen() and CalcOldVarLen() follow a pattern of looking up part of a value in an array.

These function returns the size of bytes of a variable type, but is not very clear. Replace with a switch block instead.

Removes lengthof, array indices, and magic numbers.
1 month ago
Peter Nelson 1dc94d0670
Codechange: Construct string_view with first+last. (#12568)
Avoids needing to calculate size when we already have last.
1 month ago
Peter Nelson 6a3f50aa72
Codechange: Replace separate EffectVehicle arrays. (#12565)
Combine 3 separate arrays into a single struct. This keeps related data together, and avoids needing to check that each array is same length.

Use of constexpr construct ensures data in the array is not default-initialised.

Removes lengthof.
1 month ago
Peter Nelson e20f48799e
Codechange: Make StringToContentType() clearer. (#12566)
Decouples string to ContentType mapping from position within enum.

Slightly less efficient, but removes lengthof, array indices, and casting.
1 month ago
Peter Nelson 5e689ce25e
Codechange: Store cursor sprites in vector. (#12564)
Combine two separate fixed length arrays to allow simpler iteration.

No need to check that arrays are all the same length.
No need to separately store the number of sprites to draw.
Removes the upper limit of the number of sprites that can be drawn.

Removes lengthof and array indices.
1 month ago
translators 9121770582 Update: Translations from eints
korean: 3 changes by telk5093
catalan: 3 changes by J0anJosep
french: 7 changes by ottdfevr
polish: 7 changes by pAter-exe
1 month ago
Peter Nelson ac6a945e26
Revert 2408a68910: Remove work around for an MSVC bug from 17 years ago. (#12557) 1 month ago
Koen Bussemaker bef11941c6 Change: Allow rail and road depot overbuilding in current orientation in order to connect to rail or road 1 month ago
translators 95de90dd4e Update: Translations from eints
norwegian (bokmal): 7 changes by eriksorngard
danish: 7 changes by bscargo
portuguese (brazilian): 4 changes by pasantoro
1 month ago
Peter Nelson 3814adaba8
Codechange: Simplify GetScrolledItemFromWidget() (#12556)
Return `auto`, which allows working with const containers, and use std::next instead of std::advance.
1 month ago
translators c82a2575d7 Update: Translations from eints
english (au): 3 changes by krysclarke
english (us): 3 changes by 2TallTyler
greek: 3 changes by gh658804
russian: 3 changes by Ln-Wolf
finnish: 5 changes by hpiirai
turkish: 7 changes by BeratSJ
portuguese: 3 changes by azulcosta
portuguese (brazilian): 3 changes by pasantoro
1 month ago
Rubidium ded4d63db2 Codechange: simplify access to the current screenshot format 1 month ago
Peter Nelson d465257dd0
Fix 952d111: Houses and industry tiles could accept incorrect cargo. (#12547)
Default cargo label was not cleared (set to CT_INVALID) when using older 3-slot acceptance properties for house and industry tiles.

Missed in #12053 and #12062.
1 month ago
Peter Nelson 3b01d31280
Fix 8746be8: Reinstate current order test when removing road stop. (#12552)
#12144 replaced pool iteration with FindVehiclesWithOrder, however the test for current_order being OT_GOTO_STATION was erroneously removed.
1 month ago
translators c073165e34 Update: Translations from eints
greek: 5 changes by gh658804
finnish: 2 changes by hpiirai
catalan: 4 changes by J0anJosep
1 month ago
Rubidium aa895535e6 Fix 5008706: improved scenario editor tooltips in map generation stages are out of place 1 month ago
Peter Nelson 959ced71bb
Codechange: Add constants for original input/output cargo counts. (#12548)
This replaces some magic 3s and 2s.
1 month ago
Peter Nelson b4e00fa738
Codechange: Replace C-casts in pool functions. (#12541) 2 months ago
Loïc Guilloux c5ef47ee09
Codechange: [Script] Use std::unique_ptr for Company::ai_instance (#12544) 2 months ago
Rubidium 1dfd0c19f4 Fix: allow only 255 league tables, as 255 is the invalid id sentinel 2 months ago
Rubidium c377c4740d Codechange: replace cpp_lengthof with safe alternatives 2 months ago
Peter Nelson a1a01e21cf
Change: Use std::make_unique instead of passing new() (#12539) 2 months ago
Rubidium fc7f184dbd Codechange: move knowledge about 'packed' orders to the saveload code 2 months ago
Rubidium 1691b41b54 Codechange: use C++ containers for parsing the settings int lists 2 months ago
Rubidium 434c49a1f8 Codechange: remove now unused endof macro 2 months ago
Rubidium 8fe5fdf122 Codechange: use std::none_of to express clearer what the code does 2 months ago
Rubidium 8986fb0385 Codechange: replace C-style array-pointer methods with the appropriate C++ methods 2 months ago
Peter Nelson 2114888485
Change: De-template BaseSetTextfileWindow. (#12536)
The BaseSet type is not needed after the window is constructed, only the filename and name are required, which can be passed as parameters from `ShowBaseSetTextfileWindow()` instead.

This avoids compiling three instances of `BaseSetTextfileWindow`.
2 months ago
Rubidium e441033d68 Codechange: use std::array instead of C-style array for produced/accepts cargo 2 months ago
Peter Nelson 40fa45a76a Codechange: Emplace std::pair into vectors.
This creates the pair in the vector, instead of creating it then copying it in.
2 months ago
Peter Nelson ed2db80990 Codechange: Use map.emplace() instead of map.insert(std::pair).
This avoids a copy of the pair into the map.
2 months ago
Peter Nelson 57d7359b1a
Codechange: Remove old group liveries savegame conversion. (#12537)
Conversion to set default group livery is in the wrong place (not in `AfterLoadGame()`), however it is not necessary any more as `AfterLoadGame()` always calls the function `UpdateCompanyLiveries()` which will do the same thing.
2 months ago
Peter Nelson 699c7e4c9d
Fix 3de8853e29: Industries accept/produce no cargo for pre-SLV_78 saves. (#12508)
Industry accepted/produced was trimmed too early for original and pre-SLV_78 saves, as cargo type was not stored per slot so all slots look invalid to the trim function.
2 months ago
Peter Nelson 1c31e4b68c
Change: Disallow using Action A to load sprites above the baseset unless reserved. (#12435)
Using Action A above the baseset is error prone as the sprites are not fixed and can be moved around.

Any NewGRF doing so is likely to break in the future, so force it to break instead.
2 months ago
Peter Nelson e028c15555
Codechange: Use std::accumulate to get infrastructure total rail/road pieces. (#12442) 2 months ago
translators 08140fdca3 Update: Translations from eints
dutch: 4 changes by Afoklala
2 months ago
Peter Nelson 6cbb8d02cf Change: Use aspect ratio for shared order list button. 2 months ago
Peter Nelson 5df5e3f45c Change: Use aspect ratio for file home button. 2 months ago
Peter Nelson 2a833a8968 Change: Use aspect ratio for rail station platforms/tracks buttons. 2 months ago
Peter Nelson 7e049aa2b1 Change: Use aspect ratio for common left/right buttons. 2 months ago
Peter Nelson 16eb17418b Change: Use aspect ratios for some common widgets. 2 months ago
Peter Nelson 61c6ebaacc Change: Automatically set aspect ratio of common window decorations. 2 months ago
Peter Nelson d43ff8dc49 Change: Ability to set aspect ratio of a widget.
This allows setting the shape of a widget without dealing with absolute pixel sizes.
2 months ago
Peter Nelson f267b37a33
Codechange: Use std::initializer_list/array in framerate window. (#12441) 2 months ago
Peter Nelson a28ab8cac2
Codechange: Replace C-style casts to size_t with static_cast. (#12455)
* Codechange: Replace C-style casts to size_t with static_cast.

This touches only simple value-type casts.

* Codechange: Replace static_cast<size_t>(-1) with SIZE_MAX

Co-authored-by: Rubidium <rubidium@openttd.org>
2 months ago
Peter Nelson 6ee31a2a22
Codechange: Use string_view in IniItem/IniGroup/IniFile. (#12535)
This avoids making extra copies of strings.
2 months ago
Peter Nelson 3b80a8255f
Fix #12433: Width of unit number display was too narrow. (#12534)
Digit width was counted, but ignored the thousands separator.
2 months ago
translators 7848e80f71 Update: Translations from eints
english (us): 4 changes by 2TallTyler
korean: 11 changes by telk5093
2 months ago
dP 0d1fc47edb
Cleanup: Remove redundant break statement (#12527) 2 months ago
Peter Nelson 774f811217
Codechange: Use std::optional for town parent scope resolver. (#12530)
When resolving NewGRF, the parent town_scope is lazily initialised as it does not always need to be used.

Replace the manually managed pointer with std::optional to simplify. Using std::optional avoids extra memory allocation.
2 months ago
Peter Nelson 3b75d8bbf8 Fix: Use modern comparisons instead of memcmp in cache check.
This uses C++20 default operator<=> to provide comparisons of some objects.

This works properly with caches that containers.
2 months ago
Peter Nelson 21d11ee361 Codechange: Move cache check function to own file. 2 months ago
Peter Nelson db56499c01 Codechange: Use std::array for company infrastructure arrays. 2 months ago
Peter Nelson fe7bd3a266 Codechange: Use std::array for cached town zone radius. 2 months ago
Peter Nelson d57bf84196
Codechange: Remove some unnecessary c_str() when passing std::strings. (#12532)
Functions have been updated from `char *` to `std::string` since without removing `c_str()`.
2 months ago
Peter Nelson 45886e50b2
Codechange: Unify where rail station tile flags are set. (#12531)
This avoids repeating the logic in three places.
2 months ago
Patric Stout 04a3bf76e8
Codechange: upgrade Emscripten to 3.1.57 (#12526)
This also upgrades liblzma to 5.4.6, and uses the new ports.contrib
system Emscripten 3.1.56 introduced.
2 months ago
Patric Stout 08d05bf4c0
Doc: update release documentation with the latest (#12525) 2 months ago
Peter Nelson 4170c9923a
Fix: Inconsistent space between console history and current line. (#12528) 2 months ago
Tyler Trahan 78b83190cc
Fix: Mark vehicle status bars dirty when a vehicle leaves unbunching depot (#12516) 2 months ago
Patric Stout 715f8c0218
Codefix: cast to "CommandCallback *" in a way cast-function-type-mismatch doesn't mind (#12529) 2 months ago
translators c355e98c58 Update: Translations from eints
english (au): 4 changes by krysclarke
russian: 4 changes by Ln-Wolf
finnish: 7 changes by hpiirai
portuguese: 4 changes by azulcosta
portuguese (brazilian): 5 changes by pasantoro
polish: 4 changes by pAter-exe
2 months ago
Peter Nelson 63ce81570c Remove: Custom opendir implementation for Windows no longer needed.
std::filesystem::directory_iterator is now used instead.
2 months ago
Peter Nelson 42523379d9 Codechange: Use directory_iterator in ScanPath.
Replaces use of custom ttd_opendir.
2 months ago
Peter Nelson d7c547d0db Codechange: Use directory_iterator to list directories in file list windows.
This replaces use of custom ttd_opendir. Files are listed separately using ScanPath as that handles downloaded content.
2 months ago
Peter Nelson 5a523cf212 Codechange: Simplify FioCreateDirectory.
`std::filesystem` provides `create_directories()` as a cross-platform way to create a directory tree.
2 months ago
Peter Nelson 6458980413
Change: Draw group hierarchy tree lines. (#12522) 2 months ago
Jonathan G Rennison 83d99ec11d
Fix #12506: Update station/industry nearby lists in BuildOilRig (#12511) 2 months ago
Patric Stout f7bd080015
Codechange: improve desync documentation (#12521) 2 months ago
Patric Stout 07b162ffc4
Codechange: skip all commands of the past during desync replay (#12520) 2 months ago
Patric Stout a0636d8200
Codechange: use infinite-fast-forward when rerunning command-log (#12519) 2 months ago
Patric Stout a09749f6a6
Codefix: don't send desync=0 log messages to commands.log (#12517)
They are only used during replay, and you want to see those in
the console; not in the log.
2 months ago
Patric Stout 1005c86c62
Codechange: record cache warnings with a "warning" prefix (#12518) 2 months ago
Tyler Trahan a02da5476e
Fix: Don't show train waiting for unbunching as waiting for free path (#12515) 2 months ago
Tyler Trahan 5878d09ef2
Fix: Smooth outliers in unbunching round trip calculations (#12513) 2 months ago
Tyler Trahan 824687d1f0
Codefix: Don't mix signed and unsigned ints in unbunching calculations (#12514) 2 months ago
Jonathan G Rennison 0b9029b69c Fix: Station/industry nearby list checks in CheckCaches 2 months ago
Peter Nelson ef8eb66a2b
Fix c38df2d58: Use VehicleID instead of pointer in map of vehicles to replace. (#12512)
This affects the sort order, VehicleID is deterministic, Vehicle * is not.
2 months ago
Paco Esteban b477a8458c Codechange: Use arc4random_buf on random_func.cpp for OpenBSD 2 months ago
translators 018326321c Update: Translations from eints
english (us): 5 changes by 2TallTyler
latvian: 1 change by lexuslatvia
2 months ago
Koen Bussemaker 257d312a58 Fix #12228, Fix #12231: CheckShipReverse only restricts path when it has to 2 months ago
Rubidium b2218e75d4 Codefix: missing space between close parenthesis and open curly brace 2 months ago
Peter Nelson 48eb9b8bc9
Add: Check that towns can build roads before generating map. (#12503) 2 months ago
translators 8e2ccddd77 Update: Translations from eints
portuguese (brazilian): 2 changes by pasantoro
2 months ago
Peter Nelson 3ad143c43a
Codechange: Use `x = y` instead of `x{y}` for value-type member initialisation. (#12501)
This is easier to read and less likely to look like an array definition.
2 months ago
Koen Bussemaker 672aa014d8 Doc: Updated Visual Studio, cpp standard and Cmake version 2 months ago
ladysadie 727392e0b3
Codechange: Remove per font AA settings. (#12413)
OpenTTD will use the global AA font setting for all fonts from now on.
2 months ago
Peter Nelson a1b03ee69e
Codechange: Replace platform-specific calls with std::filesystem::last_write_time. (#12487) 2 months ago
dP f5a50a874f
Codechange: Update doxygen comment to reflect removed parameter (#12499) 2 months ago
translators cd108fd9e4 Update: Translations from eints
greek: 10 changes by gh658804
2 months ago
Peter Nelson 839f486074
Codechange: Use directory_iterator to find language files. (#12495)
This avoids using custom ttd_opendir, along with C-style string comparisons against file names.
2 months ago
Peter Nelson 4eaeccdaeb
Codechange: Introduce FioRemove() to remove files. (#12491)
New function FioRemove() handles OTTD2FS conversion, and uses std::filesystem::remove instead of unlink, all in one location.
2 months ago
translators 29e932e087 Update: Translations from eints
ukrainian: 13 changes by imlystyi
lithuanian: 19 changes by dziugas1959
french: 7 changes by ottdfevr
portuguese (brazilian): 14 changes by pasantoro
2 months ago
translators 090c3b3abf Update: Translations from eints
danish: 5 changes by bscargo
dutch: 5 changes by Afoklala
portuguese (brazilian): 7 changes by pasantoro
2 months ago
Peter Nelson e83e2df023
Fix: Build industry window did not take width of count into account. (#12476) 2 months ago
Peter Nelson 12125bad82
Fix 3de8853e: Industry cargo types callback no longer functioned due to container change. (#12489)
Use defined INDUSTRY_NUM_INPUTS/INDUSTRY_NUM_OUTPUTS values instead of container size, which is now empty at this point.
2 months ago
Peter Nelson c1520cf682
Fix 25aeb1c: Driver parameter documentation was not updated. (#12486) 2 months ago
Peter Nelson ca73f03334
Codechange: Use std::filesystem::remove/rename in settingsgen. (#12483) 2 months ago
Peter Nelson 44b8210037
Codechange: settingsgen's CopyFile actually appends. (#12485) 2 months ago
Peter Nelson 003906becb
Codechange: std::filesystem::rename does not need remove first. (#12484) 2 months ago
translators bb9b8b90c7 Update: Translations from eints
swedish: 6 changes by sereneavatar
portuguese (brazilian): 11 changes by pasantoro
2 months ago
Peter Nelson 9915c1f032
Fix #12477: Use std::filesystem::rename instead of Windows Shell API call. (#12478) 2 months ago
Rubidium eda10abc8c Codechange: pass command line arguments as std::span to openttd_main 2 months ago
Peter Nelson 3316b27496
Fix: Signature validation did not close its file. (#12479) 2 months ago
Rubidium afd7878de0 Codechange: internally use a span of arguments for GetOptData 2 months ago
Rubidium 5592b4409b Codechange: use ranged for loop and separate function instead of goto 2 months ago
Rubidium e8a56db21d Codechange: use designated initializers for OptionData and pass as span 2 months ago
Rubidium 4f2412a272 Codechange: range based for loops instead of C-style for loops 2 months ago
Rubidium 2587a21400 Codechange: use zero-initialization instead of C-style loop 2 months ago
Rubidium ff27b9e76a Codechange: use std::any_of instead of custom loop 2 months ago
translators 6cade18053 Update: Translations from eints
portuguese (brazilian): 1 change by pasantoro
polish: 1 change by pAter-exe
2 months ago
rubidium42 442daf58da Codechange: replace lengthof with std::size in Windows specific code 2 months ago
Peter Nelson 6bc4a62c27 Codechange: Pass std::string_view from blitters instead of char *. 2 months ago
Peter Nelson 332cbca36e Codechange: Pass std::string_view from drivers instead of char *. 2 months ago
Peter Nelson a42aa1a086
Codechange: Remove cargo_suffix C-array from GetIndustryString. (#12472)
The information is pushed onto a vector, so string ownership can be moved there instead of using a pointer into to the CargoSuffix array.
2 months ago
Peter Nelson 144bcbbaf1
Fix: Use clear() to clear std::string. (#12471) 2 months ago
Rubidium 21b640b5ff Codechange: simplify president name generation 2 months ago
Patric Stout 1b4bb1d38a
Codefix: [CMake] use the UTC0 date for our ISODATE (#12470) 2 months ago
translators f0f97c698b Update: Translations from eints
norwegian (bokmal): 11 changes by eriksorngard
russian: 5 changes by Ln-Wolf
spanish: 5 changes by MontyMontana
portuguese (brazilian): 7 changes by pasantoro
2 months ago
Peter Nelson 1773c5b810
Change: Increase object/station/roadstop class limit. (#12094)
The class limit is arbitrary and not stored in game state.

This change prevents all entities in classes after the 255th class from being dumped into the first class.
2 months ago
Rubidium df8eeb1b10 Codechange: use C++ algorithms to determine the SaveLoadFormat 2 months ago
Peter Nelson 77f27e0804 Change: Expose NewGRF classes and specs as spans and simplify iteration.
This replaces some index-based loops with range loops.
2 months ago
Peter Nelson 052f421327 Change: Use vector/iterators/algorithms instead of C-array/loops for NewGRF classes. 2 months ago
Peter Nelson 34758d0921 Change: Allow string mapping by function instead of pointer.
This allows mapping of strings to objects that may be moved between loading stages.
2 months ago
Peter Nelson 90ca3515da
Fix #12459, f6a88e4: Crashes when deleting news messages. (#12460)
The updated logic in f6a88e4 for deleting news messages did things in the wrong order.
2 months ago
Peter Nelson 2976a46d06
Fix 54be756: Terminating NUL byte was not skipped in ReadString(). (#12462) 2 months ago
Rubidium 883d3e7a9f Codechange: use std::span instead of pointer + length 2 months ago
Peter Nelson 54be756aae
Codechange: Pass NewGRF strings as std::string_view instead of char *. (#12461) 2 months ago
Peter Nelson dc7c2bb30d
Fix: Draw continuation lines for engine variant hierarchy tree. (#12434) 2 months ago
Peter Nelson de4e00c93f
Codechange: Pass by reference to UpdateWidgetSize. (#12457)
These parameters are always provided and not optional.
2 months ago
translators b5ad28022d Update: Translations from eints
english (au): 5 changes by krysclarke
chinese (simplified): 1 change by WenSimEHRP
catalan: 7 changes by J0anJosep
portuguese (brazilian): 27 changes by pasantoro
2 months ago
Peter Nelson 1b127628cb
Codechange: Use dynamic_cast with FindWindowById. (#12458)
Missed from 74e09ab.
2 months ago
Peter Nelson 7e28605830 Fix: Use reinterpret_cast instead of C-style cast to align pointers. 2 months ago
Peter Nelson 4daf95b878 Fix: Use static_cast instead of C-cast to avoid hidden errors. 2 months ago
Peter Nelson cdfffb551c Fix: sym->sym accidentally changed to sym prevented keys working with SDL. 2 months ago
Peter Nelson 74e09abf76
Codechange: Use dynamic_cast instead of C-cast after FindWindowById. (#12448)
dynamic_cast was used in most places, but not all.
2 months ago
Rubidium 4e6d4fcf32 Codechange: replace for loops with endof with range-based for loops 2 months ago
translators 095bdf32fe Update: Translations from eints
greek: 5 changes by gh658804
finnish: 7 changes by hpiirai
ukrainian: 56 changes by Quantom2, 14 changes by imlystyi
latvian: 14 changes by lexuslatvia
portuguese: 14 changes by azulcosta
portuguese (brazilian): 10 changes by pasantoro
polish: 5 changes by pAter-exe
2 months ago
Rubidium 62f5c595f3 Codechange: use range-based for loops and let count be correct count 2 months ago
rubidium42 4718971ccc Codechange: use std::size instead of lengthof for town names 2 months ago
Peter Nelson 995fca58a2
Codechange: Use begin/end instead of endof for some industry arrays. (#12447) 2 months ago
Rubidium d7fa614a9d Codechange: use range-based for loop for debug levels and fix global variable naming 2 months ago
Rubidium 1fcf1a136d Codechange: use single function returning std::span over two functions that return size and begin 2 months ago
translators 0f6bf90731 Update: Translations from eints 2 months ago
Peter Nelson eb094a953c
Codefix: Social Plugins widget parts were not constexpr. (#12443)
Widget lists were made constexpr except these.
2 months ago
Peter Nelson 34ba969c74
Change: Display more useful information in sprite aligner than sprite ID. (#12439)
Sprite IDs are not useful information given they change don't refer to anything outside the loaded game.

Instead, include the filename and nfo line at minimum, and include action A or action 5 sprite replacement information if applicable.
2 months ago
Peter Nelson fbdf26800b
Codechange: Use std::initializer_list and range-for for credits window. (#12431)
Replaces C-style array, indexed looping, and char * strings.
2 months ago
Peter Nelson a4c2f0778a
Codechange: Use range-for to iterate keycode-to-name lookups. (#12429)
Replaces C-style looping.
2 months ago
Peter Nelson b905209421
Fix: Viewport signs assume small font is smaller than normal font. (#12422)
If the small font is set to a larger size than the normal font for some reason, viewport signs would be drawn incorrect as the area marked dirty only considered the normal size font.
2 months ago
Peter Nelson 830c9e2de8 Codechange: Simplify iteration of framerate performance elements. 2 months ago
Rubidium 97bea563d7 Codechange: let lengthof fail when anything that isn't a C-style array is passed 2 months ago
Rubidium c544a2be0a Fix: do not use lengthof() for non C-style arrays 2 months ago
translators eaafc57de6 Update: Translations from eints
swedish: 6 changes by joeax910
norwegian (bokmal): 2 changes by eriksorngard
chinese (simplified): 2 changes by WenSimEHRP
dutch: 2 changes by Afoklala
2 months ago
Peter Nelson 8e881471c1 Codechange: Pass replacement blitter name as string_view instead char *. 2 months ago
Peter Nelson 6771dbe62b Codechange: Use range-for to find replacement blitter. 2 months ago
Peter Nelson a866166673
Codechange: Use initializer_list and range-for for OpenTTD title. (#12430)
Replaces C-style array and looping.
2 months ago
Peter Nelson 2cc700d606
Codechange: Replace colour_dropdown array with StringID arithmetic. (#12426)
This assumes that the string colours are in order, but that is already assumed elsewhere.

Removes old C-style array access.
2 months ago
Peter Nelson 83da886093
Fix: Aircraft crash counter was too low to reach ground. (#12425)
Aircraft can float above the ground when crashed as the counter limit to reach the ground is too low.

Instead reset the counter until the aircraft reaches the ground, then continue the timer.
2 months ago
Peter Nelson e8c78df39e
Fix #12233: Mini order list overlaps vehicle group name. (#12423)
Move mini order list down one line to make room.
2 months ago
Peter Nelson d11622b9a0
Fix #12114: Viewport coords of crashed aircraft not updated when falling. (#12424)
This results in the aircraft glitching as the wrong viewport area is drawn.
2 months ago
translators df3e5ade11 Update: Translations from eints
korean: 2 changes by telk5093
portuguese (brazilian): 1 change by pasantoro
2 months ago
Peter Nelson 7572cfd103 Codechange: Redefine ZOOM_LVL so that ZOOM_LVL_NORMAL is 1x zoom.
This matches expectations of what normal zoom means.
2 months ago
Peter Nelson 9854553e10 Codechange: ZOOM_LVL_SHIFT/BASE are not actually ZOOM_LVLs.
Rename to ZOOM_BASE_SHIFT and ZOOM_BASE respectively, and derive from ZOOM_LVL instead of numeric value.
2 months ago
Peter Nelson 3c94e81665 Codechange: Use ZOOM_LVL_MIN to refer to first zoom level.
Many uses of ZOOM_LVL_NORMAL actually just want the first zoom level slot, so use ZOOM_LVL_MIN to make this clearer.
2 months ago
Peter Nelson 7c322ebcf1 Codechange: Define a ZOOM_LVL for minimum text effect visibility. 2 months ago
Peter Nelson 197fb00d31
Fix #12395: Ensure president name widget is tall enough. (#12419) 2 months ago
Peter Nelson 338def1b06
Fix: Segfault when using -q without providing a . character. (#12418)
Use std::filesystem::path to find extension instead of strrchr.
2 months ago
Peter Nelson f6a88e40a4
Codechange: Use std::list for News Items. (#12338) 2 months ago
translators 08cf106fc6 Update: Translations from eints
english (us): 2 changes by 2TallTyler
finnish: 2 changes by hpiirai
ukrainian: 2 changes by Quantom2
danish: 2 changes by beruic
portuguese (brazilian): 22 changes by pasantoro
2 months ago
Loïc Guilloux 243c6bead3
Fix #12415, 9c49a61, df400ef: Aircraft::tile is valid only for front vehicle (#12416) 2 months ago
translators 931aa39018 Update: Translations from eints
english (au): 2 changes by krysclarke
swedish: 2 changes by joeax910
greek: 2 changes by gh658804
russian: 3 changes by its5Q
catalan: 2 changes by J0anJosep
spanish: 2 changes by MontyMontana
portuguese: 2 changes by azulcosta
portuguese (brazilian): 27 changes by pasantoro
polish: 2 changes by pAter-exe
2 months ago
Peter Nelson 9750826590
Fix a29766d: Wrong scrolling dropdown list position with RTL. (#12412) 2 months ago
translators 72b5c6a591 Update: Translations from eints
vietnamese: 1 change by KhoiCanDev
greek: 83 changes by gh658804
german: 3 changes by Wuzzy2
ukrainian: 54 changes by Quantom2
spanish: 4 changes by MontyMontana
portuguese (brazilian): 2 changes by pasantoro
2 months ago
Peter Nelson 2047c27445 Codechange: Move drop down list item definitions to separate header.
This reduces the scope of the definitions which are no longer needed to create the common lists.
2 months ago
Peter Nelson 56cac21086 Codechange: Use functions to create common drop down list items. 2 months ago
Loïc Guilloux 11aa3694fa
Fix: [Win32] Force font mapper to only use TrueType fonts (#12406) 2 months ago
Peter Nelson 3de8853e29 Codechange: Store accepted and produced cargo in vector instead of array.
Most industries do not use the full 16 slots, so this can save a little memory and iteration time.
2 months ago
Peter Nelson 00e0021e3a Codechange: Don't assume accepted/produced slot exists. 2 months ago
Peter Nelson 295508fc53 Codechange: Avoid lengthof() on std::array. 2 months ago
Peter Nelson f79ec7955a Codechange: Explicitly reset old industry data before loading games. 2 months ago
Peter Nelson bd2a92331b Codechange: Use inline and std::array for old industry structures.
This avoids separate declaration/definition, and less C-style arrays.
2 months ago
Peter Nelson f6b38e8e06 Codechange: Remove optional MakeCargo() parameters that are never changed.
Default values are always used, so don't need to be parameters.
2 months ago
Peter Nelson e4fc8ef595 Codechange: Use std::span for industry cargo window instead of pointer + length. 2 months ago
merni-ns 6f36f3d714
Fix #11055: Make saveload failure error messages consistent with others (#12247)
The save/load error messages were combined using string parameters, rather than using the built-in functionality of error dialogs.
2 months ago
merni-ns 92a171c3e0
Doc: Improve the output and documentation of the font command. (#12392)
Now that the default font =/= sprite font, there is a different way to invoke the sprite font, and default size applies to default (not sprite).
Also, interface scaling now affects the font size.
2 months ago
Peter Nelson d68e5159e1
Feature: Allow base sounds set to be changed mid-game. (#12399) 2 months ago
translators 3d2a8fb60c Update: Translations from eints
welsh: 5 changes by Ansbaradigeidfran
ukrainian: 11 changes by StepanIvasyn
catalan: 1 change by J0anJosep
portuguese (brazilian): 69 changes by pasantoro
2 months ago
Peter Nelson d683ec0183
Codechange: Move dropdown and slider out of widgets directory. (#12403)
Also shuffle headers to place widget includes near end.

This leaves the widgets directory solely for defining Widget IDs.
2 months ago
Rubidium d5e28a904d Fix fb9d4af: use different nonces for key exchange and stream encryption 2 months ago
translators 9954187680 Update: Translations from eints
greek: 172 changes by gh658804
ukrainian: 18 changes by StepanIvasyn
portuguese (brazilian): 9 changes by pasantoro
2 months ago
Patric Stout 77f02faf15
Codefix 977aba73be: also update comment about removal of is_random (#12400) 2 months ago
translators 340c2802da Update: Translations from eints
ukrainian: 14 changes by StepanIvasyn
dutch: 3 changes by Afoklala
portuguese (brazilian): 34 changes by pasantoro
2 months ago
Patric Stout e866ca8adc
Cleanup 69d5b9d3: actually clean up all remnants of "no-thread" builds (#12398) 2 months ago
Patric Stout c0308acb03
Fix: "-q" displays NewGRF IDs in the wrong byte-order (#12397) 2 months ago
Peter Nelson 8d312b305b
Codechange: Replace currency macros with functions. (#12396) 2 months ago
translators e21c12afeb Update: Translations from eints
finnish: 39 changes by hpiirai
ukrainian: 23 changes by StepanIvasyn
danish: 1 change by bscargo
french: 1 change by Lishouuu
portuguese: 1 change by azulcosta
portuguese (brazilian): 100 changes by pasantoro
2 months ago
Peter Nelson e16b982b6a Codechange: Use iteration when dealing with all HouseSpecs. 2 months ago
Peter Nelson 3e83dcedfd Codechange: Allocate houses dynamically instead of from a fixed array.
This uses vectors for HouseSpecs and global/town building counts.
2 months ago
Peter Nelson 8746be8bf2
Codechange: Use FindVehiclesWithOrder when removing a road stop. (#12144) 2 months ago
frosch 907cb4fc53
Fix: [Script] ScriptSubsidy::GetExpireDate should return an economy-date (#12372) 2 months ago
Michael Lutz 8fb7d74dfe Fix eabb9db: Drag building of road stops should not allow mixing z levels. 2 months ago
Michael Lutz 69acc132ca Fix #12387, eabb9db: [NewGRF] Wrong tile offset passed to rail station CB 149 (slope check) 2 months ago
Loïc Guilloux 71087bb6d3
Change: [CI] Always use latest stable Xcode (#12390) 2 months ago
translators ca53e134be Update: Translations from eints
swedish: 14 changes by sereneavatar
finnish: 2 changes by hpiirai
ukrainian: 18 changes by StepanIvasyn
latvian: 4 changes by lexuslatvia
portuguese (brazilian): 13 changes by pasantoro
2 months ago
Jonathan G Rennison 433484cda3
Fix #12388: Vehicle::CopyVehicleConfigAndStatistics not releasing unit number (#12389) 2 months ago
translators b8b01818ca Update: Translations from eints
english (au): 1 change by krysclarke
norwegian (bokmal): 1 change by eriksorngard
spanish (mexican): 32 changes by rgonzalez-py
english (us): 3 changes by 2TallTyler
russian: 3 changes by Ln-Wolf
ukrainian: 18 changes by StepanIvasyn
lithuanian: 6 changes by dziugas1959
portuguese (brazilian): 11 changes by pasantoro
2 months ago
translators 575336ef43 Update: Translations from eints
swedish: 20 changes by joeax910
greek: 182 changes by gh658804
ukrainian: 11 changes by StepanIvasyn
slovak: 45 changes by puco
lithuanian: 14 changes by dziugas1959
portuguese (brazilian): 11 changes by pasantoro
2 months ago
Rubidium 8e12bd35ae Fix: server shutdown and newgame packets should be stable
The server sends shutdown and newgame (reboot) packets to any connected client.
This can be useful, so you can tell clients that are trying to join that the
server is restarting. However, that means that packets can be sent before a
version check has been done.
So, these packets should be in the stable packet range instead of the one that
is unstable and guarded by a version check.
2 months ago
Peter Nelson 4751179dc5
Codefix: Remove unused class member and extraneous spaces. (#12378) 2 months ago
Rubidium 9aa6669266 Fix: inconsistent check for client authorized status 2 months ago
Rubidium 84bbe235e4 Fix: do not send chat to clients that have not authorized yet 2 months ago
Tyler Trahan f71ada4f30
Fix #12268: Capitalize "Wait to unbunch" order string (#12375) 2 months ago
SamuXarick f845b4bbc3
Fix: Changing NPF max search nodes while in-game had no effect (#12194) 2 months ago
translators 40a75e0b8d Update: Translations from eints
swedish: 19 changes by joeax910
norwegian (bokmal): 2 changes by eriksorngard
ukrainian: 6 changes by StepanIvasyn
french: 3 changes by ottdfevr
portuguese (brazilian): 23 changes by pasantoro
polish: 1 change by pAter-exe
2 months ago
Peter Nelson 97c1738541
Fix #12368: Incorrect offset for click position within industry chain window. (#12370) 2 months ago
Peter Nelson eebfb83aa2
Fix 2fd9096: Label for fruit incorrectly changed to `FRUI` from `FRUT`. (#12367) 2 months ago
Tyler Trahan 8928f4979a
Change: Add dividers in vehicle group action dropdown (#12284) 2 months ago
Rubidium caa7c44052 Cleanup: remove checks for old MSVC versions 2 months ago
Rubidium d09b5aaeba Codechange: use int32_t instead of uint16_t for scroll bar position/size/capacity 2 months ago
Rubidium c01bf06ee1 Codefix: some minor errors in tcp-game protocol documentation 2 months ago
translators 018944dc20 Update: Translations from eints
english (au): 2 changes by krysclarke
vietnamese: 1 change by KhoiCanDev
chinese (simplified): 9 changes by WenSimEHRP
greek: 152 changes by gh658804
russian: 3 changes by Ln-Wolf
finnish: 2 changes by hpiirai
ukrainian: 9 changes by StepanIvasyn
danish: 2 changes by bscargo
portuguese: 5 changes by azulcosta
portuguese (brazilian): 35 changes by pasantoro
2 months ago
Peter Nelson 668186ca5b
Codechange: Remove macros involved with NewGRFClass. (#12363)
Use direct class instantiation instead.
2 months ago
Peter Nelson ff35288ddf
Fix: Don't let CT_INVALID map to valid cargo type. (#12364) 2 months ago
glx22 02c00f3e3e Change: [Script] Use economy time for ScriptDate 2 months ago
glx22 704e871a0e Revert bbdbf9a: ScriptTimeMode was not the best solution for economy/calendar support 2 months ago
frosch 603154899a
Add: [SDL2] Driver parameter 'no_mouse_capture' to ease interactive debugging (#12336) 2 months ago
Rubidium 0f25eaa271 Fix: crash to desktop when attempting to join a company while not joined (yet) 2 months ago
Jonathan G Rennison 515303b8be Fix #12092: Incorrect x-axis in cargo payment graph window 2 months ago
Tyler Trahan 2732b3d6c6
Change: Show unbunching action in timetable window (#12351) 2 months ago
Peter Nelson ea74ca0a76
Fix #12347: Crash attempting to find catchment tiles of a station with no catchment area. (#12348) 2 months ago
Rubidium f599108c16 Codechange: move 'months_empty' to CompanyProperties 2 months ago
translators 4321cca5fb Update: Translations from eints
vietnamese: 1 change by KhoiCanDev
chinese (simplified): 1 change by WenSimEHRP
ukrainian: 11 changes by StepanIvasyn
catalan: 1 change by J0anJosep
danish: 1 change by bscargo
dutch: 3 changes by Afoklala
portuguese (brazilian): 58 changes by pasantoro
2 months ago
Peter Nelson df2ee7b06c
Cleanup: Remove old SaveLoad workarounds for MS VS 2017. (#12355) 2 months ago
Rubidium e904122441 Codefix: follow coding style 2 months ago
merni-ns 7457f8d0ff
Codefix: Incorrect pluralisation in last service/service interval texts (#12352) 2 months ago
raddari 5751da7809
Fix #7982: Show existing coverage with unambiguous adjacent station (#12346)
When hovering a tile containing a station, show existing coverage for
that station even when adjacent to a different station.

Co-authored-by: Peter Nelson <peter@fuzzle.org>
2 months ago
translators e141734e54 Update: Translations from eints
english (au): 1 change by krysclarke
norwegian (bokmal): 1 change by eriksorngard
english (us): 1 change by 2TallTyler
korean: 3 changes by telk5093
german: 1 change by Wuzzy2
finnish: 3 changes by hpiirai
ukrainian: 12 changes by StepanIvasyn
portuguese (brazilian): 81 changes by pasantoro
polish: 1 change by pAter-exe
2 months ago
Jonathan G Rennison 2189607c34 Codechange: Reduce size of class WaterRegion
The tile patch array is 256 bytes and is not needed for the majority
of water regions, change it to be optional via std::unique_ptr
2 months ago
Jonathan G Rennison e42aec5a89 Fix #12305: Crash with large positive sprite x offset in engine preview window 2 months ago
translators 0eaeeaabb6 Update: Translations from eints
russian: 2 changes by Ln-Wolf
catalan: 2 changes by J0anJosep
portuguese (brazilian): 23 changes by pasantoro
3 months ago
Tyler Trahan 704d3b8a9b
Fix #12342: Add missing ellipsis to town generation error string (#12343) 3 months ago
translators 00a09af1fd Update: Translations from eints
english (us): 2 changes by 2TallTyler
vietnamese: 2 changes by KhoiCanDev
german: 2 changes by Wuzzy2
ukrainian: 2 changes by StepanIvasyn
portuguese (brazilian): 9 changes by pasantoro
3 months ago
Owen Rudge d4a6ee9554 Change: [CI] Use Azure Code Signing for Windows build 3 months ago
translators 3a3d8f3b53 Update: Translations from eints
english (au): 2 changes by krysclarke
norwegian (bokmal): 2 changes by eriksorngard
chinese (simplified): 31 changes by lysinelai
greek: 7 changes by Xertoveizer
ukrainian: 14 changes by StepanIvasyn
danish: 2 changes by bscargo
lithuanian: 15 changes by dziugas1959
spanish: 2 changes by MontyMontana
french: 2 changes by glx22
portuguese (brazilian): 52 changes by pasantoro
polish: 2 changes by pAter-exe
3 months ago
Peter Nelson 737e3feaf0
Codechange: Don't delete news items or NewGRF window when EffectVehicle is deleted. (#12334)
EffectVehicles never create news and can't be debugged, so searching for news items and windows to delete just wastes time.
3 months ago
Rubidium 7580eac2d5 Codechange: create helper class for useful NetworkAuthorizedKeys functions 3 months ago
Peter Nelson 2485de9462
Codefix: Widget should be passed as WidgetID type. (#12332) 3 months ago
Peter Nelson 107c208d87
Codechange: Use single list for hierarchical group lists. (#12330)
Replace both group list implementations (vehicle group list and company colour group list) with a single implementation, using a struct to hold the group and indentation level instead of two separate lists. Parts that were previously duplicated are now shared.
3 months ago
translators ec3c8d3462 Update: Translations from eints
ukrainian: 12 changes by StepanIvasyn
portuguese (brazilian): 21 changes by pasantoro
polish: 52 changes by pAter-exe
3 months ago
Rubidium 4af089b9be Feature: console command to change authorized keys 3 months ago
Rubidium b7dfa3eb90 Feature: authorized key authentication for rcon 3 months ago
Rubidium 1cf8799810 Feature: encrypt the connection between game server and client 3 months ago
Rubidium d26629c15b Codechange: make encoded length of packet size and type more explicit 3 months ago
Rubidium 5706801ea7 Feature: authenticate to the server without sending the password
Either using password-authentication key exchange or via authorized keys
3 months ago
Rubidium dd532cbc77 Codechange: add setting for authorized/secret/public keys 3 months ago
Rubidium fb9d4afa5c Codechange: add set of classes providing authentication and encryption 3 months ago
Peter Nelson 88cf99017a
Fix #12302: Allow empty train engines to use an invalid cargo type. (#12325)
The cargo type will be forced to the first available type (usually passengers) instead of the engine being disabled.
3 months ago
Peter Nelson 322ca6ef54 Codechange: Shuffle members of Vehicle to reduce size.
This reduces space wasted due to member alignment.
3 months ago
Peter Nelson 3fc7b3b9a0 Codechange: Cache train curve speed limit can be stored in 16 bits.
Cache curve speed modifier and max curve speed are both 16 bit values so can be stored in 16 bit types instead of 32 bit types.
3 months ago
Patric Stout f08da1d373
Codechange: the "no revision detected" string is with four zeros (norev0000) (#12328) 3 months ago
Peter Nelson ab94c8b511
Codechange: Iterate order lists instead of vehicles to find if any vehicle visits a station. (#12315)
This reduces the search time as shared orders are only searched once and non-front vehicles are skipped.
3 months ago
translators 6c5a8f55df Update: Translations from eints
norwegian (bokmal): 58 changes by eriksorngard
vietnamese: 45 changes by KhoiCanDev
greek: 21 changes by Xertoveizer
ukrainian: 5 changes by StepanIvasyn
tamil: 24 changes by merni-ns
lithuanian: 7 changes by dziugas1959
portuguese (brazilian): 61 changes by pasantoro
polish: 21 changes by aefoes
3 months ago

@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-20.04
container:
# If you change this version, change the number in the cache step too.
image: emscripten/emsdk:3.1.42
image: emscripten/emsdk:3.1.57
steps:
- name: Checkout
@ -23,12 +23,11 @@ jobs:
uses: actions/cache@v4
with:
path: /emsdk/upstream/emscripten/cache
key: 3.1.42-${{ runner.os }}
key: 3.1.57-${{ runner.os }}
- name: Patch Emscripten to support LZMA
- name: Add liblzma support
run: |
cd /emsdk/upstream/emscripten
patch -p1 < ${GITHUB_WORKSPACE}/os/emscripten/emsdk-liblzma.patch
cp ${GITHUB_WORKSPACE}/os/emscripten/ports/liblzma.py /emsdk/upstream/emscripten/tools/ports/contrib/
- name: Build (host tools)
run: |

@ -26,6 +26,11 @@ jobs:
MACOSX_DEPLOYMENT_TARGET: 10.13
steps:
- name: Setup Xcode version
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest-stable
- name: Checkout
uses: actions/checkout@v4

@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest
container:
# If you change this version, change the number in the cache step too.
image: emscripten/emsdk:3.1.42
image: emscripten/emsdk:3.1.57
steps:
- name: Checkout
@ -38,12 +38,11 @@ jobs:
uses: actions/cache@v4
with:
path: /emsdk/upstream/emscripten/cache
key: 3.1.42-${{ runner.os }}
key: 3.1.57-${{ runner.os }}
- name: Patch Emscripten to support LZMA
- name: Add liblzma support
run: |
cd /emsdk/upstream/emscripten
patch -p1 < ${GITHUB_WORKSPACE}/os/emscripten/emsdk-liblzma.patch
cp ${GITHUB_WORKSPACE}/os/emscripten/ports/liblzma.py /emsdk/upstream/emscripten/tools/ports/contrib/
- name: Build (host tools)
run: |

@ -17,6 +17,11 @@ jobs:
MACOSX_DEPLOYMENT_TARGET: 10.13
steps:
- name: Setup Xcode version
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest-stable
- name: Download source
uses: actions/download-artifact@v4
with:

@ -100,21 +100,6 @@ jobs:
with:
arch: ${{ matrix.host }}
- name: Import code signing certificate
shell: powershell
# If this is run on a fork, there may not be a certificate set up - continue in this case
continue-on-error: true
run: |
$tempFile = [System.IO.Path]::GetTempFileName()
$bytes = [System.Convert]::FromBase64String($env:WINDOWS_CERTIFICATE_P12)
[IO.File]::WriteAllBytes($tempFile, $bytes)
$pwd = ConvertTo-SecureString $env:WINDOWS_CERTIFICATE_PASSWORD -AsPlainText -Force
Import-PfxCertificate -FilePath $tempFile -CertStoreLocation Cert:\CurrentUser\My -Password $pwd
Remove-Item $tempFile
env:
WINDOWS_CERTIFICATE_P12: ${{ secrets.WINDOWS_CERTIFICATE_P12 }}
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
- name: Build (with installer)
if: inputs.is_tag == 'true'
shell: bash
@ -131,7 +116,6 @@ jobs:
-DHOST_BINARY_DIR=${GITHUB_WORKSPACE}/build-host \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DOPTION_SURVEY_KEY=${{ inputs.survey_key }} \
-DWINDOWS_CERTIFICATE_COMMON_NAME="${WINDOWS_CERTIFICATE_COMMON_NAME}" \
# EOF
echo "::endgroup::"
@ -139,7 +123,12 @@ jobs:
cmake --build . --target openttd
echo "::endgroup::"
env:
WINDOWS_CERTIFICATE_COMMON_NAME: ${{ secrets.WINDOWS_CERTIFICATE_COMMON_NAME }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
AZURE_CODESIGN_ACCOUNT_NAME: ${{ secrets.AZURE_CODESIGN_ACCOUNT_NAME }}
AZURE_CODESIGN_ENDPOINT: ${{ secrets.AZURE_CODESIGN_ENDPOINT }}
AZURE_CODESIGN_PROFILE_NAME: ${{ secrets.AZURE_CODESIGN_PROFILE_NAME }}
- name: Build (without installer)
if: inputs.is_tag != 'true'
@ -156,7 +145,6 @@ jobs:
-DHOST_BINARY_DIR=${GITHUB_WORKSPACE}/build-host \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DOPTION_SURVEY_KEY=${{ inputs.survey_key }} \
-DWINDOWS_CERTIFICATE_COMMON_NAME="${WINDOWS_CERTIFICATE_COMMON_NAME}" \
# EOF
echo "::endgroup::"
@ -164,7 +152,12 @@ jobs:
cmake --build . --target openttd
echo "::endgroup::"
env:
WINDOWS_CERTIFICATE_COMMON_NAME: ${{ secrets.WINDOWS_CERTIFICATE_COMMON_NAME }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
AZURE_CODESIGN_ACCOUNT_NAME: ${{ secrets.AZURE_CODESIGN_ACCOUNT_NAME }}
AZURE_CODESIGN_ENDPOINT: ${{ secrets.AZURE_CODESIGN_ENDPOINT }}
AZURE_CODESIGN_PROFILE_NAME: ${{ secrets.AZURE_CODESIGN_PROFILE_NAME }}
- name: Create breakpad symbols
shell: bash
@ -198,13 +191,15 @@ jobs:
- name: Sign installer
if: inputs.is_tag == 'true'
shell: bash
# If this is run on a fork, there may not be a certificate set up - continue in this case
continue-on-error: true
run: |
cd ${GITHUB_WORKSPACE}/build/bundles
../../os/windows/sign.bat *.exe "${WINDOWS_CERTIFICATE_COMMON_NAME}"
${GITHUB_WORKSPACE}/os/windows/sign.bat "${GITHUB_WORKSPACE}/build/bundles"
env:
WINDOWS_CERTIFICATE_COMMON_NAME: ${{ secrets.WINDOWS_CERTIFICATE_COMMON_NAME }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
AZURE_CODESIGN_ACCOUNT_NAME: ${{ secrets.AZURE_CODESIGN_ACCOUNT_NAME }}
AZURE_CODESIGN_ENDPOINT: ${{ secrets.AZURE_CODESIGN_ENDPOINT }}
AZURE_CODESIGN_PROFILE_NAME: ${{ secrets.AZURE_CODESIGN_PROFILE_NAME }}
- name: Store bundles
uses: actions/upload-artifact@v4

@ -343,7 +343,7 @@ if(NOT OPTION_DEDICATED)
endif()
endif()
include(CheckAtomic)
include(3rdparty/llvm/CheckAtomic)
if(APPLE)
link_package(Iconv TARGET Iconv::Iconv)

@ -29,7 +29,7 @@ open most older savegames or use the content downloading system.
## Windows
You need Microsoft Visual Studio 2017 or more recent.
You need Microsoft Visual Studio 2022 or more recent.
You can download the free Visual Studio Community Edition from Microsoft at
https://visualstudio.microsoft.com/vs/community/.
@ -65,7 +65,7 @@ To install both the x64 (64bit) and x86 (32bit) variants (though only one is nec
You can open the folder (as a CMake project). CMake will be detected, and you can compile from there.
If libraries are installed but not found, you need to set VCPKG_TARGET_TRIPLET in CMake parameters.
For Visual Studio 2017 you also need to set CMAKE_TOOLCHAIN_FILE.
For Visual Studio 2022 you also need to set CMAKE_TOOLCHAIN_FILE.
(Typical values are shown in the MSVC project file command line example)
Alternatively, you can create a MSVC project file via CMake. For this
@ -75,7 +75,7 @@ that comes with vcpkg. After that, you can run something similar to this:
```powershell
mkdir build
cd build
cmake.exe .. -G"Visual Studio 16 2019" -DCMAKE_TOOLCHAIN_FILE="<location of vcpkg>\vcpkg\scripts\buildsystems\vcpkg.cmake" -DVCPKG_TARGET_TRIPLET="x64-windows-static"
cmake.exe .. -G"Visual Studio 17 2022" -DCMAKE_TOOLCHAIN_FILE="<location of vcpkg>\vcpkg\scripts\buildsystems\vcpkg.cmake" -DVCPKG_TARGET_TRIPLET="x64-windows-static"
```
Change `<location of vcpkg>` to where you have installed vcpkg. After this
@ -83,7 +83,7 @@ in the build folder are MSVC project files. MSVC can rebuild the project
files himself via the `ZERO_CHECK` project.
## All other platforms
Minimum required version of CMake is 3.9.
Minimum required version of CMake is 3.16.
By default this produces a Debug build with assertations enabled.
This is a far slower build than release builds.
@ -110,17 +110,14 @@ builds.
- `-DOPTION_USE_ASSERTS=OFF`: disable asserts. Use with care, as assert
statements capture early signs of trouble. Release builds have them
disabled by default.
- `-DOPTION_USE_THREADS=OFF`: disable the use of threads. This will block
the interface in many places, and in general gives a worse experience of
the game. Use with care.
- `-DOPTION_TOOLS_ONLY=ON`: only build tools like `strgen`. Does not build
the game itself. Useful for cross-compiling.
## Supported compilers
Every compiler that is supported by CMake and supports C++17, should be
Every compiler that is supported by CMake and supports C++20, should be
able to compile OpenTTD. As the exact list of compilers changes constantly,
we refer to the compiler manual to see if it supports C++17, and to CMake
we refer to the compiler manual to see if it supports C++20, and to CMake
to see if it supports your compiler.
## Compilation of base sets

@ -212,6 +212,9 @@ See `src/3rdparty/monocypher/LICENSE.md` for the complete license text.
The OpenTTD Social Integration API in `src/3rdparty/openttd_social_integration_api` is licensed under the MIT license.
See `src/3rdparty/openttd_social_integration_api/LICENSE` for the complete license text.
The atomic datatype support detection in `cmake/3rdparty/llvm/CheckAtomic.cmake` is licensed under the Apache 2.0 license.
See `cmake/3rdparty/llvm/LICENSE.txt` for the complete license text.
## 4.0 Credits
See [CREDITS.md](./CREDITS.md)

@ -1,3 +1,11 @@
#
# This was originally part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
# See (https://llvm.org/)LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
# Modifications have been made to suit building OpenTTD.
#
# atomic builtins are required for threading support.
INCLUDE(CheckCXXSourceCompiles)
@ -48,6 +56,8 @@ else()
check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITHOUT_LIB)
# If not, check if the library exists, and atomics work with it.
if(NOT HAVE_CXX_ATOMICS_WITHOUT_LIB)
# check_library_exists requires the C-compiler as the atomic functions are built-in declared.
enable_language(C)
check_library_exists(atomic __atomic_fetch_add_4 "" HAVE_LIBATOMIC)
if(HAVE_LIBATOMIC)
list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic")
@ -69,6 +79,8 @@ else()
check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITHOUT_LIB)
# If not, check if the library exists, and atomics work with it.
if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB)
# check_library_exists requires the C-compiler as the atomic functions are built-in declared.
enable_language(C)
check_library_exists(atomic __atomic_load_8 "" HAVE_CXX_LIBATOMICS64)
if(HAVE_CXX_LIBATOMICS64)
list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic")

@ -0,0 +1,279 @@
==============================================================================
The LLVM Project is under the Apache License v2.0 with LLVM Exceptions:
==============================================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
---- LLVM Exceptions to the Apache 2.0 License ----
As an exception, if, as a result of your compiling your source code, portions
of this Software are embedded into an Object form of such source code, you
may redistribute such embedded portions in such Object form without complying
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
In addition, if you combine or link compiled forms of this Software with
software that is licensed under the GPLv2 ("Combined Software") and if a
court of competent jurisdiction determines that the patent provision (Section
3), the indemnity provision (Section 9) or other Section of the License
conflicts with the conditions of the GPLv2, you may retroactively and
prospectively choose to deem waived or otherwise exclude such Section(s) of
the License, but only in their entirety and only with respect to the Combined
Software.
==============================================================================
Software from third parties included in the LLVM Project:
==============================================================================
The LLVM Project contains third party software which is under different license
terms. All such code will be identified clearly using at least one of two
mechanisms:
1) It will be in a separate directory tree with its own `LICENSE.txt` or
`LICENSE` file at the top containing the specific license and restrictions
which apply to that software, or
2) It will contain specific license and restriction terms at the top of every
file.
==============================================================================
Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy):
==============================================================================
University of Illinois/NCSA
Open Source License
Copyright (c) 2003-2019 University of Illinois at Urbana-Champaign.
All rights reserved.
Developed by:
LLVM Team
University of Illinois at Urbana-Champaign
http://llvm.org
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal with
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimers.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimers in the
documentation and/or other materials provided with the distribution.
* Neither the names of the LLVM Team, University of Illinois at
Urbana-Champaign, nor the names of its contributors may be used to
endorse or promote products derived from this Software without specific
prior written permission.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
SOFTWARE.

@ -172,10 +172,10 @@ elseif(WIN32)
set(CPACK_PACKAGE_FILE_NAME "openttd-#CPACK_PACKAGE_VERSION#-windows-${CPACK_SYSTEM_NAME}")
if(WINDOWS_CERTIFICATE_COMMON_NAME)
if(DEFINED ENV{AZURE_CODESIGN_PROFILE_NAME})
add_custom_command(TARGET openttd
POST_BUILD
COMMAND "${CMAKE_SOURCE_DIR}/os/windows/sign.bat" "$<TARGET_FILE:openttd>" "${WINDOWS_CERTIFICATE_COMMON_NAME}"
COMMAND "${CMAKE_SOURCE_DIR}/os/windows/sign.bat" "${BINARY_DESTINATION_DIR}"
)
endif()
elseif(UNIX)

@ -57,13 +57,6 @@ function(set_options)
option(OPTION_DEDICATED "Build dedicated server only (no GUI)" OFF)
option(OPTION_INSTALL_FHS "Install with Filesystem Hierarchy Standard folders" ${DEFAULT_OPTION_INSTALL_FHS})
option(OPTION_USE_ASSERTS "Use assertions; leave enabled for nightlies, betas, and RCs" ON)
if(EMSCRIPTEN)
# Although pthreads is supported, it is not in a way yet that is
# useful for us.
option(OPTION_USE_THREADS "Use threads" OFF)
else()
option(OPTION_USE_THREADS "Use threads" ON)
endif()
option(OPTION_USE_NSIS "Use NSIS to create windows installer; enable only for stable releases" OFF)
option(OPTION_TOOLS_ONLY "Build only tools target" OFF)
option(OPTION_DOCS_ONLY "Build only docs target" OFF)
@ -85,7 +78,6 @@ function(show_options)
message(STATUS "Option Dedicated - ${OPTION_DEDICATED}")
message(STATUS "Option Install FHS - ${OPTION_INSTALL_FHS}")
message(STATUS "Option Use assert - ${OPTION_USE_ASSERTS}")
message(STATUS "Option Use threads - ${OPTION_USE_THREADS}")
message(STATUS "Option Use NSIS - ${OPTION_USE_NSIS}")
if(OPTION_SURVEY_KEY)
@ -109,10 +101,6 @@ function(add_definitions_based_on_options)
add_definitions(-DDEDICATED)
endif()
if(NOT OPTION_USE_THREADS)
add_definitions(-DNO_THREADS)
endif()
if(OPTION_USE_ASSERTS)
add_definitions(-DWITH_ASSERT)
else()

@ -49,7 +49,8 @@ if(GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
string(SUBSTRING "${FULLHASH}" 0 10 SHORTHASH)
# Get the last commit date
execute_process(COMMAND ${GIT_EXECUTABLE} show -s --pretty=format:%ci HEAD
set(ENV{TZ} "UTC0")
execute_process(COMMAND ${GIT_EXECUTABLE} show -s --date=iso-local --pretty=format:%cd HEAD
OUTPUT_VARIABLE COMMITDATE
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}

@ -195,10 +195,11 @@ Last updated: 2014-02-23
'src/network/network_func.h'.
(DEBUG_FAILED_DUMP_COMMANDS is explained later)
- Put the 'commands-out.log' into the root save folder, and rename
it to 'commands.log'.
- Run 'openttd -D -d desync=3 -g startsavegame.sav'.
This replays the server log and creates new 'commands-out.log'
and 'dmp_cmds_*.sav' in your autosave folder.
it to 'commands.log'. Strip everything and including the "newgame"
entry from the log.
- Run 'openttd -D -d desync=0 -g startsavegame.sav'.
This replays the server log. Use "-d desync=3" to also create a
new 'commands-out.log' and 'dmp_cmds_*.sav' in your autosave folder.
## 3.2) Evaluation of the replay
@ -226,7 +227,7 @@ Last updated: 2014-02-23
savegames with your own ones from the replay. You can also comment/disable
the 'NOT_REACHED' mentioned above, to get another 'dmp_cmds_*.sav' from
the replay after the mismatch has already been detected.
See Section 3.2 on how to compare savegames.
See Section 3.3 on how to compare savegames.
If the saves differ you have located the Desync between the last dmp_cmds
that match and the first one that does not. The difference of the saves
may point you in the direction of what causes it.
@ -252,16 +253,14 @@ Last updated: 2014-02-23
are replayed. Their internal state will thus not change in the
replay and will differ.
To compare savegame more semantically, there exist some ugly hackish
tools at:
http://devs.openttd.org/~frosch/texts/zpipe.c
http://devs.openttd.org/~frosch/texts/printhunk.c
To compare savegame more semantically, easiest is to first export them
to a JSON format with for example:
The first one decompresses OpenTTD savegames. The second one creates
a textual representation of an uncompressed savegame, by parsing hunks
and arrays and such. With both tools you need to be a bit careful
since they work on stdin and stdout, which may not deal well with
binary data.
https://github.com/TrueBrain/OpenTTD-savegame-reader
If you have the textual representation of the savegames, you can
compare them with regular diff tools.
By running:
python -m savegame_reader --export-json dmp_cmds_NNN.sav | jq . > NNN.json
Now you can use any (JSON) diff tool to compare the two savegames in a
somewhat human readable way.

@ -20,7 +20,6 @@
.Op Fl M Ar musicset
.Op Fl n Ar host Ns Oo : Ns Ar port Oc Ns Op # Ns Ar company
.Op Fl p Ar password
.Op Fl P Ar password
.Op Fl q Ar savegame
.Op Fl r Ar width Ns x Ns Ar height
.Op Fl s Ar driver
@ -100,10 +99,6 @@ play as.
Password used to join server.
Only useful with
.Fl n .
.It Fl P Ar password
Password used to join company.
Only useful with
.Fl n .
.It Fl q Ar savegame
Write some information about the specified savegame and exit.
.It Fl Q

@ -7,15 +7,17 @@ This guide is for OpenTTD developers/maintainers, to release a new version of Op
* If this is a beta version release, skip this step.
* If this is an RC1 (first Release Candidate) build, create a new branch `release/nn` where `nn` is the major version number, then apply changes similar to [PR#9573](https://github.com/OpenTTD/OpenTTD/pull/9573). You also need to forwardport the changelog, as in [PR#10113](https://github.com/OpenTTD/OpenTTD/pull/10113).
* Update CMakeLists.txt
* Add a new (empty) AI compatibility script in bin/ai/
* Add the new version to CheckAPIVersion in src/ai/ai_info.cpp + src/game/game_info.cpp
* Add the new version to src/script/api/ai_changelog.hpp + src/script/api/game_changelog.hpp
* Update the version of regression in bin/ai/regression/regression_info.nut
* Add a note to src/saveload/saveload.h about which savegame version is used in the branch.
* Update the version in `CMakeLists.txt` in the master branch, heading for the next major release, e.g. from 14.0 to 15.0.
* Add a new (empty) AI compatibility script in `bin/ai/`
* Add the new version to CheckAPIVersion in `src/ai/ai_info.cpp` and `src/game/game_info.cpp`
* Add the new version to `src/script/api/ai_changelog.hpp` and `src/script/api/game_changelog.hpp`
* Update the version of regression in `bin/ai/regression/regression_info.nut`
* Add a note to `src/saveload/saveload.h` about which savegame version is used in the branch.
* If this is a later RC or release build and the release branch already exists, you'll need to backport fixes and language from master to this branch, which were merged after the branch diverged from master. You can use these two helper scripts: https://github.com/OpenTTD/scripts/tree/main/backport
* If this is a maintenance release, update the version in `CMakeLists.txt` in the release branch, e.g. from 14.0 to 14.1.
## Step 1: Prepare changelog documentation
1. Update the [changelog](../changelog.txt) with new changes since the last release.
@ -29,20 +31,26 @@ This guide is for OpenTTD developers/maintainers, to release a new version of Op
1. Go to https://github.com/OpenTTD/website/new/main/_posts and write a new announcement post. See a [previous example](https://github.com/OpenTTD/website/pull/238) for a template.
2. Create a new branch for this post and open a PR for it.
3. Write announcement text for socials like Forum/Discord/Twitter/Reddit and include it in the PR.
3. Write announcement text for the store pages and socials like TT-Forums / Discord / Twitter / Reddit / Fosstodon / etc., and include it in the PR.
4. Create a Steam news image for that post and include it in the PR.
5. Check the website post (preview link via checks page) and make corrections. We usually just use the GitHub web interface for this and squash the result later.
5. Check the website post ("View Deployment" link) and make corrections. We usually just use the GitHub web interface for this and squash the result later.
6. Get this PR approved, but do not merge yet.
## Step 3: Make the actual OpenTTD release
1. Go to https://github.com/OpenTTD/OpenTTD/releases/new and create a new tag matching the release number. For the body of the release, see any older release. "Set as a pre-release" for a beta or RC, set as latest for a real release.
2. Merge website PR.
3. Wait for the OpenTTD release checks to be complete.
4. Check that website links to the new release are working and correct, using the [staging website](https://www-staging.openttd.org/).
5. If this is a full release, ask orudge to update the Microsoft Store and TrueBrain to move the release from the "testing" to "default" branch on Steam.
1. Confirm that the version in `CMakeLists.txt` matches the intended release version.
2. Go to https://github.com/OpenTTD/OpenTTD/releases/new and create a new tag matching the release number. For the body of the release, copy in the changelog. "Set as a pre-release" for a beta or RC.
3. Wait for the OpenTTD release workflow to be complete.
4. If this is a full release:
* for `Steam`: under Steamworks -> SteamPipe -> Builds, set the "testing" branch live on the "default" branch. This will request 2FA validation.
* for `GOG`: under Builds, "Publish" the freshly uploaded builds to `Master`, `GOG-use only` and `Testing`.
* for `Microsoft Store`: ask orudge to publish the new release.
Access to `Steam`, `GOG` and/or `Microsoft Store` requires a developer account on that platform.
You will need access to the shared keystore in order to create such an account.
For help and/or access to either or both, please contact TrueBrain.
## Step 4: Tell the world
1. Tag and create a website release to trigger the actions that update the website.
2. After the website is live, make announcements on social media. You may need to coordinate with other developers who can make posts on Twitter, Reddit, Steam, and GOG.
1. Merge the website PR. This will publish the release post.
2. Make announcements on social media and store pages. You may need to coordinate with other developers who can make posts on TT-Forums, Twitter, Reddit, Fosstodon, Discord, Steam, GOG, Microsoft Store, etc.

@ -1,4 +1,3 @@
FROM emscripten/emsdk:3.1.42
FROM emscripten/emsdk:3.1.57
COPY emsdk-liblzma.patch /
RUN cd /emsdk/upstream/emscripten && patch -p1 < /emsdk-liblzma.patch
COPY ports/liblzma.py /emsdk/upstream/emscripten/tools/ports/contrib/liblzma.py

@ -2,8 +2,8 @@
Please use docker with the supplied `Dockerfile` to build for emscripten.
It takes care of a few things:
- Use a version of emscripten we know works
- Patch in LibLZMA support (as this is not supported by upstream)
- Use a version of emscripten we know works.
- Add LibLZMA library under contrib ports.
First, build the docker image by navigating in the folder this `README.md` is in, and executing:
```

@ -1,7 +1,7 @@
# LibLZMA is a custom addition to the emscripten SDK, so it is possible
# someone patched their SDK. Test out if the SDK supports LibLZMA.
include(CheckCXXSourceCompiles)
set(CMAKE_REQUIRED_FLAGS "-sUSE_LIBLZMA=1")
set(CMAKE_REQUIRED_FLAGS "--use-port=contrib.liblzma")
check_cxx_source_compiles("
#include <lzma.h>
@ -12,9 +12,9 @@ check_cxx_source_compiles("
if (LIBLZMA_FOUND)
add_library(LibLZMA::LibLZMA INTERFACE IMPORTED)
set_target_properties(LibLZMA::LibLZMA PROPERTIES
INTERFACE_COMPILE_OPTIONS "-sUSE_LIBLZMA=1"
INTERFACE_LINK_LIBRARIES "-sUSE_LIBLZMA=1"
INTERFACE_COMPILE_OPTIONS "--use-port=contrib.liblzma"
INTERFACE_LINK_LIBRARIES "--use-port=contrib.liblzma"
)
else()
message(WARNING "You are using an emscripten SDK without LibLZMA support. Many savegames won't be able to load in OpenTTD. Please apply 'emsdk-liblzma.patch' to your local emsdk installation.")
message(WARNING "You are using an emscripten SDK without LibLZMA support. Many savegames won't be able to load in OpenTTD. Please copy liblzma.py to your ports/contrib folder in your local emsdk installation.")
endif()

@ -1,198 +0,0 @@
From 84d0e9112d5c87a714abd21ec8547921f46f37b5 Mon Sep 17 00:00:00 2001
From: milek7 <me@milek7.pl>
Date: Tue, 8 Dec 2020 01:03:31 +0100
Subject: [PATCH] Add liblzma port
---
src/settings.js | 4 ++
tools/ports/liblzma.py | 151 +++++++++++++++++++++++++++++++++++++++++
tools/settings.py | 1 +
3 files changed, 156 insertions(+)
create mode 100644 tools/ports/liblzma.py
diff --git a/src/settings.js b/src/settings.js
index f93140d..7b6bec9 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -1451,6 +1451,10 @@ var USE_GIFLIB = false;
// [compile+link]
var USE_LIBJPEG = false;
+// 1 = use liblzma from emscripten-ports
+// [compile+link]
+var USE_LIBLZMA = false;
+
// 1 = use libpng from emscripten-ports
// [compile+link]
var USE_LIBPNG = false;
diff --git a/tools/ports/liblzma.py b/tools/ports/liblzma.py
new file mode 100644
index 0000000..6872a8b
--- /dev/null
+++ b/tools/ports/liblzma.py
@@ -0,0 +1,151 @@
+# Copyright 2020 The Emscripten Authors. All rights reserved.
+# Emscripten is available under two separate licenses, the MIT license and the
+# University of Illinois/NCSA Open Source License. Both these licenses can be
+# found in the LICENSE file.
+
+import os
+import shutil
+import logging
+from pathlib import Path
+
+VERSION = '5.4.2'
+HASH = '149f980338bea3d66de1ff5994b2b236ae1773135eda68b62b009df0c9dcdf5467f8cb2c06da95a71b6556d60bd3d21f475feced34d5dfdb80ee95416a2f9737'
+
+
+def needed(settings):
+ return settings.USE_LIBLZMA
+
+
+def get(ports, settings, shared):
+ ports.fetch_project('liblzma', f'https://tukaani.org/xz/xz-{VERSION}.tar.gz', sha512hash=HASH)
+
+ def create(final):
+ logging.info('building port: liblzma')
+
+ ports.clear_project_build('liblzma')
+
+ source_path = os.path.join(ports.get_dir(), 'liblzma', f'xz-{VERSION}', 'src', 'liblzma')
+ ports.write_file(os.path.join(source_path, 'config.h'), config_h)
+ ports.install_headers(os.path.join(source_path, 'api'), pattern='lzma.h')
+ ports.install_headers(os.path.join(source_path, 'api', 'lzma'), pattern='*.h', target='lzma')
+
+ build_flags = ['-DHAVE_CONFIG_H', '-DTUKLIB_SYMBOL_PREFIX=lzma_', '-fvisibility=hidden']
+ exclude_files = ['crc32_small.c', 'crc64_small.c', 'crc32_tablegen.c', 'crc64_tablegen.c', 'price_tablegen.c', 'fastpos_tablegen.c',
+ 'tuklib_exit.c', 'tuklib_mbstr_fw.c', 'tuklib_mbstr_width.c', 'tuklib_open_stdxxx.c', 'tuklib_progname.c']
+ include_dirs_rel = ['../common', 'api', 'check', 'common', 'delta', 'lz', 'lzma', 'rangecoder', 'simple']
+
+ include_dirs = [os.path.join(source_path, p) for p in include_dirs_rel]
+ ports.build_port(source_path, final, 'liblzma', flags=build_flags, exclude_files=exclude_files, includes=include_dirs)
+
+ return [shared.cache.get_lib('liblzma.a', create, what='port')]
+
+
+def clear(ports, settings, shared):
+ shared.cache.erase_lib('liblzma.a')
+
+
+def process_args(ports):
+ return []
+
+
+def show():
+ return 'liblzma (USE_LIBLZMA=1; public domain)'
+
+
+config_h = '''
+#define ASSUME_RAM 128
+#define ENABLE_NLS 1
+#define HAVE_CHECK_CRC32 1
+#define HAVE_CHECK_CRC64 1
+#define HAVE_CHECK_SHA256 1
+#define HAVE_CLOCK_GETTIME 1
+#define HAVE_DCGETTEXT 1
+#define HAVE_DECL_CLOCK_MONOTONIC 1
+#define HAVE_DECL_PROGRAM_INVOCATION_NAME 1
+#define HAVE_DECODERS 1
+#define HAVE_DECODER_ARM 1
+#define HAVE_DECODER_ARMTHUMB 1
+#define HAVE_DECODER_DELTA 1
+#define HAVE_DECODER_IA64 1
+#define HAVE_DECODER_LZMA1 1
+#define HAVE_DECODER_LZMA2 1
+#define HAVE_DECODER_POWERPC 1
+#define HAVE_DECODER_SPARC 1
+#define HAVE_DECODER_X86 1
+#define HAVE_DLFCN_H 1
+#define HAVE_ENCODERS 1
+#define HAVE_ENCODER_ARM 1
+#define HAVE_ENCODER_ARMTHUMB 1
+#define HAVE_ENCODER_DELTA 1
+#define HAVE_ENCODER_IA64 1
+#define HAVE_ENCODER_LZMA1 1
+#define HAVE_ENCODER_LZMA2 1
+#define HAVE_ENCODER_POWERPC 1
+#define HAVE_ENCODER_SPARC 1
+#define HAVE_ENCODER_X86 1
+#define HAVE_FCNTL_H 1
+#define HAVE_FUTIMENS 1
+#define HAVE_GETOPT_H 1
+#define HAVE_GETOPT_LONG 1
+#define HAVE_GETTEXT 1
+#define HAVE_IMMINTRIN_H 1
+#define HAVE_INTTYPES_H 1
+#define HAVE_LIMITS_H 1
+#define HAVE_MBRTOWC 1
+#define HAVE_MEMORY_H 1
+#define HAVE_MF_BT2 1
+#define HAVE_MF_BT3 1
+#define HAVE_MF_BT4 1
+#define HAVE_MF_HC3 1
+#define HAVE_MF_HC4 1
+#define HAVE_OPTRESET 1
+#define HAVE_POSIX_FADVISE 1
+#define HAVE_PTHREAD_CONDATTR_SETCLOCK 1
+#define HAVE_PTHREAD_PRIO_INHERIT 1
+#define HAVE_STDBOOL_H 1
+#define HAVE_STDINT_H 1
+#define HAVE_STDLIB_H 1
+#define HAVE_STRINGS_H 1
+#define HAVE_STRING_H 1
+#define HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC 1
+#define HAVE_SYS_PARAM_H 1
+#define HAVE_SYS_STAT_H 1
+#define HAVE_SYS_TIME_H 1
+#define HAVE_SYS_TYPES_H 1
+#define HAVE_UINTPTR_T 1
+#define HAVE_UNISTD_H 1
+#define HAVE_VISIBILITY 1
+#define HAVE_WCWIDTH 1
+#define HAVE__BOOL 1
+#define HAVE___BUILTIN_ASSUME_ALIGNED 1
+#define HAVE___BUILTIN_BSWAPXX 1
+#define MYTHREAD_POSIX 1
+#define NDEBUG 1
+#define PACKAGE "xz"
+#define PACKAGE_BUGREPORT "lasse.collin@tukaani.org"
+#define PACKAGE_NAME "XZ Utils"
+#define PACKAGE_STRING "XZ Utils 5.4.0"
+#define PACKAGE_TARNAME "xz"
+#define PACKAGE_VERSION "5.4.0"
+#define SIZEOF_SIZE_T 4
+#define STDC_HEADERS 1
+#define TUKLIB_CPUCORES_SYSCONF 1
+#define TUKLIB_FAST_UNALIGNED_ACCESS 1
+#define TUKLIB_PHYSMEM_SYSCONF 1
+#ifndef _ALL_SOURCE
+# define _ALL_SOURCE 1
+#endif
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE 1
+#endif
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# define _POSIX_PTHREAD_SEMANTICS 1
+#endif
+#ifndef _TANDEM_SOURCE
+# define _TANDEM_SOURCE 1
+#endif
+#ifndef __EXTENSIONS__
+# define __EXTENSIONS__ 1
+#endif
+#define VERSION "5.4.0"
+'''
diff --git a/tools/settings.py b/tools/settings.py
index 10d6ca0..827e4a9 100644
--- a/tools/settings.py
+++ b/tools/settings.py
@@ -40,6 +40,7 @@ PORTS_SETTINGS = {
'USE_SDL_NET',
'USE_SDL_GFX',
'USE_LIBJPEG',
+ 'USE_LIBLZMA',
'USE_OGG',
'USE_REGAL',
'USE_BOOST_HEADERS',
--
2.34.1

@ -0,0 +1,139 @@
import os
import logging
VERSION = '5.4.6'
HASH = '495cc890d25c075c927c907b77e60d86dd8a4c377cea5b1172c8e916984149a7bb5fb32db25091f7219346b83155b47e4bc0404cc8529d992014cd7ed0c278b7'
URL = 'https://github.com/tukaani-project/xz'
DESCRIPTION = 'liblzma provides a general-purpose data-compression library.'
LICENSE = 'LGPL-2.1'
def get(ports, settings, shared):
ports.fetch_project('contrib.liblzma', f'https://github.com/tukaani-project/xz/releases/download/v{VERSION}/xz-{VERSION}.tar.xz', sha512hash=HASH)
def create(final):
logging.info('building port: contrib.liblzma')
ports.clear_project_build('contrib.liblzma')
source_path = os.path.join(ports.get_dir(), 'contrib.liblzma', f'xz-{VERSION}', 'src', 'liblzma')
ports.write_file(os.path.join(source_path, 'config.h'), config_h)
ports.install_headers(os.path.join(source_path, 'api'), pattern='lzma.h')
ports.install_headers(os.path.join(source_path, 'api', 'lzma'), pattern='*.h', target='lzma')
build_flags = ['-DHAVE_CONFIG_H', '-DTUKLIB_SYMBOL_PREFIX=lzma_', '-fvisibility=hidden']
exclude_files = ['crc32_small.c', 'crc64_small.c', 'crc32_tablegen.c', 'crc64_tablegen.c', 'price_tablegen.c', 'fastpos_tablegen.c',
'tuklib_exit.c', 'tuklib_mbstr_fw.c', 'tuklib_mbstr_width.c', 'tuklib_open_stdxxx.c', 'tuklib_progname.c']
include_dirs_rel = ['../common', 'api', 'check', 'common', 'delta', 'lz', 'lzma', 'rangecoder', 'simple']
include_dirs = [os.path.join(source_path, p) for p in include_dirs_rel]
ports.build_port(source_path, final, 'contrib.liblzma', flags=build_flags, exclude_files=exclude_files, includes=include_dirs)
return [shared.cache.get_lib('liblzma.a', create, what='port')]
def clear(ports, settings, shared):
shared.cache.erase_lib('liblzma.a')
def process_args(ports):
return []
config_h = '''
#define ASSUME_RAM 128
#define ENABLE_NLS 1
#define HAVE_CHECK_CRC32 1
#define HAVE_CHECK_CRC64 1
#define HAVE_CHECK_SHA256 1
#define HAVE_CLOCK_GETTIME 1
#define HAVE_DCGETTEXT 1
#define HAVE_DECL_CLOCK_MONOTONIC 1
#define HAVE_DECL_PROGRAM_INVOCATION_NAME 1
#define HAVE_DECODERS 1
#define HAVE_DECODER_ARM 1
#define HAVE_DECODER_ARMTHUMB 1
#define HAVE_DECODER_DELTA 1
#define HAVE_DECODER_IA64 1
#define HAVE_DECODER_LZMA1 1
#define HAVE_DECODER_LZMA2 1
#define HAVE_DECODER_POWERPC 1
#define HAVE_DECODER_SPARC 1
#define HAVE_DECODER_X86 1
#define HAVE_DLFCN_H 1
#define HAVE_ENCODERS 1
#define HAVE_ENCODER_ARM 1
#define HAVE_ENCODER_ARMTHUMB 1
#define HAVE_ENCODER_DELTA 1
#define HAVE_ENCODER_IA64 1
#define HAVE_ENCODER_LZMA1 1
#define HAVE_ENCODER_LZMA2 1
#define HAVE_ENCODER_POWERPC 1
#define HAVE_ENCODER_SPARC 1
#define HAVE_ENCODER_X86 1
#define HAVE_FCNTL_H 1
#define HAVE_FUTIMENS 1
#define HAVE_GETOPT_H 1
#define HAVE_GETOPT_LONG 1
#define HAVE_GETTEXT 1
#define HAVE_IMMINTRIN_H 1
#define HAVE_INTTYPES_H 1
#define HAVE_LIMITS_H 1
#define HAVE_MBRTOWC 1
#define HAVE_MEMORY_H 1
#define HAVE_MF_BT2 1
#define HAVE_MF_BT3 1
#define HAVE_MF_BT4 1
#define HAVE_MF_HC3 1
#define HAVE_MF_HC4 1
#define HAVE_OPTRESET 1
#define HAVE_POSIX_FADVISE 1
#define HAVE_PTHREAD_CONDATTR_SETCLOCK 1
#define HAVE_PTHREAD_PRIO_INHERIT 1
#define HAVE_STDBOOL_H 1
#define HAVE_STDINT_H 1
#define HAVE_STDLIB_H 1
#define HAVE_STRINGS_H 1
#define HAVE_STRING_H 1
#define HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC 1
#define HAVE_SYS_PARAM_H 1
#define HAVE_SYS_STAT_H 1
#define HAVE_SYS_TIME_H 1
#define HAVE_SYS_TYPES_H 1
#define HAVE_UINTPTR_T 1
#define HAVE_UNISTD_H 1
#define HAVE_VISIBILITY 1
#define HAVE_WCWIDTH 1
#define HAVE__BOOL 1
#define HAVE___BUILTIN_ASSUME_ALIGNED 1
#define HAVE___BUILTIN_BSWAPXX 1
#define MYTHREAD_POSIX 1
#define NDEBUG 1
#define PACKAGE "xz"
#define PACKAGE_BUGREPORT "lasse.collin@tukaani.org"
#define PACKAGE_NAME "XZ Utils"
#define PACKAGE_STRING "XZ Utils 5.4.0"
#define PACKAGE_TARNAME "xz"
#define PACKAGE_VERSION "5.4.0"
#define SIZEOF_SIZE_T 4
#define STDC_HEADERS 1
#define TUKLIB_CPUCORES_SYSCONF 1
#define TUKLIB_FAST_UNALIGNED_ACCESS 1
#define TUKLIB_PHYSMEM_SYSCONF 1
#ifndef _ALL_SOURCE
# define _ALL_SOURCE 1
#endif
#ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
#endif
#ifndef _POSIX_PTHREAD_SEMANTICS
# define _POSIX_PTHREAD_SEMANTICS 1
#endif
#ifndef _TANDEM_SOURCE
# define _TANDEM_SOURCE 1
#endif
#ifndef __EXTENSIONS__
# define __EXTENSIONS__ 1
#endif
#define VERSION "5.4.0"
'''

@ -1,18 +1,2 @@
@echo off
REM Signing script
REM Arguments: sign.bat exe_to_sign certificate_subject_name
REM This is a loose wrapper around the Microsoft signtool application (included in the Windows SDK).
REM See https://docs.microsoft.com/en-us/dotnet/framework/tools/signtool-exe for more details.
REM Path to signtool.exe
IF NOT DEFINED SIGNTOOL_PATH (SET SIGNTOOL_PATH=signtool)
REM URL of the timestamp server
IF NOT DEFINED SIGNTOOL_TIMESTAMP_URL (SET SIGNTOOL_TIMESTAMP_URL=http://timestamp.digicert.com)
REM Sign with SHA-1 for Windows 7 and below
"%SIGNTOOL_PATH%" sign -v -n %2 -t %SIGNTOOL_TIMESTAMP_URL% -fd sha1 %1
REM Sign with SHA-256 for Windows 8 and above
"%SIGNTOOL_PATH%" sign -v -n %2 -tr %SIGNTOOL_TIMESTAMP_URL% -fd sha256 -td sha256 -as %1
pwsh -File "%~dp0sign_azure.ps1" %1

@ -0,0 +1,40 @@
# Signing script for Azure Code Signing
# Arguments: sign_azure.ps1 path_to_sign
#
# Environment variables must be set up before use:
#
# AZURE_TENANT_ID
# AZURE_CLIENT_ID
# AZURE_CLIENT_SECRET
# AZURE_CODESIGN_ACCOUNT_NAME
# AZURE_CODESIGN_ENDPOINT
# AZURE_CODESIGN_PROFILE_NAME
Param
(
# Files folder
[Parameter(Mandatory=$true, Position=0)]
$FilesFolder
)
if (!$Env:AZURE_CODESIGN_ENDPOINT -or !$Env:AZURE_CODESIGN_ACCOUNT_NAME -or !$Env:AZURE_CODESIGN_PROFILE_NAME -or
!$Env:AZURE_TENANT_ID -or !$Env:AZURE_CLIENT_ID -or !$Env:AZURE_CLIENT_SECRET)
{
"Code signing variables not found; most likely running in a fork. Skipping signing."
exit
}
Install-Module -Name AzureCodeSigning -Scope CurrentUser -RequiredVersion 0.3.0 -Force -Repository PSGallery
$params = @{}
$params["Endpoint"] = $Env:AZURE_CODESIGN_ENDPOINT
$params["CodeSigningAccountName"] = $Env:AZURE_CODESIGN_ACCOUNT_NAME
$params["CertificateProfileName"] = $Env:AZURE_CODESIGN_PROFILE_NAME
$params["FilesFolder"] = $FilesFolder
$params["FilesFolderFilter"] = "exe"
$params["FileDigest"] = "SHA256"
$params["TimestampRfc3161"] = "http://timestamp.acs.microsoft.com"
$params["TimestampDigest"] = "SHA256"
Invoke-AzureCodeSigning @params

@ -7510,7 +7510,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
BuildRailDepot(): false
BuildRailDepot(): true
BuildRailDepot(): true
BuildRailDepot(): false
BuildRailDepot(): true
GetRailDepotFrontTile(): 33412
IsBuildable(): false
DepotList
@ -7604,12 +7604,12 @@ ERROR: IsEnd() is invalid as Begin() is never called
BuildRoadDepot(): false
BuildRoadDepot(): true
BuildRoadDepot(): true
BuildRoadDepot(): false
BuildRoadDepot(): true
HasRoadType(Road): true
HasRoadType(Tram): false
GetLastError(): 259
GetLastErrorString(): ERR_ALREADY_BUILT
GetErrorCategory(): 1
GetLastError(): 0
GetLastErrorString(): ERR_NONE
GetErrorCategory(): 0
IsRoadTile(): false
GetRoadDepotFrontTile(): 33412
IsRoadDepotTile(): true
@ -9471,7 +9471,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
IsStoppedInDepot(): false
--Accounting--
GetCosts(): -5947
Should be: -5946
Should be: -5947
GetName(): Road Vehicle #1
SetName(): true
GetName(): MyVehicleName
@ -9485,7 +9485,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
GetAgeLeft(): 5489
GetCurrentSpeed(): 7
GetRunningCost(): 421
GetProfitThisYear(): -1
GetProfitThisYear(): 0
GetProfitLastYear(): 0
GetCurrentValue(): 5947
GetVehicleType(): 1
@ -9604,7 +9604,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
16 => 0
14 => 0
13 => 0
12 => -1
12 => 0
ProfitLastYear ListDump:
17 => 0
16 => 0

@ -77,6 +77,7 @@ add_files(
bridge_map.cpp
bridge_map.h
build_vehicle_gui.cpp
cachecheck.cpp
cargo_type.h
cargoaction.cpp
cargoaction.h
@ -138,6 +139,10 @@ add_files(
dock_gui.cpp
driver.cpp
driver.h
dropdown.cpp
dropdown_common_type.h
dropdown_func.h
dropdown_type.h
economy.cpp
economy_base.h
economy_cmd.h
@ -250,6 +255,7 @@ add_files(
music_gui.cpp
newgrf.cpp
newgrf.h
newgrf_act5.h
newgrf_airport.cpp
newgrf_airport.h
newgrf_airporttiles.cpp
@ -393,6 +399,8 @@ add_files(
signs_func.h
signs_gui.cpp
signs_type.h
slider.cpp
slider_func.h
slope_func.h
slope_type.h
smallmap_gui.cpp
@ -468,7 +476,6 @@ add_files(
tilearea_type.h
tilehighlight_func.h
tilehighlight_type.h
tilematrix_type.hpp
timetable.h
timetable_cmd.cpp
timetable_cmd.h

@ -60,7 +60,7 @@
c->ai_info = info;
assert(c->ai_instance == nullptr);
c->ai_instance = new AIInstance();
c->ai_instance = std::make_unique<AIInstance>();
c->ai_instance->Initialize(info);
c->ai_instance->LoadOnStack(config->GetToLoadData());
config->SetToLoadData(nullptr);
@ -112,8 +112,7 @@
Backup<CompanyID> cur_company(_current_company, company);
Company *c = Company::Get(company);
delete c->ai_instance;
c->ai_instance = nullptr;
c->ai_instance.reset();
c->ai_info = nullptr;
c->ai_config.reset();

@ -128,20 +128,20 @@ struct AIConfigWindow : public Window {
}
}
void UpdateWidgetSize(WidgetID widget, Dimension *size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension *fill, [[maybe_unused]] Dimension *resize) override
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
{
switch (widget) {
case WID_AIC_DECREASE_NUMBER:
case WID_AIC_INCREASE_NUMBER:
case WID_AIC_DECREASE_INTERVAL:
case WID_AIC_INCREASE_INTERVAL:
*size = maxdim(*size, NWidgetScrollbar::GetHorizontalDimension());
size = maxdim(size, NWidgetScrollbar::GetHorizontalDimension());
break;
case WID_AIC_LIST:
this->line_height = GetCharacterHeight(FS_NORMAL) + padding.height;
resize->height = this->line_height;
size->height = 8 * this->line_height;
resize.height = this->line_height;
size.height = 8 * this->line_height;
break;
}
}

@ -111,6 +111,7 @@ struct Aircraft final : public SpecializedVehicle<Aircraft, VEH_AIRCRAFT> {
void OnNewEconomyDay() override;
uint Crash(bool flooded = false) override;
TileIndex GetOrderStationLocation(StationID station) override;
TileIndex GetCargoTile() const override { return this->First()->tile; }
ClosestDepot FindClosestDepot() override;
/**

@ -1182,10 +1182,13 @@ static bool HandleCrashedAircraft(Aircraft *v)
if (v->crashed_counter < 500 && st == nullptr && ((v->crashed_counter % 3) == 0) ) {
int z = GetSlopePixelZ(Clamp(v->x_pos, 0, Map::MaxX() * TILE_SIZE), Clamp(v->y_pos, 0, Map::MaxY() * TILE_SIZE));
v->z_pos -= 1;
if (v->z_pos == z) {
if (v->z_pos <= z) {
v->crashed_counter = 500;
v->z_pos++;
v->z_pos = z + 1;
} else {
v->crashed_counter = 0;
}
SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
}
if (v->crashed_counter < 650) {

@ -22,7 +22,8 @@
#include "station_type.h"
#include "newgrf_airport.h"
#include "newgrf_callbacks.h"
#include "widgets/dropdown_type.h"
#include "dropdown_type.h"
#include "dropdown_func.h"
#include "core/geometry_func.hpp"
#include "hotkeys.h"
#include "vehicle_func.h"
@ -240,8 +241,8 @@ class BuildAirportWindow : public PickerWindowBase {
{
DropDownList list;
for (uint i = 0; i < AirportClass::GetClassCount(); i++) {
list.push_back(std::make_unique<DropDownListStringItem>(AirportClass::Get((AirportClassID)i)->name, i, false));
for (const auto &cls : AirportClass::Classes()) {
list.push_back(MakeDropDownListStringItem(cls.name, cls.Index()));
}
return list;
@ -276,7 +277,7 @@ public:
const AirportSpec *as = ac->GetSpec(_selected_airport_index);
if (as->IsAvailable()) {
/* Ensure the airport layout is valid. */
_selected_airport_layout = Clamp(_selected_airport_layout, 0, as->num_table - 1);
_selected_airport_layout = Clamp(_selected_airport_layout, 0, static_cast<uint8_t>(as->layouts.size() - 1));
selectFirstAirport = false;
this->UpdateSelectSize();
}
@ -305,7 +306,7 @@ public:
StringID string = GetAirportTextCallback(as, _selected_airport_layout, CBID_AIRPORT_LAYOUT_NAME);
if (string != STR_UNDEFINED) {
SetDParam(0, string);
} else if (as->num_table > 1) {
} else if (as->layouts.size() > 1) {
SetDParam(0, STR_STATION_BUILD_AIRPORT_LAYOUT_NAME);
SetDParam(1, _selected_airport_layout + 1);
}
@ -316,17 +317,17 @@ public:
}
}
void UpdateWidgetSize(WidgetID widget, Dimension *size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension *fill, [[maybe_unused]] Dimension *resize) override
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
{
switch (widget) {
case WID_AP_CLASS_DROPDOWN: {
Dimension d = {0, 0};
for (uint i = 0; i < AirportClass::GetClassCount(); i++) {
d = maxdim(d, GetStringBoundingBox(AirportClass::Get((AirportClassID)i)->name));
for (const auto &cls : AirportClass::Classes()) {
d = maxdim(d, GetStringBoundingBox(cls.name));
}
d.width += padding.width;
d.height += padding.height;
*size = maxdim(*size, d);
size = maxdim(size, d);
break;
}
@ -335,11 +336,11 @@ public:
const AirportSpec *as = AirportSpec::Get(i);
if (!as->enabled) continue;
size->width = std::max(size->width, GetStringBoundingBox(as->name).width + padding.width);
size.width = std::max(size.width, GetStringBoundingBox(as->name).width + padding.width);
}
this->line_height = GetCharacterHeight(FS_NORMAL) + padding.height;
size->height = 5 * this->line_height;
size.height = 5 * this->line_height;
break;
}
@ -347,13 +348,13 @@ public:
for (int i = 0; i < NUM_AIRPORTS; i++) {
const AirportSpec *as = AirportSpec::Get(i);
if (!as->enabled) continue;
for (uint8_t layout = 0; layout < as->num_table; layout++) {
for (uint8_t layout = 0; layout < static_cast<uint8_t>(as->layouts.size()); layout++) {
SpriteID sprite = GetCustomAirportSprite(as, layout);
if (sprite != 0) {
Dimension d = GetSpriteSize(sprite);
d.width += WidgetDimensions::scaled.framerect.Horizontal();
d.height += WidgetDimensions::scaled.framerect.Vertical();
*size = maxdim(d, *size);
size = maxdim(d, size);
}
}
}
@ -363,12 +364,12 @@ public:
for (int i = NEW_AIRPORT_OFFSET; i < NUM_AIRPORTS; i++) {
const AirportSpec *as = AirportSpec::Get(i);
if (!as->enabled) continue;
for (uint8_t layout = 0; layout < as->num_table; layout++) {
for (uint8_t layout = 0; layout < static_cast<uint8_t>(as->layouts.size()); layout++) {
StringID string = GetAirportTextCallback(as, layout, CBID_AIRPORT_ADDITIONAL_TEXT);
if (string == STR_UNDEFINED) continue;
Dimension d = GetStringMultiLineBoundingBox(string, *size);
*size = maxdim(d, *size);
Dimension d = GetStringMultiLineBoundingBox(string, size);
size = maxdim(d, size);
}
}
break;
@ -473,14 +474,14 @@ public:
const AirportSpec *as = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index);
int w = as->size_x;
int h = as->size_y;
Direction rotation = as->rotation[_selected_airport_layout];
Direction rotation = as->layouts[_selected_airport_layout].rotation;
if (rotation == DIR_E || rotation == DIR_W) Swap(w, h);
SetTileSelectSize(w, h);
this->preview_sprite = GetCustomAirportSprite(as, _selected_airport_layout);
this->SetWidgetDisabledState(WID_AP_LAYOUT_DECREASE, _selected_airport_layout == 0);
this->SetWidgetDisabledState(WID_AP_LAYOUT_INCREASE, _selected_airport_layout + 1 >= as->num_table);
this->SetWidgetDisabledState(WID_AP_LAYOUT_INCREASE, _selected_airport_layout + 1U >= as->layouts.size());
int rad = _settings_game.station.modified_catchment ? as->catchment : (uint)CA_UNMODIFIED;
if (_settings_client.gui.station_show_coverage) SetTileSelectBigSize(-rad, -rad, 2 * rad, 2 * rad);
@ -495,8 +496,8 @@ public:
break;
case WID_AP_AIRPORT_LIST: {
int num_clicked = this->vscroll->GetScrolledRowFromWidget(pt.y, this, widget, 0, this->line_height);
if (num_clicked == INT_MAX) break;
int32_t num_clicked = this->vscroll->GetScrolledRowFromWidget(pt.y, this, widget, 0, this->line_height);
if (num_clicked == INT32_MAX) break;
const AirportSpec *as = AirportClass::Get(_selected_airport_class)->GetSpec(num_clicked);
if (as->IsAvailable()) this->SelectOtherAirport(num_clicked);
break;
@ -545,14 +546,12 @@ public:
if (change_class) {
/* If that fails, select the first available airport
* from the first class where airports are available. */
for (AirportClassID j = APC_BEGIN; j < APC_MAX; j++) {
AirportClass *apclass = AirportClass::Get(j);
for (uint i = 0; i < apclass->GetSpecCount(); i++) {
const AirportSpec *as = apclass->GetSpec(i);
for (const auto &cls : AirportClass::Classes()) {
for (const auto &as : cls.Specs()) {
if (as->IsAvailable()) {
_selected_airport_class = j;
this->vscroll->SetCount(apclass->GetSpecCount());
this->SelectOtherAirport(i);
_selected_airport_class = cls.Index();
this->vscroll->SetCount(cls.GetSpecCount());
this->SelectOtherAirport(as->GetIndex());
return;
}
}

@ -24,7 +24,8 @@
#include "core/geometry_func.hpp"
#include "rail_gui.h"
#include "road_gui.h"
#include "widgets/dropdown_func.h"
#include "dropdown_type.h"
#include "dropdown_func.h"
#include "autoreplace_cmd.h"
#include "group_cmd.h"
#include "settings_cmd.h"
@ -33,8 +34,6 @@
#include "safeguards.h"
void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_list, const Scrollbar &sb, EngineID selected_id, bool show_count, GroupID selected_group);
static bool EngineNumberSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
{
return Engine::Get(a.engine_id)->list_position < Engine::Get(b.engine_id)->list_position;
@ -72,7 +71,6 @@ void AddRemoveEngineFromAutoreplaceAndBuildWindows(VehicleType type)
static const StringID _start_replace_dropdown[] = {
STR_REPLACE_VEHICLES_NOW,
STR_REPLACE_VEHICLES_WHEN_OLD,
INVALID_STRING_ID
};
/**
@ -113,27 +111,6 @@ class ReplaceVehicleWindow : public Window {
return true;
}
void AddChildren(const GUIEngineList &source, GUIEngineList &target, EngineID parent, int indent, int side)
{
for (const auto &item : source) {
if (item.variant_id != parent || item.engine_id == parent) continue;
const Engine *e = Engine::Get(item.engine_id);
EngineDisplayFlags flags = item.flags;
if (e->display_last_variant != INVALID_ENGINE) flags &= ~EngineDisplayFlags::Shaded;
target.emplace_back(e->display_last_variant == INVALID_ENGINE ? item.engine_id : e->display_last_variant, item.engine_id, flags, indent);
/* Add variants if not folded */
if ((item.flags & (EngineDisplayFlags::HasVariants | EngineDisplayFlags::IsFolded)) == EngineDisplayFlags::HasVariants) {
/* Add this engine again as a child */
if ((item.flags & EngineDisplayFlags::Shaded) == EngineDisplayFlags::None) {
target.emplace_back(item.engine_id, item.engine_id, EngineDisplayFlags::None, indent + 1);
}
AddChildren(source, target, item.engine_id, indent + 1, side);
}
}
}
/**
* Generate an engines list
* @param draw_left true if generating the left list, otherwise false
@ -207,7 +184,7 @@ class ReplaceVehicleWindow : public Window {
this->engines[side].clear();
if (side == 1) {
AddChildren(list, this->engines[side], INVALID_ENGINE, 0, side);
GUIEngineListAddChildren(this->engines[side], list);
} else {
this->engines[side].swap(list);
}
@ -315,26 +292,26 @@ public:
this->sel_group = id_g;
}
void UpdateWidgetSize(WidgetID widget, Dimension *size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension *fill, [[maybe_unused]] Dimension *resize) override
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
{
switch (widget) {
case WID_RV_SORT_ASCENDING_DESCENDING: {
Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
d.height += padding.height;
*size = maxdim(*size, d);
size = maxdim(size, d);
break;
}
case WID_RV_LEFT_MATRIX:
case WID_RV_RIGHT_MATRIX:
resize->height = GetEngineListHeight((VehicleType)this->window_number);
size->height = (this->window_number <= VEH_ROAD ? 8 : 4) * resize->height;
resize.height = GetEngineListHeight((VehicleType)this->window_number);
size.height = (this->window_number <= VEH_ROAD ? 8 : 4) * resize.height;
break;
case WID_RV_LEFT_DETAILS:
case WID_RV_RIGHT_DETAILS:
size->height = GetCharacterHeight(FS_NORMAL) * this->details_height + padding.height;
size.height = GetCharacterHeight(FS_NORMAL) * this->details_height + padding.height;
break;
case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: {
@ -345,7 +322,7 @@ public:
d = maxdim(d, GetStringBoundingBox(str));
d.width += padding.width;
d.height += padding.height;
*size = maxdim(*size, d);
size = maxdim(size, d);
break;
}
@ -354,7 +331,7 @@ public:
d = maxdim(d, GetStringBoundingBox(STR_REPLACE_WAGONS));
d.width += padding.width;
d.height += padding.height;
*size = maxdim(*size, d);
size = maxdim(size, d);
break;
}
@ -363,7 +340,7 @@ public:
d = maxdim(d, GetStringBoundingBox(STR_REPLACE_NOT_REPLACING_VEHICLE_SELECTED));
d.width += padding.width;
d.height += padding.height;
*size = maxdim(*size, d);
size = maxdim(size, d);
break;
}
@ -374,7 +351,7 @@ public:
}
d.width += padding.width;
d.height += padding.height;
*size = maxdim(*size, d);
size = maxdim(size, d);
break;
}
@ -385,18 +362,16 @@ public:
}
d.width += padding.width;
d.height += padding.height;
*size = maxdim(*size, d);
size = maxdim(size, d);
break;
}
case WID_RV_START_REPLACE: {
Dimension d = GetStringBoundingBox(STR_REPLACE_VEHICLES_START);
for (int i = 0; _start_replace_dropdown[i] != INVALID_STRING_ID; i++) {
d = maxdim(d, GetStringBoundingBox(_start_replace_dropdown[i]));
}
d = maxdim(d, GetStringListBoundingBox(_start_replace_dropdown));
d.width += padding.width;
d.height += padding.height;
*size = maxdim(*size, d);
size = maxdim(size, d);
break;
}
}
@ -424,7 +399,7 @@ public:
break;
case WID_RV_SORT_DROPDOWN:
SetDParam(0, _engine_sort_listing[this->window_number][this->sort_criteria]);
SetDParam(0, std::data(_engine_sort_listing[this->window_number])[this->sort_criteria]);
break;
case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: {
@ -563,8 +538,8 @@ public:
case WID_RV_TRAIN_ENGINEWAGON_DROPDOWN: {
DropDownList list;
list.push_back(std::make_unique<DropDownListStringItem>(STR_REPLACE_ENGINES, 1, false));
list.push_back(std::make_unique<DropDownListStringItem>(STR_REPLACE_WAGONS, 0, false));
list.push_back(MakeDropDownListStringItem(STR_REPLACE_ENGINES, 1));
list.push_back(MakeDropDownListStringItem(STR_REPLACE_WAGONS, 0));
ShowDropDownList(this, std::move(list), this->replace_engines ? 1 : 0, WID_RV_TRAIN_ENGINEWAGON_DROPDOWN);
break;
}

@ -47,7 +47,7 @@ public:
void PaletteAnimate(const Palette &palette) override;
Blitter::PaletteAnimation UsePaletteAnimation() override;
const char *GetName() override { return "32bpp-anim"; }
std::string_view GetName() override { return "32bpp-anim"; }
void PostResize() override;
/**

@ -31,7 +31,7 @@
class Blitter_32bppSSE2_Anim : public Blitter_32bppAnim {
public:
void PaletteAnimate(const Palette &palette) override;
const char *GetName() override { return "32bpp-sse2-anim"; }
std::string_view GetName() override { return "32bpp-sse2-anim"; }
};
/** Factory for the partially 32bpp blitter with animation. */

@ -42,7 +42,7 @@ public:
Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, AllocatorProc *allocator) override {
return Blitter_32bppSSE_Base::Encode(sprite, allocator);
}
const char *GetName() override { return "32bpp-sse4-anim"; }
std::string_view GetName() override { return "32bpp-sse4-anim"; }
using Blitter_32bppSSE2_Anim::LookupColourInPalette;
};

@ -306,9 +306,9 @@ template <bool Tpal_to_rgb> Sprite *Blitter_32bppOptimized::EncodeInternal(const
ZoomLevel zoom_min;
ZoomLevel zoom_max;
if (sprite[ZOOM_LVL_NORMAL].type == SpriteType::Font) {
zoom_min = ZOOM_LVL_NORMAL;
zoom_max = ZOOM_LVL_NORMAL;
if (sprite[ZOOM_LVL_MIN].type == SpriteType::Font) {
zoom_min = ZOOM_LVL_MIN;
zoom_max = ZOOM_LVL_MIN;
} else {
zoom_min = _settings_client.gui.zoom_min;
zoom_max = _settings_client.gui.zoom_max;
@ -416,10 +416,10 @@ template <bool Tpal_to_rgb> Sprite *Blitter_32bppOptimized::EncodeInternal(const
Sprite *dest_sprite = (Sprite *)allocator(sizeof(*dest_sprite) + sizeof(SpriteData) + len);
dest_sprite->height = sprite[ZOOM_LVL_NORMAL].height;
dest_sprite->width = sprite[ZOOM_LVL_NORMAL].width;
dest_sprite->x_offs = sprite[ZOOM_LVL_NORMAL].x_offs;
dest_sprite->y_offs = sprite[ZOOM_LVL_NORMAL].y_offs;
dest_sprite->height = sprite[ZOOM_LVL_MIN].height;
dest_sprite->width = sprite[ZOOM_LVL_MIN].width;
dest_sprite->x_offs = sprite[ZOOM_LVL_MIN].x_offs;
dest_sprite->y_offs = sprite[ZOOM_LVL_MIN].y_offs;
SpriteData *dst = (SpriteData *)dest_sprite->data;
memset(dst, 0, sizeof(*dst));

@ -24,7 +24,7 @@ public:
void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override;
Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, AllocatorProc *allocator) override;
const char *GetName() override { return "32bpp-optimized"; }
std::string_view GetName() override { return "32bpp-optimized"; }
template <BlitterMode mode, bool Tpal_to_rgb = false> void Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom);

@ -118,17 +118,17 @@ void Blitter_32bppSimple::DrawColourMappingRect(void *dst, int width, int height
Sprite *Blitter_32bppSimple::Encode(const SpriteLoader::SpriteCollection &sprite, AllocatorProc *allocator)
{
Blitter_32bppSimple::Pixel *dst;
Sprite *dest_sprite = (Sprite *)allocator(sizeof(*dest_sprite) + (size_t)sprite[ZOOM_LVL_NORMAL].height * (size_t)sprite[ZOOM_LVL_NORMAL].width * sizeof(*dst));
Sprite *dest_sprite = static_cast<Sprite *>(allocator(sizeof(*dest_sprite) + static_cast<size_t>(sprite[ZOOM_LVL_MIN].height) * static_cast<size_t>(sprite[ZOOM_LVL_MIN].width) * sizeof(*dst)));
dest_sprite->height = sprite[ZOOM_LVL_NORMAL].height;
dest_sprite->width = sprite[ZOOM_LVL_NORMAL].width;
dest_sprite->x_offs = sprite[ZOOM_LVL_NORMAL].x_offs;
dest_sprite->y_offs = sprite[ZOOM_LVL_NORMAL].y_offs;
dest_sprite->height = sprite[ZOOM_LVL_MIN].height;
dest_sprite->width = sprite[ZOOM_LVL_MIN].width;
dest_sprite->x_offs = sprite[ZOOM_LVL_MIN].x_offs;
dest_sprite->y_offs = sprite[ZOOM_LVL_MIN].y_offs;
dst = (Blitter_32bppSimple::Pixel *)dest_sprite->data;
SpriteLoader::CommonPixel *src = (SpriteLoader::CommonPixel *)sprite[ZOOM_LVL_NORMAL].data;
SpriteLoader::CommonPixel *src = (SpriteLoader::CommonPixel *)sprite[ZOOM_LVL_MIN].data;
for (int i = 0; i < sprite[ZOOM_LVL_NORMAL].height * sprite[ZOOM_LVL_NORMAL].width; i++) {
for (int i = 0; i < sprite[ZOOM_LVL_MIN].height * sprite[ZOOM_LVL_MIN].width; i++) {
if (src->m == 0) {
dst[i].r = src->r;
dst[i].g = src->g;

@ -28,7 +28,7 @@ public:
void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal) override;
Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, AllocatorProc *allocator) override;
const char *GetName() override { return "32bpp-simple"; }
std::string_view GetName() override { return "32bpp-simple"; }
};
/** Factory for the simple 32 bpp blitter. */

@ -26,9 +26,9 @@ Sprite *Blitter_32bppSSE_Base::Encode(const SpriteLoader::SpriteCollection &spri
* Second uint32_t of a line = the number of transparent pixels from the right.
* Then all RGBA then all MV.
*/
ZoomLevel zoom_min = ZOOM_LVL_NORMAL;
ZoomLevel zoom_max = ZOOM_LVL_NORMAL;
if (sprite[ZOOM_LVL_NORMAL].type != SpriteType::Font) {
ZoomLevel zoom_min = ZOOM_LVL_MIN;
ZoomLevel zoom_max = ZOOM_LVL_MIN;
if (sprite[ZOOM_LVL_MIN].type != SpriteType::Font) {
zoom_min = _settings_client.gui.zoom_min;
zoom_max = _settings_client.gui.zoom_max;
if (zoom_max == zoom_min) zoom_max = ZOOM_LVL_MAX;
@ -52,10 +52,10 @@ Sprite *Blitter_32bppSSE_Base::Encode(const SpriteLoader::SpriteCollection &spri
}
Sprite *dst_sprite = (Sprite *) allocator(sizeof(Sprite) + sizeof(SpriteData) + all_sprites_size);
dst_sprite->height = sprite[ZOOM_LVL_NORMAL].height;
dst_sprite->width = sprite[ZOOM_LVL_NORMAL].width;
dst_sprite->x_offs = sprite[ZOOM_LVL_NORMAL].x_offs;
dst_sprite->y_offs = sprite[ZOOM_LVL_NORMAL].y_offs;
dst_sprite->height = sprite[ZOOM_LVL_MIN].height;
dst_sprite->width = sprite[ZOOM_LVL_MIN].width;
dst_sprite->x_offs = sprite[ZOOM_LVL_MIN].x_offs;
dst_sprite->y_offs = sprite[ZOOM_LVL_MIN].y_offs;
memcpy(dst_sprite->data, &sd, sizeof(SpriteData));
/* Copy colours and determine flags. */

@ -92,7 +92,7 @@ public:
return Blitter_32bppSSE_Base::Encode(sprite, allocator);
}
const char *GetName() override { return "32bpp-sse2"; }
std::string_view GetName() override { return "32bpp-sse2"; }
};
/** Factory for the SSE2 32 bpp blitter (without palette animation). */

@ -32,7 +32,7 @@ public:
void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override;
template <BlitterMode mode, Blitter_32bppSSE_Base::ReadMode read_mode, Blitter_32bppSSE_Base::BlockType bt_last, bool translucent>
void Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom);
const char *GetName() override { return "32bpp-sse4"; }
std::string_view GetName() override { return "32bpp-sse4"; }
};
/** Factory for the SSE4 32 bpp blitter (without palette animation). */

@ -28,7 +28,7 @@
#endif
#define META_LENGTH 2 ///< Number of uint32_t inserted before each line of pixels in a sprite.
#define MARGIN_NORMAL_THRESHOLD (zoom == ZOOM_LVL_OUT_32X ? 8 : 4) ///< Minimum width to use margins with BM_NORMAL.
#define MARGIN_NORMAL_THRESHOLD (zoom == ZOOM_LVL_OUT_8X ? 8 : 4) ///< Minimum width to use margins with BM_NORMAL.
#define MARGIN_REMAP_THRESHOLD 4 ///< Minimum width to use margins with BM_COLOUR_REMAP.
#undef ALIGN

@ -32,7 +32,7 @@ public:
void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override;
template <BlitterMode mode, Blitter_32bppSSE_Base::ReadMode read_mode, Blitter_32bppSSE_Base::BlockType bt_last, bool translucent>
void Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom);
const char *GetName() override { return "32bpp-ssse3"; }
std::string_view GetName() override { return "32bpp-ssse3"; }
};
/** Factory for the SSSE3 32 bpp blitter (without palette animation). */

@ -32,7 +32,7 @@ public:
Blitter::PaletteAnimation UsePaletteAnimation() override;
bool NeedsAnimationBuffer() override;
const char *GetName() override { return "40bpp-anim"; }
std::string_view GetName() override { return "40bpp-anim"; }
template <BlitterMode mode> void Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom);

@ -128,9 +128,9 @@ Sprite *Blitter_8bppOptimized::Encode(const SpriteLoader::SpriteCollection &spri
ZoomLevel zoom_min;
ZoomLevel zoom_max;
if (sprite[ZOOM_LVL_NORMAL].type == SpriteType::Font) {
zoom_min = ZOOM_LVL_NORMAL;
zoom_max = ZOOM_LVL_NORMAL;
if (sprite[ZOOM_LVL_MIN].type == SpriteType::Font) {
zoom_min = ZOOM_LVL_MIN;
zoom_max = ZOOM_LVL_MIN;
} else {
zoom_min = _settings_client.gui.zoom_min;
zoom_max = _settings_client.gui.zoom_max;
@ -221,10 +221,10 @@ Sprite *Blitter_8bppOptimized::Encode(const SpriteLoader::SpriteCollection &spri
/* Allocate the exact amount of memory we need */
Sprite *dest_sprite = (Sprite *)allocator(sizeof(*dest_sprite) + size);
dest_sprite->height = sprite[ZOOM_LVL_NORMAL].height;
dest_sprite->width = sprite[ZOOM_LVL_NORMAL].width;
dest_sprite->x_offs = sprite[ZOOM_LVL_NORMAL].x_offs;
dest_sprite->y_offs = sprite[ZOOM_LVL_NORMAL].y_offs;
dest_sprite->height = sprite[ZOOM_LVL_MIN].height;
dest_sprite->width = sprite[ZOOM_LVL_MIN].width;
dest_sprite->x_offs = sprite[ZOOM_LVL_MIN].x_offs;
dest_sprite->y_offs = sprite[ZOOM_LVL_MIN].y_offs;
memcpy(dest_sprite->data, temp_dst, size);
return dest_sprite;

@ -25,7 +25,7 @@ public:
void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override;
Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, AllocatorProc *allocator) override;
const char *GetName() override { return "8bpp-optimized"; }
std::string_view GetName() override { return "8bpp-optimized"; }
};
/** Factory for the 8bpp blitter optimised for speed. */

@ -64,16 +64,16 @@ void Blitter_8bppSimple::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Zoom
Sprite *Blitter_8bppSimple::Encode(const SpriteLoader::SpriteCollection &sprite, AllocatorProc *allocator)
{
Sprite *dest_sprite;
dest_sprite = (Sprite *)allocator(sizeof(*dest_sprite) + (size_t)sprite[ZOOM_LVL_NORMAL].height * (size_t)sprite[ZOOM_LVL_NORMAL].width);
dest_sprite = static_cast<Sprite *>(allocator(sizeof(*dest_sprite) + static_cast<size_t>(sprite[ZOOM_LVL_MIN].height) * static_cast<size_t>(sprite[ZOOM_LVL_MIN].width)));
dest_sprite->height = sprite[ZOOM_LVL_NORMAL].height;
dest_sprite->width = sprite[ZOOM_LVL_NORMAL].width;
dest_sprite->x_offs = sprite[ZOOM_LVL_NORMAL].x_offs;
dest_sprite->y_offs = sprite[ZOOM_LVL_NORMAL].y_offs;
dest_sprite->height = sprite[ZOOM_LVL_MIN].height;
dest_sprite->width = sprite[ZOOM_LVL_MIN].width;
dest_sprite->x_offs = sprite[ZOOM_LVL_MIN].x_offs;
dest_sprite->y_offs = sprite[ZOOM_LVL_MIN].y_offs;
/* Copy over only the 'remap' channel, as that is what we care about in 8bpp */
for (int i = 0; i < sprite[ZOOM_LVL_NORMAL].height * sprite[ZOOM_LVL_NORMAL].width; i++) {
dest_sprite->data[i] = sprite[ZOOM_LVL_NORMAL].data[i].m;
for (int i = 0; i < sprite[ZOOM_LVL_MIN].height * sprite[ZOOM_LVL_MIN].width; i++) {
dest_sprite->data[i] = sprite[ZOOM_LVL_MIN].data[i].m;
}
return dest_sprite;

@ -19,7 +19,7 @@ public:
void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override;
Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, AllocatorProc *allocator) override;
const char *GetName() override { return "8bpp-simple"; }
std::string_view GetName() override { return "8bpp-simple"; }
};
/** Factory for the most trivial 8bpp blitter. */

@ -197,7 +197,7 @@ public:
/**
* Get the name of the blitter, the same as the Factory-instance returns.
*/
virtual const char *GetName() = 0;
virtual std::string_view GetName() = 0;
/**
* Post resize event

@ -93,7 +93,7 @@ public:
* @param name the blitter to select.
* @post Sets the blitter so GetCurrentBlitter() returns it too.
*/
static Blitter *SelectBlitter(const std::string &name)
static Blitter *SelectBlitter(const std::string_view name)
{
BlitterFactory *b = GetBlitterFactory(name);
if (b == nullptr) return nullptr;
@ -111,17 +111,17 @@ public:
* @param name the blitter factory to select.
* @return The blitter factory, or nullptr when there isn't one with the wanted name.
*/
static BlitterFactory *GetBlitterFactory(const std::string &name)
static BlitterFactory *GetBlitterFactory(const std::string_view name)
{
#if defined(DEDICATED)
const char *default_blitter = "null";
const std::string_view default_blitter = "null";
#elif defined(WITH_COCOA)
const char *default_blitter = "32bpp-anim";
const std::string_view default_blitter = "32bpp-anim";
#else
const char *default_blitter = "8bpp-optimized";
const std::string_view default_blitter = "8bpp-optimized";
#endif
if (GetBlitters().empty()) return nullptr;
const char *bname = name.empty() ? default_blitter : name.c_str();
const std::string_view bname = name.empty() ? default_blitter : name;
for (auto &it : GetBlitters()) {
BlitterFactory *b = it.second;
@ -159,7 +159,7 @@ public:
/**
* Get the long, human readable, name for the Blitter-class.
*/
const std::string &GetName() const
std::string_view GetName() const
{
return this->name;
}
@ -167,7 +167,7 @@ public:
/**
* Get a nice description of the blitter-class.
*/
const std::string &GetDescription() const
std::string_view GetDescription() const
{
return this->description;
}

@ -20,10 +20,10 @@ Sprite *Blitter_Null::Encode(const SpriteLoader::SpriteCollection &sprite, Alloc
Sprite *dest_sprite;
dest_sprite = (Sprite *)allocator(sizeof(*dest_sprite));
dest_sprite->height = sprite[ZOOM_LVL_NORMAL].height;
dest_sprite->width = sprite[ZOOM_LVL_NORMAL].width;
dest_sprite->x_offs = sprite[ZOOM_LVL_NORMAL].x_offs;
dest_sprite->y_offs = sprite[ZOOM_LVL_NORMAL].y_offs;
dest_sprite->height = sprite[ZOOM_LVL_MIN].height;
dest_sprite->width = sprite[ZOOM_LVL_MIN].width;
dest_sprite->x_offs = sprite[ZOOM_LVL_MIN].x_offs;
dest_sprite->y_offs = sprite[ZOOM_LVL_MIN].y_offs;
return dest_sprite;
}

@ -31,7 +31,7 @@ public:
void PaletteAnimate(const Palette &) override { };
Blitter::PaletteAnimation UsePaletteAnimation() override { return Blitter::PALETTE_ANIMATION_NONE; };
const char *GetName() override { return "null"; }
std::string_view GetName() override { return "null"; }
};
/** Factory for the blitter that does nothing. */

@ -97,12 +97,12 @@ public:
this->Window::Close();
}
void UpdateWidgetSize(WidgetID widget, Dimension *size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension *fill, [[maybe_unused]] Dimension *resize) override
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
{
if (widget == WID_BEM_MESSAGE) {
*size = GetStringBoundingBox(STR_MISSING_GRAPHICS_ERROR);
size->width += WidgetDimensions::scaled.frametext.Horizontal();
size->height += WidgetDimensions::scaled.frametext.Vertical();
size = GetStringBoundingBox(STR_MISSING_GRAPHICS_ERROR);
size.width += WidgetDimensions::scaled.frametext.Horizontal();
size.height += WidgetDimensions::scaled.frametext.Vertical();
}
}
@ -211,7 +211,7 @@ public:
this->Window::Close();
}
void UpdateWidgetSize(WidgetID widget, Dimension *size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension *fill, [[maybe_unused]] Dimension *resize) override
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
{
/* We cache the button size. This is safe as no reinit can happen here. */
if (this->button_size.width == 0) {
@ -223,13 +223,13 @@ public:
switch (widget) {
case WID_BAFD_QUESTION:
/* The question is twice as wide as the buttons, and determine the height based on the width. */
size->width = this->button_size.width * 2;
size->height = GetStringHeight(STR_MISSING_GRAPHICS_SET_MESSAGE, size->width - WidgetDimensions::scaled.frametext.Horizontal()) + WidgetDimensions::scaled.frametext.Vertical();
size.width = this->button_size.width * 2;
size.height = GetStringHeight(STR_MISSING_GRAPHICS_SET_MESSAGE, size.width - WidgetDimensions::scaled.frametext.Horizontal()) + WidgetDimensions::scaled.frametext.Vertical();
break;
case WID_BAFD_YES:
case WID_BAFD_NO:
*size = this->button_size;
size = this->button_size;
break;
}
}
@ -291,10 +291,10 @@ public:
# include "video/video_driver.hpp"
class BootstrapEmscripten : public ContentCallback {
bool downloading{false};
uint total_files{0};
uint total_bytes{0};
uint downloaded_bytes{0};
bool downloading = false;
uint total_files = 0;
uint total_bytes = 0;
uint downloaded_bytes = 0;
public:
BootstrapEmscripten()

@ -18,7 +18,7 @@
#include "gfx_func.h"
#include "tunnelbridge.h"
#include "sortlist_type.h"
#include "widgets/dropdown_func.h"
#include "dropdown_func.h"
#include "core/geometry_func.hpp"
#include "tunnelbridge_map.h"
#include "road_gui.h"
@ -75,8 +75,12 @@ private:
static Listing last_sorting; ///< Last setting of the sort.
/* Constants for sorting the bridges */
static const StringID sorter_names[];
static GUIBridgeList::SortFunction * const sorter_funcs[];
static inline const StringID sorter_names[] = {
STR_SORT_BY_NUMBER,
STR_SORT_BY_COST,
STR_SORT_BY_MAX_SPEED,
};
static const std::initializer_list<GUIBridgeList::SortFunction * const> sorter_funcs;
/* Internal variables */
TileIndex start_tile;
@ -122,7 +126,7 @@ private:
this->bridges.Sort();
/* Display the current sort variant */
this->GetWidget<NWidgetCore>(WID_BBS_DROPDOWN_CRITERIA)->widget_data = this->sorter_names[this->bridges.SortType()];
this->GetWidget<NWidgetCore>(WID_BBS_DROPDOWN_CRITERIA)->widget_data = BuildBridgeWindow::sorter_names[this->bridges.SortType()];
/* Set the modified widgets dirty */
this->SetWidgetDirty(WID_BBS_DROPDOWN_CRITERIA);
@ -161,8 +165,8 @@ public:
this->FinishInitNested(transport_type); // Initializes 'this->icon_width'.
this->parent = FindWindowById(WC_BUILD_TOOLBAR, transport_type);
this->bridges.SetListing(this->last_sorting);
this->bridges.SetSortFuncs(this->sorter_funcs);
this->bridges.SetListing(BuildBridgeWindow::last_sorting);
this->bridges.SetSortFuncs(BuildBridgeWindow::sorter_funcs);
this->bridges.NeedResort();
this->SortBridgeList();
@ -171,27 +175,24 @@ public:
~BuildBridgeWindow()
{
this->last_sorting = this->bridges.GetListing();
BuildBridgeWindow::last_sorting = this->bridges.GetListing();
}
void UpdateWidgetSize(WidgetID widget, Dimension *size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension *fill, [[maybe_unused]] Dimension *resize) override
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
{
switch (widget) {
case WID_BBS_DROPDOWN_ORDER: {
Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
d.height += padding.height;
*size = maxdim(*size, d);
size = maxdim(size, d);
break;
}
case WID_BBS_DROPDOWN_CRITERIA: {
Dimension d = {0, 0};
for (const StringID *str = this->sorter_names; *str != INVALID_STRING_ID; str++) {
d = maxdim(d, GetStringBoundingBox(*str));
}
Dimension d = GetStringListBoundingBox(BuildBridgeWindow::sorter_names);
d.width += padding.width;
d.height += padding.height;
*size = maxdim(*size, d);
size = maxdim(size, d);
break;
}
case WID_BBS_BRIDGE_LIST: {
@ -201,11 +202,11 @@ public:
sprite_dim = maxdim(sprite_dim, GetScaledSpriteSize(bridge_data.spec->sprite));
text_dim = maxdim(text_dim, GetStringBoundingBox(GetBridgeSelectString(bridge_data)));
}
resize->height = std::max(sprite_dim.height, text_dim.height) + padding.height; // Max of both sizes + account for matrix edges.
resize.height = std::max(sprite_dim.height, text_dim.height) + padding.height; // Max of both sizes + account for matrix edges.
this->icon_width = sprite_dim.width; // Width of bridge icon.
size->width = this->icon_width + WidgetDimensions::scaled.hsep_normal + text_dim.width + padding.width;
size->height = 4 * resize->height; // Smallest bridge gui is 4 entries high in the matrix.
size.width = this->icon_width + WidgetDimensions::scaled.hsep_normal + text_dim.width + padding.width;
size.height = 4 * resize.height; // Smallest bridge gui is 4 entries high in the matrix.
break;
}
}
@ -274,7 +275,7 @@ public:
break;
case WID_BBS_DROPDOWN_CRITERIA:
ShowDropDownMenu(this, this->sorter_names, this->bridges.SortType(), WID_BBS_DROPDOWN_CRITERIA, 0, 0);
ShowDropDownMenu(this, BuildBridgeWindow::sorter_names, this->bridges.SortType(), WID_BBS_DROPDOWN_CRITERIA, 0, 0);
break;
}
}
@ -298,20 +299,12 @@ public:
Listing BuildBridgeWindow::last_sorting = {true, 2};
/** Available bridge sorting functions. */
GUIBridgeList::SortFunction * const BuildBridgeWindow::sorter_funcs[] = {
const std::initializer_list<GUIBridgeList::SortFunction * const> BuildBridgeWindow::sorter_funcs = {
&BridgeIndexSorter,
&BridgePriceSorter,
&BridgeSpeedSorter
};
/** Names of the sorting functions. */
const StringID BuildBridgeWindow::sorter_names[] = {
STR_SORT_BY_NUMBER,
STR_SORT_BY_COST,
STR_SORT_BY_MAX_SPEED,
INVALID_STRING_ID
};
/** Widgets of the bridge gui. */
static constexpr NWidgetPart _nested_build_bridge_widgets[] = {
/* Header */

@ -25,7 +25,8 @@
#include "window_func.h"
#include "timer/timer_game_calendar.h"
#include "vehicle_func.h"
#include "widgets/dropdown_func.h"
#include "dropdown_type.h"
#include "dropdown_func.h"
#include "engine_gui.h"
#include "cargotype.h"
#include "core/geometry_func.hpp"
@ -483,7 +484,7 @@ EngList_SortTypeFunction * const _engine_sort_functions[][11] = {{
}};
/** Dropdown menu strings for the vehicle sort criteria. */
const StringID _engine_sort_listing[][12] = {{
const std::initializer_list<const StringID> _engine_sort_listing[] = {{
/* Trains */
STR_SORT_BY_ENGINE_ID,
STR_SORT_BY_COST,
@ -496,7 +497,6 @@ const StringID _engine_sort_listing[][12] = {{
STR_SORT_BY_POWER_VS_RUNNING_COST,
STR_SORT_BY_RELIABILITY,
STR_SORT_BY_CARGO_CAPACITY,
INVALID_STRING_ID
}, {
/* Road vehicles */
STR_SORT_BY_ENGINE_ID,
@ -510,7 +510,6 @@ const StringID _engine_sort_listing[][12] = {{
STR_SORT_BY_POWER_VS_RUNNING_COST,
STR_SORT_BY_RELIABILITY,
STR_SORT_BY_CARGO_CAPACITY,
INVALID_STRING_ID
}, {
/* Ships */
STR_SORT_BY_ENGINE_ID,
@ -521,7 +520,6 @@ const StringID _engine_sort_listing[][12] = {{
STR_SORT_BY_RUNNING_COST,
STR_SORT_BY_RELIABILITY,
STR_SORT_BY_CARGO_CAPACITY,
INVALID_STRING_ID
}, {
/* Aircraft */
STR_SORT_BY_ENGINE_ID,
@ -533,11 +531,10 @@ const StringID _engine_sort_listing[][12] = {{
STR_SORT_BY_RELIABILITY,
STR_SORT_BY_CARGO_CAPACITY,
STR_SORT_BY_RANGE,
INVALID_STRING_ID
}};
/** Filters vehicles by cargo and engine (in case of rail vehicle). */
static bool CDECL CargoAndEngineFilter(const GUIEngineListItem *item, const CargoID cid)
static bool CargoAndEngineFilter(const GUIEngineListItem *item, const CargoID cid)
{
if (cid == CargoFilterCriteria::CF_ANY) {
return true;
@ -549,7 +546,7 @@ static bool CDECL CargoAndEngineFilter(const GUIEngineListItem *item, const Carg
}
}
static GUIEngineList::FilterFunction * const _filter_funcs[] = {
static GUIEngineList::FilterFunction * const _engine_filter_funcs[] = {
&CargoAndEngineFilter,
};
@ -1044,6 +1041,9 @@ void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_li
int small_text_y_offset = ir.Height() - GetCharacterHeight(FS_SMALL);
int replace_icon_y_offset = (ir.Height() - replace_icon.height) / 2;
const int offset = (rtl ? -circle_width : circle_width) / 2;
const int level_width = rtl ? -WidgetDimensions::scaled.hsep_indent : WidgetDimensions::scaled.hsep_indent;
int y = ir.top;
for (auto it = first; it != last; ++it) {
const auto &item = *it;
@ -1051,6 +1051,21 @@ void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_li
bool has_variants = (item.flags & EngineDisplayFlags::HasVariants) != EngineDisplayFlags::None;
bool is_folded = (item.flags & EngineDisplayFlags::IsFolded) != EngineDisplayFlags::None;
bool shaded = (item.flags & EngineDisplayFlags::Shaded) != EngineDisplayFlags::None;
if (item.indent > 0) {
/* Draw tree continuation lines. */
int tx = (rtl ? ir.right : ir.left) + offset;
int ty = y - WidgetDimensions::scaled.matrix.top;
for (uint lvl = 1; lvl <= item.indent; ++lvl) {
if (HasBit(item.level_mask, lvl)) GfxDrawLine(tx, ty, tx, ty + step_size - 1, linecolour, WidgetDimensions::scaled.fullbevel.top);
if (lvl < item.indent) tx += level_width;
}
/* Draw our node in the tree. */
int ycentre = y + normal_text_y_offset + GetCharacterHeight(FS_NORMAL) / 2 - 1;
if (!HasBit(item.level_mask, item.indent)) GfxDrawLine(tx, ty, tx, ycentre, linecolour, WidgetDimensions::scaled.fullbevel.top);
GfxDrawLine(tx, ycentre, tx + offset - (rtl ? -1 : 1), ycentre, linecolour, WidgetDimensions::scaled.fullbevel.top);
}
/* Note: num_engines is only used in the autoreplace GUI, so it is correct to use _local_company here. */
const uint num_engines = GetGroupNumEngines(_local_company, selected_group, item.engine_id);
@ -1078,14 +1093,6 @@ void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_li
Rect fr = ir.Indent(indent, rtl).WithWidth(circle_width, rtl);
DrawSpriteIgnorePadding(is_folded ? SPR_CIRCLE_FOLDED : SPR_CIRCLE_UNFOLDED, PAL_NONE, {fr.left, y, fr.right, y + ir.Height() - 1}, SA_CENTER);
}
if (indent > 0) {
/* Draw tree lines */
Rect fr = ir.Indent(indent - WidgetDimensions::scaled.hsep_indent, rtl).WithWidth(circle_width, rtl);
int ycenter = y + normal_text_y_offset + GetCharacterHeight(FS_NORMAL) / 2;
bool continues = std::next(it) != std::end(eng_list) && std::next(it)->indent == item.indent;
GfxDrawLine(fr.left + circle_width / 2, y - WidgetDimensions::scaled.matrix.top, fr.left + circle_width / 2, continues ? y - WidgetDimensions::scaled.matrix.top + step_size - 1 : ycenter, linecolour, WidgetDimensions::scaled.fullbevel.top);
GfxDrawLine(fr.left + circle_width / 2, ycenter, fr.right, ycenter, linecolour, WidgetDimensions::scaled.fullbevel.top);
}
y += step_size;
}
}
@ -1113,6 +1120,44 @@ void DisplayVehicleSortDropDown(Window *w, VehicleType vehicle_type, int selecte
ShowDropDownMenu(w, _engine_sort_listing[vehicle_type], selected, button, 0, hidden_mask);
}
/**
* Add children to GUI engine list to build a hierarchical tree.
* @param dst Destination list.
* @param src Source list.
* @param parent Current tree parent (set by self with recursion).
* @param indent Current tree indentation level (set by self with recursion).
*/
void GUIEngineListAddChildren(GUIEngineList &dst, const GUIEngineList &src, EngineID parent, uint8_t indent)
{
for (const auto &item : src) {
if (item.variant_id != parent || item.engine_id == parent) continue;
const Engine *e = Engine::Get(item.engine_id);
EngineDisplayFlags flags = item.flags;
if (e->display_last_variant != INVALID_ENGINE) flags &= ~EngineDisplayFlags::Shaded;
dst.emplace_back(e->display_last_variant == INVALID_ENGINE ? item.engine_id : e->display_last_variant, item.engine_id, flags, indent);
/* Add variants if not folded */
if ((item.flags & (EngineDisplayFlags::HasVariants | EngineDisplayFlags::IsFolded)) == EngineDisplayFlags::HasVariants) {
/* Add this engine again as a child */
if ((item.flags & EngineDisplayFlags::Shaded) == EngineDisplayFlags::None) {
dst.emplace_back(item.engine_id, item.engine_id, EngineDisplayFlags::None, indent + 1);
}
GUIEngineListAddChildren(dst, src, item.engine_id, indent + 1);
}
}
if (indent > 0 || dst.empty()) return;
/* Hierarchy is complete, traverse in reverse to find where indentation levels continue. */
uint16_t level_mask = 0;
for (auto it = std::rbegin(dst); std::next(it) != std::rend(dst); ++it) {
auto next_it = std::next(it);
SB(level_mask, it->indent, 1, it->indent <= next_it->indent);
next_it->level_mask = level_mask;
}
}
/** Enum referring to the Hotkeys in the build vehicle window */
enum BuildVehicleHotkeys {
BVHK_FOCUS_FILTER_BOX, ///< Focus the edit box for editing the filter string
@ -1156,27 +1201,6 @@ struct BuildVehicleWindow : Window {
}
}
void AddChildren(const GUIEngineList &source, EngineID parent, int indent)
{
for (const auto &item : source) {
if (item.variant_id != parent || item.engine_id == parent) continue;
const Engine *e = Engine::Get(item.engine_id);
EngineDisplayFlags flags = item.flags;
if (e->display_last_variant != INVALID_ENGINE) flags &= ~EngineDisplayFlags::Shaded;
this->eng_list.emplace_back(e->display_last_variant == INVALID_ENGINE ? item.engine_id : e->display_last_variant, item.engine_id, flags, indent);
/* Add variants if not folded */
if ((item.flags & (EngineDisplayFlags::HasVariants | EngineDisplayFlags::IsFolded)) == EngineDisplayFlags::HasVariants) {
/* Add this engine again as a child */
if ((item.flags & EngineDisplayFlags::Shaded) == EngineDisplayFlags::None) {
this->eng_list.emplace_back(item.engine_id, item.engine_id, EngineDisplayFlags::None, indent + 1);
}
AddChildren(source, item.engine_id, indent + 1);
}
}
}
BuildVehicleWindow(WindowDesc *desc, TileIndex tile, VehicleType type) : Window(desc), vehicle_editbox(MAX_LENGTH_VEHICLE_NAME_CHARS * MAX_CHAR_LENGTH, MAX_LENGTH_VEHICLE_NAME_CHARS)
{
this->vehicle_type = type;
@ -1232,7 +1256,7 @@ struct BuildVehicleWindow : Window {
/* Select the first unshaded engine in the list as default when opening the window */
EngineID engine = INVALID_ENGINE;
auto it = std::find_if(this->eng_list.begin(), this->eng_list.end(), [&](GUIEngineListItem &item){ return (item.flags & EngineDisplayFlags::Shaded) == EngineDisplayFlags::None; });
auto it = std::find_if(this->eng_list.begin(), this->eng_list.end(), [&](GUIEngineListItem &item) { return (item.flags & EngineDisplayFlags::Shaded) == EngineDisplayFlags::None; });
if (it != this->eng_list.end()) engine = it->engine_id;
this->SelectEngine(engine);
}
@ -1284,7 +1308,7 @@ struct BuildVehicleWindow : Window {
this->cargo_filter_criteria = _engine_sort_last_cargo_criteria[this->vehicle_type];
if (this->cargo_filter_criteria < NUM_CARGO && !HasBit(_standard_cargo_mask, this->cargo_filter_criteria)) this->cargo_filter_criteria = CargoFilterCriteria::CF_ANY;
this->eng_list.SetFilterFuncs(_filter_funcs);
this->eng_list.SetFilterFuncs(_engine_filter_funcs);
this->eng_list.SetFilterState(this->cargo_filter_criteria != CargoFilterCriteria::CF_ANY);
}
@ -1517,8 +1541,7 @@ struct BuildVehicleWindow : Window {
default: NOT_REACHED();
case VEH_TRAIN:
this->GenerateBuildTrainList(list);
AddChildren(list, INVALID_ENGINE, 0);
this->eng_list.shrink_to_fit();
GUIEngineListAddChildren(this->eng_list, list);
this->eng_list.RebuildDone();
return;
case VEH_ROAD:
@ -1555,8 +1578,7 @@ struct BuildVehicleWindow : Window {
EngList_Sort(this->eng_list, _engine_sort_functions[this->vehicle_type][this->sort_criteria]);
this->eng_list.swap(list);
AddChildren(list, INVALID_ENGINE, 0);
this->eng_list.shrink_to_fit();
GUIEngineListAddChildren(this->eng_list, list, INVALID_ENGINE, 0);
this->eng_list.RebuildDone();
}
@ -1565,20 +1587,20 @@ struct BuildVehicleWindow : Window {
DropDownList list;
/* Add item for disabling filtering. */
list.push_back(std::make_unique<DropDownListStringItem>(this->GetCargoFilterLabel(CargoFilterCriteria::CF_ANY), CargoFilterCriteria::CF_ANY, false));
list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_ANY), CargoFilterCriteria::CF_ANY));
/* Specific filters for trains. */
if (this->vehicle_type == VEH_TRAIN) {
/* Add item for locomotives only in case of trains. */
list.push_back(std::make_unique<DropDownListStringItem>(this->GetCargoFilterLabel(CargoFilterCriteria::CF_ENGINES), CargoFilterCriteria::CF_ENGINES, false));
list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_ENGINES), CargoFilterCriteria::CF_ENGINES));
/* Add item for vehicles not carrying anything, e.g. train engines.
* This could also be useful for eyecandy vehicles of other types, but is likely too confusing for joe, */
list.push_back(std::make_unique<DropDownListStringItem>(this->GetCargoFilterLabel(CargoFilterCriteria::CF_NONE), CargoFilterCriteria::CF_NONE, false));
list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_NONE), CargoFilterCriteria::CF_NONE));
}
/* Add cargos */
Dimension d = GetLargestCargoIconSize();
for (const CargoSpec *cs : _sorted_standard_cargo_specs) {
list.push_back(std::make_unique<DropDownListIconItem>(d, cs->GetCargoIcon(), PAL_NONE, cs->name, cs->Index(), false));
list.push_back(MakeDropDownListIconItem(d, cs->GetCargoIcon(), PAL_NONE, cs->name, cs->Index()));
}
return list;
@ -1721,7 +1743,7 @@ struct BuildVehicleWindow : Window {
break;
case WID_BV_SORT_DROPDOWN:
SetDParam(0, _engine_sort_listing[this->vehicle_type][this->sort_criteria]);
SetDParam(0, std::data(_engine_sort_listing[this->vehicle_type])[this->sort_criteria]);
break;
case WID_BV_CARGO_FILTER_DROPDOWN:
@ -1740,43 +1762,43 @@ struct BuildVehicleWindow : Window {
}
}
void UpdateWidgetSize(WidgetID widget, Dimension *size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension *fill, [[maybe_unused]] Dimension *resize) override
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
{
switch (widget) {
case WID_BV_LIST:
resize->height = GetEngineListHeight(this->vehicle_type);
size->height = 3 * resize->height;
size->width = std::max(size->width, GetVehicleImageCellSize(this->vehicle_type, EIT_PURCHASE).extend_left + GetVehicleImageCellSize(this->vehicle_type, EIT_PURCHASE).extend_right + 165) + padding.width;
resize.height = GetEngineListHeight(this->vehicle_type);
size.height = 3 * resize.height;
size.width = std::max(size.width, GetVehicleImageCellSize(this->vehicle_type, EIT_PURCHASE).extend_left + GetVehicleImageCellSize(this->vehicle_type, EIT_PURCHASE).extend_right + 165) + padding.width;
break;
case WID_BV_PANEL:
size->height = GetCharacterHeight(FS_NORMAL) * this->details_height + padding.height;
size.height = GetCharacterHeight(FS_NORMAL) * this->details_height + padding.height;
break;
case WID_BV_SORT_ASCENDING_DESCENDING: {
Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
d.height += padding.height;
*size = maxdim(*size, d);
size = maxdim(size, d);
break;
}
case WID_BV_CARGO_FILTER_DROPDOWN:
size->width = std::max(size->width, GetDropDownListDimension(this->BuildCargoDropDownList()).width + padding.width);
size.width = std::max(size.width, GetDropDownListDimension(this->BuildCargoDropDownList()).width + padding.width);
break;
case WID_BV_BUILD:
*size = GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_BUTTON + this->vehicle_type);
*size = maxdim(*size, GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_BUTTON + this->vehicle_type));
size->width += padding.width;
size->height += padding.height;
size = GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_BUTTON + this->vehicle_type);
size = maxdim(size, GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_BUTTON + this->vehicle_type));
size.width += padding.width;
size.height += padding.height;
break;
case WID_BV_SHOW_HIDE:
*size = GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_HIDE_TOGGLE_BUTTON + this->vehicle_type);
*size = maxdim(*size, GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_SHOW_TOGGLE_BUTTON + this->vehicle_type));
size->width += padding.width;
size->height += padding.height;
size = GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_HIDE_TOGGLE_BUTTON + this->vehicle_type);
size = maxdim(size, GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_SHOW_TOGGLE_BUTTON + this->vehicle_type));
size.width += padding.width;
size.height += padding.height;
break;
}
}

@ -0,0 +1,224 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file cachecheck.cpp Check caches. */
#include "stdafx.h"
#include "aircraft.h"
#include "company_base.h"
#include "debug.h"
#include "industry.h"
#include "roadstop_base.h"
#include "roadveh.h"
#include "ship.h"
#include "station_base.h"
#include "station_map.h"
#include "subsidy_func.h"
#include "town.h"
#include "train.h"
#include "vehicle_base.h"
#include "safeguards.h"
extern void AfterLoadCompanyStats();
extern void RebuildTownCaches();
/**
* Check the validity of some of the caches.
* Especially in the sense of desyncs between
* the cached value and what the value would
* be when calculated from the 'base' data.
*/
void CheckCaches()
{
/* Return here so it is easy to add checks that are run
* always to aid testing of caches. */
if (_debug_desync_level <= 1) return;
/* Check the town caches. */
std::vector<TownCache> old_town_caches;
for (const Town *t : Town::Iterate()) {
old_town_caches.push_back(t->cache);
}
RebuildTownCaches();
RebuildSubsidisedSourceAndDestinationCache();
uint i = 0;
for (Town *t : Town::Iterate()) {
if (old_town_caches[i] != t->cache) {
Debug(desync, 2, "warning: town cache mismatch: town {}", t->index);
}
i++;
}
/* Check company infrastructure cache. */
std::vector<CompanyInfrastructure> old_infrastructure;
for (const Company *c : Company::Iterate()) old_infrastructure.push_back(c->infrastructure);
AfterLoadCompanyStats();
i = 0;
for (const Company *c : Company::Iterate()) {
if (old_infrastructure[i] != c->infrastructure) {
Debug(desync, 2, "warning: infrastructure cache mismatch: company {}", c->index);
}
i++;
}
/* Strict checking of the road stop cache entries */
for (const RoadStop *rs : RoadStop::Iterate()) {
if (IsBayRoadStopTile(rs->xy)) continue;
assert(rs->GetEntry(DIAGDIR_NE) != rs->GetEntry(DIAGDIR_NW));
rs->GetEntry(DIAGDIR_NE)->CheckIntegrity(rs);
rs->GetEntry(DIAGDIR_NW)->CheckIntegrity(rs);
}
std::vector<NewGRFCache> grf_cache;
std::vector<VehicleCache> veh_cache;
std::vector<GroundVehicleCache> gro_cache;
std::vector<TrainCache> tra_cache;
for (Vehicle *v : Vehicle::Iterate()) {
if (v != v->First() || v->vehstatus & VS_CRASHED || !v->IsPrimaryVehicle()) continue;
for (const Vehicle *u = v; u != nullptr; u = u->Next()) {
FillNewGRFVehicleCache(u);
grf_cache.emplace_back(u->grf_cache);
veh_cache.emplace_back(u->vcache);
switch (u->type) {
case VEH_TRAIN:
gro_cache.emplace_back(Train::From(u)->gcache);
tra_cache.emplace_back(Train::From(u)->tcache);
break;
case VEH_ROAD:
gro_cache.emplace_back(RoadVehicle::From(u)->gcache);
break;
default:
break;
}
}
switch (v->type) {
case VEH_TRAIN: Train::From(v)->ConsistChanged(CCF_TRACK); break;
case VEH_ROAD: RoadVehUpdateCache(RoadVehicle::From(v)); break;
case VEH_AIRCRAFT: UpdateAircraftCache(Aircraft::From(v)); break;
case VEH_SHIP: Ship::From(v)->UpdateCache(); break;
default: break;
}
uint length = 0;
for (const Vehicle *u = v; u != nullptr; u = u->Next()) {
FillNewGRFVehicleCache(u);
if (grf_cache[length] != u->grf_cache) {
Debug(desync, 2, "warning: newgrf cache mismatch: type {}, vehicle {}, company {}, unit number {}, wagon {}", v->type, v->index, v->owner, v->unitnumber, length);
}
if (veh_cache[length] != u->vcache) {
Debug(desync, 2, "warning: vehicle cache mismatch: type {}, vehicle {}, company {}, unit number {}, wagon {}", v->type, v->index, v->owner, v->unitnumber, length);
}
switch (u->type) {
case VEH_TRAIN:
if (gro_cache[length] != Train::From(u)->gcache) {
Debug(desync, 2, "warning: train ground vehicle cache mismatch: vehicle {}, company {}, unit number {}, wagon {}", v->index, v->owner, v->unitnumber, length);
}
if (tra_cache[length] != Train::From(u)->tcache) {
Debug(desync, 2, "warning: train cache mismatch: vehicle {}, company {}, unit number {}, wagon {}", v->index, v->owner, v->unitnumber, length);
}
break;
case VEH_ROAD:
if (gro_cache[length] != RoadVehicle::From(u)->gcache) {
Debug(desync, 2, "warning: road vehicle ground vehicle cache mismatch: vehicle {}, company {}, unit number {}, wagon {}", v->index, v->owner, v->unitnumber, length);
}
break;
default:
break;
}
length++;
}
grf_cache.clear();
veh_cache.clear();
gro_cache.clear();
tra_cache.clear();
}
/* Check whether the caches are still valid */
for (Vehicle *v : Vehicle::Iterate()) {
[[maybe_unused]] const auto a = v->cargo.PeriodsInTransit();
[[maybe_unused]] const auto b = v->cargo.TotalCount();
[[maybe_unused]] const auto c = v->cargo.GetFeederShare();
v->cargo.InvalidateCache();
assert(a == v->cargo.PeriodsInTransit());
assert(b == v->cargo.TotalCount());
assert(c == v->cargo.GetFeederShare());
}
/* Backup stations_near */
std::vector<StationList> old_town_stations_near;
for (Town *t : Town::Iterate()) old_town_stations_near.push_back(t->stations_near);
std::vector<StationList> old_industry_stations_near;
for (Industry *ind : Industry::Iterate()) old_industry_stations_near.push_back(ind->stations_near);
std::vector<IndustryList> old_station_industries_near;
for (Station *st : Station::Iterate()) old_station_industries_near.push_back(st->industries_near);
for (Station *st : Station::Iterate()) {
for (GoodsEntry &ge : st->goods) {
[[maybe_unused]] const auto a = ge.cargo.PeriodsInTransit();
[[maybe_unused]] const auto b = ge.cargo.TotalCount();
ge.cargo.InvalidateCache();
assert(a == ge.cargo.PeriodsInTransit());
assert(b == ge.cargo.TotalCount());
}
/* Check docking tiles */
TileArea ta;
std::map<TileIndex, bool> docking_tiles;
for (TileIndex tile : st->docking_station) {
ta.Add(tile);
docking_tiles[tile] = IsDockingTile(tile);
}
UpdateStationDockingTiles(st);
if (ta.tile != st->docking_station.tile || ta.w != st->docking_station.w || ta.h != st->docking_station.h) {
Debug(desync, 2, "warning: station docking mismatch: station {}, company {}", st->index, st->owner);
}
for (TileIndex tile : ta) {
if (docking_tiles[tile] != IsDockingTile(tile)) {
Debug(desync, 2, "warning: docking tile mismatch: tile {}", tile);
}
}
}
Station::RecomputeCatchmentForAll();
/* Check industries_near */
i = 0;
for (Station *st : Station::Iterate()) {
if (st->industries_near != old_station_industries_near[i]) {
Debug(desync, 2, "warning: station industries near mismatch: station {}", st->index);
}
i++;
}
/* Check stations_near */
i = 0;
for (Town *t : Town::Iterate()) {
if (t->stations_near != old_town_stations_near[i]) {
Debug(desync, 2, "warning: town stations near mismatch: town {}", t->index);
}
i++;
}
i = 0;
for (Industry *ind : Industry::Iterate()) {
if (ind->stations_near != old_industry_stations_near[i]) {
Debug(desync, 2, "warning: industry stations near mismatch: industry {}", ind->index);
}
i++;
}
}

@ -47,7 +47,7 @@ static constexpr CargoLabel CT_FOOD = CargoLabel{'FOOD'};
/* Tropic */
static constexpr CargoLabel CT_RUBBER = CargoLabel{'RUBR'};
static constexpr CargoLabel CT_FRUIT = CargoLabel{'FRUI'};
static constexpr CargoLabel CT_FRUIT = CargoLabel{'FRUT'};
static constexpr CargoLabel CT_MAIZE = CargoLabel{'MAIZ'};
static constexpr CargoLabel CT_COPPER_ORE = CargoLabel{'CORE'};
static constexpr CargoLabel CT_WATER = CargoLabel{'WATR'};

@ -71,8 +71,7 @@ static int32_t GetAmount(CargoMonitorMap &monitor_map, CargoMonitorID monitor, b
CargoMonitorMap::iterator iter = monitor_map.find(monitor);
if (iter == monitor_map.end()) {
if (keep_monitoring) {
std::pair<CargoMonitorID, uint32_t> p(monitor, 0);
monitor_map.insert(p);
monitor_map.emplace(monitor, 0);
}
return 0;
} else {

@ -45,23 +45,23 @@ private:
int16_t y;
};
uint16_t count{0}; ///< The amount of cargo in this packet.
uint16_t periods_in_transit{0}; ///< Amount of cargo aging periods this packet has been in transit.
uint16_t count = 0; ///< The amount of cargo in this packet.
uint16_t periods_in_transit = 0; ///< Amount of cargo aging periods this packet has been in transit.
Money feeder_share{0}; ///< Value of feeder pickup to be paid for on delivery of cargo.
Money feeder_share = 0; ///< Value of feeder pickup to be paid for on delivery of cargo.
TileIndex source_xy{INVALID_TILE}; ///< The origin of the cargo.
TileIndex source_xy = INVALID_TILE; ///< The origin of the cargo.
Vector travelled{0, 0}; ///< If cargo is in station: the vector from the unload tile to the source tile. If in vehicle: an intermediate value.
SourceID source_id{INVALID_SOURCE}; ///< Index of industry/town/HQ, INVALID_SOURCE if unknown/invalid.
SourceType source_type{SourceType::Industry}; ///< Type of \c source_id.
SourceID source_id = INVALID_SOURCE; ///< Index of industry/town/HQ, INVALID_SOURCE if unknown/invalid.
SourceType source_type = SourceType::Industry; ///< Type of \c source_id.
#ifdef WITH_ASSERT
bool in_vehicle{false}; ///< NOSAVE: Whether this cargo is in a vehicle or not.
bool in_vehicle = false; ///< NOSAVE: Whether this cargo is in a vehicle or not.
#endif /* WITH_ASSERT */
StationID first_station{INVALID_STATION}; ///< The station where the cargo came from first.
StationID next_hop{INVALID_STATION}; ///< Station where the cargo wants to go next.
StationID first_station = INVALID_STATION; ///< The station where the cargo came from first.
StationID next_hop = INVALID_STATION; ///< Station where the cargo wants to go next.
/** The CargoList caches, thus needs to know about it. */
template <class Tinst, class Tcont> friend class CargoList;

@ -95,11 +95,11 @@ void BuildCargoLabelMap()
CargoSpec::label_map.clear();
for (const CargoSpec &cs : CargoSpec::array) {
/* During initialization, CargoSpec can be marked valid before the label has been set. */
if (!cs.IsValid() || cs.label == CargoLabel{0}) continue;
if (!cs.IsValid() || cs.label == CargoLabel{0} || cs.label == CT_INVALID) continue;
/* Label already exists, don't addd again. */
if (CargoSpec::label_map.count(cs.label) != 0) continue;
CargoSpec::label_map.insert(std::make_pair(cs.label, cs.Index()));
CargoSpec::label_map.emplace(cs.label, cs.Index());
}
}

@ -67,19 +67,19 @@ static const uint TOWN_PRODUCTION_DIVISOR = 256;
/** Specification of a cargo type. */
struct CargoSpec {
CargoLabel label; ///< Unique label of the cargo type.
uint8_t bitnum{INVALID_CARGO_BITNUM}; ///< Cargo bit number, is #INVALID_CARGO_BITNUM for a non-used spec.
uint8_t bitnum = INVALID_CARGO_BITNUM; ///< Cargo bit number, is #INVALID_CARGO_BITNUM for a non-used spec.
uint8_t legend_colour;
uint8_t rating_colour;
uint8_t weight; ///< Weight of a single unit of this cargo type in 1/16 ton (62.5 kg).
uint16_t multiplier{0x100}; ///< Capacity multiplier for vehicles. (8 fractional bits)
uint16_t multiplier = 0x100; ///< Capacity multiplier for vehicles. (8 fractional bits)
uint16_t classes; ///< Classes of this cargo type. @see CargoClass
int32_t initial_payment; ///< Initial payment rate before inflation is applied.
uint8_t transit_periods[2];
bool is_freight; ///< Cargo type is considered to be freight (affects train freight multiplier).
TownAcceptanceEffect town_acceptance_effect; ///< The effect that delivering this cargo type has on towns. Also affects destination of subsidies.
TownProductionEffect town_production_effect{INVALID_TPE}; ///< The effect on town cargo production.
uint16_t town_production_multiplier{TOWN_PRODUCTION_DIVISOR}; ///< Town production multipler, if commanded by TownProductionEffect.
TownProductionEffect town_production_effect = INVALID_TPE; ///< The effect on town cargo production.
uint16_t town_production_multiplier = TOWN_PRODUCTION_DIVISOR; ///< Town production multipler, if commanded by TownProductionEffect.
uint8_t callback_mask; ///< Bitmask of cargo callbacks that have to be called
StringID name; ///< Name of this type of cargo.

@ -298,38 +298,37 @@ struct CheatWindow : Window {
}
}
void UpdateWidgetSize(WidgetID widget, Dimension *size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension *fill, [[maybe_unused]] Dimension *resize) override
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
{
if (widget != WID_C_PANEL) return;
uint width = 0;
for (int i = 0; i != lengthof(_cheats_ui); i++) {
const CheatEntry *ce = &_cheats_ui[i];
switch (ce->type) {
for (const auto &ce : _cheats_ui) {
switch (ce.type) {
case SLE_BOOL:
SetDParam(0, STR_CONFIG_SETTING_ON);
width = std::max(width, GetStringBoundingBox(ce->str).width);
width = std::max(width, GetStringBoundingBox(ce.str).width);
SetDParam(0, STR_CONFIG_SETTING_OFF);
width = std::max(width, GetStringBoundingBox(ce->str).width);
width = std::max(width, GetStringBoundingBox(ce.str).width);
break;
default:
switch (ce->str) {
switch (ce.str) {
/* Display date for change date cheat */
case STR_CHEAT_CHANGE_DATE:
SetDParam(0, TimerGameCalendar::ConvertYMDToDate(CalendarTime::MAX_YEAR, 11, 31));
width = std::max(width, GetStringBoundingBox(ce->str).width);
width = std::max(width, GetStringBoundingBox(ce.str).width);
break;
/* Draw coloured flag for change company cheat */
case STR_CHEAT_CHANGE_COMPANY:
SetDParamMaxValue(0, MAX_COMPANIES);
width = std::max(width, GetStringBoundingBox(ce->str).width + WidgetDimensions::scaled.hsep_wide);
width = std::max(width, GetStringBoundingBox(ce.str).width + WidgetDimensions::scaled.hsep_wide);
break;
default:
SetDParam(0, INT64_MAX);
width = std::max(width, GetStringBoundingBox(ce->str).width);
width = std::max(width, GetStringBoundingBox(ce.str).width);
break;
}
break;
@ -339,8 +338,8 @@ struct CheatWindow : Window {
this->line_height = std::max<uint>(this->icon.height, SETTING_BUTTON_HEIGHT);
this->line_height = std::max<uint>(this->line_height, GetCharacterHeight(FS_NORMAL)) + WidgetDimensions::scaled.framerect.Vertical();
size->width = width + WidgetDimensions::scaled.hsep_wide * 2 + SETTING_BUTTON_WIDTH;
size->height = WidgetDimensions::scaled.framerect.Vertical() + this->line_height * lengthof(_cheats_ui);
size.width = width + WidgetDimensions::scaled.hsep_wide * 2 + SETTING_BUTTON_WIDTH;
size.height = WidgetDimensions::scaled.framerect.Vertical() + this->line_height * lengthof(_cheats_ui);
}
void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override

@ -250,7 +250,7 @@ public:
template <typename Tcallback>
static Tret Unsafe(StringID err_message, Tcallback *callback, bool my_cmd, bool estimate_only, TileIndex location, std::tuple<Targs...> args)
{
return Execute(err_message, reinterpret_cast<CommandCallback *>(callback), my_cmd, estimate_only, false, location, std::move(args));
return Execute(err_message, reinterpret_cast<CommandCallback *>(reinterpret_cast<void(*)()>(callback)), my_cmd, estimate_only, false, location, std::move(args));
}
protected:
@ -301,7 +301,7 @@ protected:
/* Only set client IDs when the command does not come from the network. */
if (!network_command && GetCommandFlags<Tcmd>() & CMD_CLIENT_ID) SetClientIds(args, std::index_sequence_for<Targs...>{});
Tret res = Execute(err_message, reinterpret_cast<CommandCallback *>(callback), my_cmd, estimate_only, network_command, tile, args);
Tret res = Execute(err_message, reinterpret_cast<CommandCallback *>(reinterpret_cast<void(*)()>(callback)), my_cmd, estimate_only, network_command, tile, args);
InternalPostResult(ExtractCommandCost(res), tile, estimate_only, only_sending, err_message, my_cmd);
if (!estimate_only && !only_sending && callback != nullptr) {

@ -294,6 +294,7 @@ enum Commands : uint16_t {
CMD_CREATE_SUBSIDY, ///< create a new subsidy
CMD_COMPANY_CTRL, ///< used in multiplayer to create a new companies etc.
CMD_COMPANY_ADD_ALLOW_LIST, ///< Used in multiplayer to add a client's public key to the company's allow list.
CMD_CUSTOM_NEWS_ITEM, ///< create a custom news message
CMD_CREATE_GOAL, ///< create a new goal
CMD_REMOVE_GOAL, ///< remove a goal

@ -30,19 +30,19 @@ struct CompanyEconomyEntry {
};
struct CompanyInfrastructure {
uint32_t road[ROADTYPE_END]; ///< Count of company owned track bits for each road type.
std::array<uint32_t, RAILTYPE_END> rail{}; ///< Count of company owned track bits for each rail type.
std::array<uint32_t, ROADTYPE_END> road{}; ///< Count of company owned track bits for each road type.
uint32_t signal; ///< Count of company owned signals.
uint32_t rail[RAILTYPE_END]; ///< Count of company owned track bits for each rail type.
uint32_t water; ///< Count of company owned track bits for canals.
uint32_t station; ///< Count of company owned station tiles.
uint32_t airport; ///< Count of company owned airports.
auto operator<=>(const CompanyInfrastructure &) const = default;
/** Get total sum of all owned track bits. */
uint32_t GetRailTotal() const
{
uint32_t total = 0;
for (RailType rt = RAILTYPE_BEGIN; rt < RAILTYPE_END; rt++) total += this->rail[rt];
return total;
return std::accumulate(std::begin(this->rail), std::end(this->rail), 0U);
}
uint32_t GetRoadTotal() const;
@ -75,6 +75,8 @@ struct CompanyProperties {
uint32_t president_name_2; ///< Parameter of #president_name_1
std::string president_name; ///< Name of the president if the user changed it.
NetworkAuthorizedKeys allow_list; ///< Public keys of clients that are allowed to join this company.
CompanyManagerFace face; ///< Face description of the president.
Money money; ///< Money owned by the company.
@ -91,6 +93,7 @@ struct CompanyProperties {
TimerGameEconomy::Year inaugurated_year; ///< Economy year of starting the company.
uint8_t months_empty = 0; ///< NOSAVE: Number of months this company has not had a client in multiplayer.
uint8_t months_of_bankruptcy; ///< Number of months that the company is unable to pay its debts
CompanyMask bankrupt_asked; ///< which companies were asked about buying it?
int16_t bankrupt_timeout; ///< If bigger than \c 0, amount of time to wait for an answer on an offer to buy this company.
@ -133,7 +136,7 @@ struct Company : CompanyProperties, CompanyPool::PoolItem<&_company_pool> {
RailTypes avail_railtypes; ///< Rail types available to this company.
RoadTypes avail_roadtypes; ///< Road types available to this company.
class AIInstance *ai_instance;
std::unique_ptr<class AIInstance> ai_instance;
class AIInfo *ai_info;
std::unique_ptr<class AIConfig> ai_config;

@ -20,6 +20,7 @@
#include "network/network_base.h"
#include "network/network_admin.h"
#include "ai/ai.hpp"
#include "ai/ai_instance.hpp"
#include "ai/ai_config.hpp"
#include "company_manager_face.h"
#include "window_func.h"
@ -34,12 +35,13 @@
#include "game/game.hpp"
#include "goal_base.h"
#include "story_base.h"
#include "widgets/statusbar_widget.h"
#include "company_cmd.h"
#include "timer/timer.h"
#include "timer/timer_game_economy.h"
#include "timer/timer_game_tick.h"
#include "widgets/statusbar_widget.h"
#include "table/strings.h"
#include "safeguards.h"
@ -635,7 +637,7 @@ Company *DoStartupNewCompany(bool is_ai, CompanyID company = INVALID_COMPANY)
}
/** Start a new competitor company if possible. */
TimeoutTimer<TimerGameTick> _new_competitor_timeout(0, []() {
TimeoutTimer<TimerGameTick> _new_competitor_timeout({ TimerGameTick::Priority::COMPETITOR_TIMEOUT, 0 }, []() {
if (_game_mode == GM_MENU || !AI::CanStartNew()) return;
if (_networking && Company::GetNumItems() >= _settings_client.network.max_companies) return;
@ -776,7 +778,7 @@ void OnTick_Companies()
/* Randomize a bit when the AI is actually going to start; ranges from 87.5% .. 112.5% of indicated value. */
timeout += ScriptObject::GetRandomizer(OWNER_NONE).Next(timeout / 4) - timeout / 8;
_new_competitor_timeout.Reset(std::max(1, timeout));
_new_competitor_timeout.Reset({ TimerGameTick::Priority::COMPETITOR_TIMEOUT, static_cast<uint>(std::max(1, timeout)) });
}
_cur_company_tick_index = (_cur_company_tick_index + 1) % MAX_COMPANIES;
@ -888,8 +890,6 @@ CommandCost CmdCompanyCtrl(DoCommandFlag flags, CompanyCtrlAction cca, CompanyID
break;
}
/* Send new companies, before potentially setting the password. Otherwise,
* the password update could be sent when the company is not yet known. */
NetworkAdminCompanyNew(c);
NetworkServerNewCompany(c, ci);
@ -897,9 +897,6 @@ CommandCost CmdCompanyCtrl(DoCommandFlag flags, CompanyCtrlAction cca, CompanyID
if (client_id == _network_own_client_id) {
assert(_local_company == COMPANY_SPECTATOR);
SetLocalCompany(c->index);
if (!_settings_client.network.default_company_pass.empty()) {
NetworkChangeCompanyPassword(_local_company, _settings_client.network.default_company_pass);
}
/* In network games, we need to try setting the company manager face here to sync it to all clients.
* If a favorite company manager face is selected, choose it. Otherwise, use a random face. */
@ -978,6 +975,24 @@ CommandCost CmdCompanyCtrl(DoCommandFlag flags, CompanyCtrlAction cca, CompanyID
return CommandCost();
}
/**
* Add the given public key to the allow list of this company.
* @param flags Operation to perform.
* @param public_key The public key of the client to add.
* @return The cost of this operation or an error.
*/
CommandCost CmdCompanyAddAllowList(DoCommandFlag flags, const std::string &public_key)
{
if (flags & DC_EXEC) {
if (Company::Get(_current_company)->allow_list.Add(public_key)) {
InvalidateWindowData(WC_CLIENT_LIST, 0);
SetWindowDirty(WC_COMPANY, _current_company);
}
}
return CommandCost();
}
/**
* Change the company manager's face.
* @param flags operation to perform
@ -1181,6 +1196,7 @@ CommandCost CmdRenamePresident(DoCommandFlag flags, const std::string &text)
}
}
InvalidateWindowClassesData(WC_COMPANY, 1);
MarkWholeScreenDirty();
CompanyAdminUpdate(c);
}

@ -18,6 +18,7 @@ enum ClientID : uint32_t;
enum Colours : uint8_t;
CommandCost CmdCompanyCtrl(DoCommandFlag flags, CompanyCtrlAction cca, CompanyID company_id, CompanyRemoveReason reason, ClientID client_id);
CommandCost CmdCompanyAddAllowList(DoCommandFlag flags, const std::string &public_key);
CommandCost CmdGiveMoney(DoCommandFlag flags, Money money, CompanyID dest_company);
CommandCost CmdRenameCompany(DoCommandFlag flags, const std::string &text);
CommandCost CmdRenamePresident(DoCommandFlag flags, const std::string &text);
@ -25,6 +26,7 @@ CommandCost CmdSetCompanyManagerFace(DoCommandFlag flags, CompanyManagerFace cmf
CommandCost CmdSetCompanyColour(DoCommandFlag flags, LiveryScheme scheme, bool primary, Colours colour);
DEF_CMD_TRAIT(CMD_COMPANY_CTRL, CmdCompanyCtrl, CMD_SPECTATOR | CMD_CLIENT_ID | CMD_NO_EST, CMDT_SERVER_SETTING)
DEF_CMD_TRAIT(CMD_COMPANY_ADD_ALLOW_LIST, CmdCompanyAddAllowList, CMD_NO_EST, CMDT_SERVER_SETTING)
DEF_CMD_TRAIT(CMD_GIVE_MONEY, CmdGiveMoney, 0, CMDT_MONEY_MANAGEMENT)
DEF_CMD_TRAIT(CMD_RENAME_COMPANY, CmdRenameCompany, 0, CMDT_OTHER_MANAGEMENT)
DEF_CMD_TRAIT(CMD_RENAME_PRESIDENT, CmdRenamePresident, 0, CMDT_OTHER_MANAGEMENT)

@ -23,7 +23,8 @@
#include "company_manager_face.h"
#include "strings_func.h"
#include "timer/timer_game_economy.h"
#include "widgets/dropdown_type.h"
#include "dropdown_type.h"
#include "dropdown_common_type.h"
#include "tilehighlight_func.h"
#include "company_base.h"
#include "core/geometry_func.hpp"
@ -40,6 +41,7 @@
#include "company_cmd.h"
#include "economy_cmd.h"
#include "group_cmd.h"
#include "group_gui.h"
#include "misc_cmd.h"
#include "object_cmd.h"
#include "timer/timer.h"
@ -285,7 +287,7 @@ static constexpr NWidgetPart _nested_company_finances_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, COLOUR_GREY),
NWidget(WWT_CAPTION, COLOUR_GREY, WID_CF_CAPTION), SetDataTip(STR_FINANCES_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
NWidget(WWT_IMGBTN, COLOUR_GREY, WID_CF_TOGGLE_SIZE), SetDataTip(SPR_LARGE_SMALL_WINDOW, STR_TOOLTIP_TOGGLE_LARGE_SMALL_WINDOW),
NWidget(WWT_IMGBTN, COLOUR_GREY, WID_CF_TOGGLE_SIZE), SetDataTip(SPR_LARGE_SMALL_WINDOW, STR_TOOLTIP_TOGGLE_LARGE_SMALL_WINDOW), SetAspect(WidgetDimensions::ASPECT_TOGGLE_SIZE),
NWidget(WWT_SHADEBOX, COLOUR_GREY),
NWidget(WWT_STICKYBOX, COLOUR_GREY),
EndContainer(),
@ -387,29 +389,29 @@ struct CompanyFinancesWindow : Window {
}
}
void UpdateWidgetSize(WidgetID widget, Dimension *size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension *fill, [[maybe_unused]] Dimension *resize) override
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
{
switch (widget) {
case WID_CF_EXPS_CATEGORY:
size->width = GetMaxCategoriesWidth();
size->height = GetTotalCategoriesHeight();
size.width = GetMaxCategoriesWidth();
size.height = GetTotalCategoriesHeight();
break;
case WID_CF_EXPS_PRICE1:
case WID_CF_EXPS_PRICE2:
case WID_CF_EXPS_PRICE3:
size->height = GetTotalCategoriesHeight();
size.height = GetTotalCategoriesHeight();
[[fallthrough]];
case WID_CF_BALANCE_VALUE:
case WID_CF_LOAN_VALUE:
case WID_CF_OWN_VALUE:
SetDParamMaxValue(0, CompanyFinancesWindow::max_money);
size->width = std::max(GetStringBoundingBox(STR_FINANCES_NEGATIVE_INCOME).width, GetStringBoundingBox(STR_FINANCES_POSITIVE_INCOME).width) + padding.width;
size.width = std::max(GetStringBoundingBox(STR_FINANCES_NEGATIVE_INCOME).width, GetStringBoundingBox(STR_FINANCES_POSITIVE_INCOME).width) + padding.width;
break;
case WID_CF_INTEREST_RATE:
size->height = GetCharacterHeight(FS_NORMAL);
size.height = GetCharacterHeight(FS_NORMAL);
break;
}
}
@ -549,26 +551,6 @@ void ShowCompanyFinances(CompanyID company)
new CompanyFinancesWindow(&_company_finances_desc, company);
}
/* List of colours for the livery window */
static const StringID _colour_dropdown[] = {
STR_COLOUR_DARK_BLUE,
STR_COLOUR_PALE_GREEN,
STR_COLOUR_PINK,
STR_COLOUR_YELLOW,
STR_COLOUR_RED,
STR_COLOUR_LIGHT_BLUE,
STR_COLOUR_GREEN,
STR_COLOUR_DARK_GREEN,
STR_COLOUR_BLUE,
STR_COLOUR_CREAM,
STR_COLOUR_MAUVE,
STR_COLOUR_PURPLE,
STR_COLOUR_ORANGE,
STR_COLOUR_BROWN,
STR_COLOUR_GREY,
STR_COLOUR_WHITE,
};
/* Association of liveries to livery classes */
static const LiveryClass _livery_class[LS_END] = {
LC_OTHER,
@ -586,13 +568,11 @@ static const LiveryClass _livery_class[LS_END] = {
template <SpriteID TSprite = SPR_SQUARE>
class DropDownListColourItem : public DropDownIcon<DropDownString<DropDownListItem>> {
public:
DropDownListColourItem(int colour, bool masked) : DropDownIcon<DropDownString<DropDownListItem>>(TSprite, GENERAL_SPRITE_COLOUR(colour % COLOUR_END), colour < COLOUR_END ? _colour_dropdown[colour] : STR_COLOUR_DEFAULT, colour, masked)
DropDownListColourItem(int colour, bool masked) : DropDownIcon<DropDownString<DropDownListItem>>(TSprite, GENERAL_SPRITE_COLOUR(colour % COLOUR_END), colour < COLOUR_END ? (STR_COLOUR_DARK_BLUE + colour) : STR_COLOUR_DEFAULT, colour, masked)
{
}
};
typedef GUIList<const Group*> GUIGroupList;
/** Company livery colour scheme window. */
struct SelectCompanyLiveryWindow : public Window {
private:
@ -602,7 +582,6 @@ private:
uint rows;
uint line_height;
GUIGroupList groups;
std::vector<int> indents;
Scrollbar *vscroll;
void ShowColourDropDownMenu(uint32_t widget)
@ -647,8 +626,8 @@ private:
default_col = (primary ? default_livery->colour1 : default_livery->colour2) + COLOUR_END;
list.push_back(std::make_unique<DropDownListColourItem<>>(default_col, false));
}
for (uint i = 0; i < lengthof(_colour_dropdown); i++) {
list.push_back(std::make_unique<DropDownListColourItem<>>(i, HasBit(used_colours, i)));
for (Colours colour = COLOUR_BEGIN; colour != COLOUR_END; colour++) {
list.push_back(std::make_unique<DropDownListColourItem<>>(colour, HasBit(used_colours, colour)));
}
uint8_t sel;
@ -660,60 +639,17 @@ private:
ShowDropDownList(this, std::move(list), sel, widget);
}
void AddChildren(GUIGroupList &source, GroupID parent, int indent)
{
for (const Group *g : source) {
if (g->parent != parent) continue;
this->groups.push_back(g);
this->indents.push_back(indent);
AddChildren(source, g->index, indent + 1);
}
}
void BuildGroupList(CompanyID owner)
{
if (!this->groups.NeedRebuild()) return;
this->groups.clear();
this->indents.clear();
if (this->livery_class >= LC_GROUP_RAIL) {
GUIGroupList list;
VehicleType vtype = (VehicleType)(this->livery_class - LC_GROUP_RAIL);
for (const Group *g : Group::Iterate()) {
if (g->owner == owner && g->vehicle_type == vtype) {
list.push_back(g);
}
}
list.ForceResort();
/* Sort the groups by their name */
const Group *last_group[2] = { nullptr, nullptr };
std::string last_name[2] = { {}, {} };
list.Sort([&](const Group * const &a, const Group * const &b) -> bool {
if (a != last_group[0]) {
last_group[0] = a;
SetDParam(0, a->index);
last_name[0] = GetString(STR_GROUP_NAME);
}
if (b != last_group[1]) {
last_group[1] = b;
SetDParam(0, b->index);
last_name[1] = GetString(STR_GROUP_NAME);
}
int r = StrNaturalCompare(last_name[0], last_name[1]); // Sort by name (natural sorting).
if (r == 0) return a->index < b->index;
return r < 0;
});
AddChildren(list, INVALID_GROUP, 0);
BuildGuiGroupList(this->groups, false, owner, vtype);
}
this->groups.shrink_to_fit();
this->groups.RebuildDone();
}
@ -774,14 +710,14 @@ public:
/* Position scrollbar to selected group */
for (uint i = 0; i < this->rows; i++) {
if (this->groups[i]->index == sel) {
if (this->groups[i].group->index == sel) {
this->vscroll->SetPosition(i - this->vscroll->GetCapacity() / 2);
break;
}
}
}
void UpdateWidgetSize(WidgetID widget, Dimension *size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension *fill, [[maybe_unused]] Dimension *resize) override
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
{
switch (widget) {
case WID_SCL_SPACER_DROPDOWN: {
@ -799,7 +735,7 @@ public:
}
}
size->width = std::max(size->width, 5 + d.width + padding.width);
size.width = std::max(size.width, 5 + d.width + padding.width);
break;
}
@ -808,15 +744,15 @@ public:
this->square = GetSpriteSize(SPR_SQUARE);
this->line_height = std::max(this->square.height, (uint)GetCharacterHeight(FS_NORMAL)) + padding.height;
size->height = 5 * this->line_height;
resize->width = 1;
resize->height = this->line_height;
size.height = 5 * this->line_height;
resize.width = 1;
resize.height = this->line_height;
break;
}
case WID_SCL_SEC_COL_DROPDOWN:
if (!_loaded_newgrf_features.has_2CC) {
size->width = 0;
size.width = 0;
break;
}
[[fallthrough]];
@ -824,10 +760,10 @@ public:
case WID_SCL_PRI_COL_DROPDOWN: {
this->square = GetSpriteSize(SPR_SQUARE);
int string_padding = this->square.width + WidgetDimensions::scaled.hsep_normal + padding.width;
for (const StringID *id = _colour_dropdown; id != endof(_colour_dropdown); id++) {
size->width = std::max(size->width, GetStringBoundingBox(*id).width + string_padding);
for (Colours colour = COLOUR_BEGIN; colour != COLOUR_END; colour++) {
size.width = std::max(size.width, GetStringBoundingBox(STR_COLOUR_DARK_BLUE + colour).width + string_padding);
}
size->width = std::max(size->width, GetStringBoundingBox(STR_COLOUR_DEFAULT).width + string_padding);
size.width = std::max(size.width, GetStringBoundingBox(STR_COLOUR_DEFAULT).width + string_padding);
break;
}
}
@ -944,11 +880,11 @@ public:
}
}
} else {
uint max = static_cast<uint>(std::min<size_t>(this->vscroll->GetPosition() + this->vscroll->GetCapacity(), this->groups.size()));
for (uint i = this->vscroll->GetPosition(); i < max; ++i) {
const Group *g = this->groups[i];
auto [first, last] = this->vscroll->GetVisibleRangeIterators(this->groups);
for (auto it = first; it != last; ++it) {
const Group *g = it->group;
SetDParam(0, g->index);
draw_livery(STR_GROUP_NAME, g->livery, this->sel == g->index, false, this->indents[i] * WidgetDimensions::scaled.hsep_indent);
draw_livery(STR_GROUP_NAME, g->livery, this->sel == g->index, false, it->indent * WidgetDimensions::scaled.hsep_indent);
}
if (this->vscroll->GetCount() == 0) {
@ -991,7 +927,7 @@ public:
this->BuildGroupList((CompanyID)this->window_number);
if (!this->groups.empty()) {
this->sel = this->groups[0]->index;
this->sel = this->groups[0].group->index;
}
}
@ -1008,10 +944,10 @@ public:
break;
case WID_SCL_MATRIX: {
uint row = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SCL_MATRIX);
if (row >= this->rows) return;
if (this->livery_class < LC_GROUP_RAIL) {
uint row = this->vscroll->GetScrolledRowFromWidget(pt.y, this, widget);
if (row >= this->rows) return;
LiveryScheme j = (LiveryScheme)row;
for (LiveryScheme scheme = LS_BEGIN; scheme <= j && scheme < LS_END; scheme++) {
@ -1025,7 +961,10 @@ public:
this->sel = 1 << j;
}
} else {
this->sel = this->groups[row]->index;
auto it = this->vscroll->GetScrolledItemFromWidget(this->groups, pt.y, this, widget);
if (it == std::end(this->groups)) return;
this->sel = it->group->index;
}
this->SetDirty();
break;
@ -1078,7 +1017,7 @@ public:
if (!Group::IsValidID(this->sel)) {
this->sel = INVALID_GROUP;
if (!this->groups.empty()) this->sel = this->groups[0]->index;
if (!this->groups.empty()) this->sel = this->groups[0].group->index;
}
this->SetDirty();
@ -1209,7 +1148,7 @@ static constexpr NWidgetPart _nested_select_company_manager_face_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, COLOUR_GREY),
NWidget(WWT_CAPTION, COLOUR_GREY, WID_SCMF_CAPTION), SetDataTip(STR_FACE_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
NWidget(WWT_IMGBTN, COLOUR_GREY, WID_SCMF_TOGGLE_LARGE_SMALL), SetDataTip(SPR_LARGE_SMALL_WINDOW, STR_FACE_ADVANCED_TOOLTIP),
NWidget(WWT_IMGBTN, COLOUR_GREY, WID_SCMF_TOGGLE_LARGE_SMALL), SetDataTip(SPR_LARGE_SMALL_WINDOW, STR_FACE_ADVANCED_TOOLTIP), SetAspect(WidgetDimensions::ASPECT_TOGGLE_SIZE),
EndContainer(),
NWidget(WWT_PANEL, COLOUR_GREY, WID_SCMF_SELECT_FACE),
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPadding(2),
@ -1465,31 +1404,31 @@ public:
this->number_dim = number_dim;
}
void UpdateWidgetSize(WidgetID widget, Dimension *size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension *fill, [[maybe_unused]] Dimension *resize) override
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
{
switch (widget) {
case WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT:
*size = maxdim(*size, GetStringBoundingBox(STR_FACE_EARRING));
*size = maxdim(*size, GetStringBoundingBox(STR_FACE_MOUSTACHE));
size = maxdim(size, GetStringBoundingBox(STR_FACE_EARRING));
size = maxdim(size, GetStringBoundingBox(STR_FACE_MOUSTACHE));
break;
case WID_SCMF_TIE_EARRING_TEXT:
*size = maxdim(*size, GetStringBoundingBox(STR_FACE_EARRING));
*size = maxdim(*size, GetStringBoundingBox(STR_FACE_TIE));
size = maxdim(size, GetStringBoundingBox(STR_FACE_EARRING));
size = maxdim(size, GetStringBoundingBox(STR_FACE_TIE));
break;
case WID_SCMF_LIPS_MOUSTACHE_TEXT:
*size = maxdim(*size, GetStringBoundingBox(STR_FACE_LIPS));
*size = maxdim(*size, GetStringBoundingBox(STR_FACE_MOUSTACHE));
size = maxdim(size, GetStringBoundingBox(STR_FACE_LIPS));
size = maxdim(size, GetStringBoundingBox(STR_FACE_MOUSTACHE));
break;
case WID_SCMF_FACE:
*size = maxdim(*size, GetScaledSpriteSize(SPR_GRADIENT));
size = maxdim(size, GetScaledSpriteSize(SPR_GRADIENT));
break;
case WID_SCMF_HAS_MOUSTACHE_EARRING:
case WID_SCMF_HAS_GLASSES:
*size = this->yesno_dim;
size = this->yesno_dim;
break;
case WID_SCMF_EYECOLOUR:
@ -1502,7 +1441,7 @@ public:
case WID_SCMF_COLLAR:
case WID_SCMF_TIE_EARRING:
case WID_SCMF_GLASSES:
*size = this->number_dim;
size = this->number_dim;
break;
}
}
@ -1900,7 +1839,7 @@ struct CompanyInfrastructureWindow : Window
}
}
void UpdateWidgetSize(WidgetID widget, Dimension *size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension *fill, [[maybe_unused]] Dimension *resize) override
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
{
const Company *c = Company::Get((CompanyID)this->window_number);
@ -1908,20 +1847,20 @@ struct CompanyInfrastructureWindow : Window
case WID_CI_RAIL_DESC: {
uint lines = 1; // Starts at 1 because a line is also required for the section title
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_RAIL_SECT).width + padding.width);
size.width = std::max(size.width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_RAIL_SECT).width + padding.width);
for (const auto &rt : _sorted_railtypes) {
if (HasBit(this->railtypes, rt)) {
lines++;
size->width = std::max(size->width, GetStringBoundingBox(GetRailTypeInfo(rt)->strings.name).width + padding.width + WidgetDimensions::scaled.hsep_indent);
size.width = std::max(size.width, GetStringBoundingBox(GetRailTypeInfo(rt)->strings.name).width + padding.width + WidgetDimensions::scaled.hsep_indent);
}
}
if (this->railtypes != RAILTYPES_NONE) {
lines++;
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_SIGNALS).width + padding.width + WidgetDimensions::scaled.hsep_indent);
size.width = std::max(size.width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_SIGNALS).width + padding.width + WidgetDimensions::scaled.hsep_indent);
}
size->height = std::max(size->height, lines * GetCharacterHeight(FS_NORMAL));
size.height = std::max(size.height, lines * GetCharacterHeight(FS_NORMAL));
break;
}
@ -1929,28 +1868,28 @@ struct CompanyInfrastructureWindow : Window
case WID_CI_TRAM_DESC: {
uint lines = 1; // Starts at 1 because a line is also required for the section title
size->width = std::max(size->width, GetStringBoundingBox(widget == WID_CI_ROAD_DESC ? STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD_SECT : STR_COMPANY_INFRASTRUCTURE_VIEW_TRAM_SECT).width + padding.width);
size.width = std::max(size.width, GetStringBoundingBox(widget == WID_CI_ROAD_DESC ? STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD_SECT : STR_COMPANY_INFRASTRUCTURE_VIEW_TRAM_SECT).width + padding.width);
for (const auto &rt : _sorted_roadtypes) {
if (HasBit(this->roadtypes, rt) && RoadTypeIsRoad(rt) == (widget == WID_CI_ROAD_DESC)) {
lines++;
size->width = std::max(size->width, GetStringBoundingBox(GetRoadTypeInfo(rt)->strings.name).width + padding.width + WidgetDimensions::scaled.hsep_indent);
size.width = std::max(size.width, GetStringBoundingBox(GetRoadTypeInfo(rt)->strings.name).width + padding.width + WidgetDimensions::scaled.hsep_indent);
}
}
size->height = std::max(size->height, lines * GetCharacterHeight(FS_NORMAL));
size.height = std::max(size.height, lines * GetCharacterHeight(FS_NORMAL));
break;
}
case WID_CI_WATER_DESC:
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_WATER_SECT).width + padding.width);
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_CANALS).width + padding.width + WidgetDimensions::scaled.hsep_indent);
size.width = std::max(size.width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_WATER_SECT).width + padding.width);
size.width = std::max(size.width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_CANALS).width + padding.width + WidgetDimensions::scaled.hsep_indent);
break;
case WID_CI_STATION_DESC:
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_STATION_SECT).width + padding.width);
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_STATIONS).width + padding.width + WidgetDimensions::scaled.hsep_indent);
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_AIRPORTS).width + padding.width + WidgetDimensions::scaled.hsep_indent);
size.width = std::max(size.width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_STATION_SECT).width + padding.width);
size.width = std::max(size.width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_STATIONS).width + padding.width + WidgetDimensions::scaled.hsep_indent);
size.width = std::max(size.width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_AIRPORTS).width + padding.width + WidgetDimensions::scaled.hsep_indent);
break;
case WID_CI_RAIL_COUNT:
@ -1990,17 +1929,17 @@ struct CompanyInfrastructureWindow : Window
StringID str_total = TimerGameEconomy::UsingWallclockUnits() ? STR_COMPANY_INFRASTRUCTURE_VIEW_TOTAL_PERIOD : STR_COMPANY_INFRASTRUCTURE_VIEW_TOTAL_YEAR;
SetDParamMaxValue(0, this->GetTotalMaintenanceCost() * 12); // Convert to per year
this->total_width = GetStringBoundingBox(str_total).width + WidgetDimensions::scaled.hsep_indent * 2;
size->width = std::max(size->width, this->total_width);
size.width = std::max(size.width, this->total_width);
SetDParamMaxValue(0, max_cost * 12); // Convert to per year
count_width += std::max(this->total_width, GetStringBoundingBox(str_total).width);
}
size->width = std::max(size->width, count_width);
size.width = std::max(size.width, count_width);
/* Set height of the total line. */
if (widget == WID_CI_TOTAL) {
size->height = _settings_game.economy.infrastructure_maintenance ? std::max<uint>(size->height, WidgetDimensions::scaled.vsep_normal + GetCharacterHeight(FS_NORMAL)) : 0;
size.height = _settings_game.economy.infrastructure_maintenance ? std::max<uint>(size.height, WidgetDimensions::scaled.vsep_normal + GetCharacterHeight(FS_NORMAL)) : 0;
}
break;
}
@ -2208,9 +2147,6 @@ static constexpr NWidgetPart _nested_company_widgets[] = {
/* Multi player buttons. */
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 0),
NWidget(NWID_VERTICAL), SetPIPRatio(1, 0, 0),
NWidget(WWT_EMPTY, COLOUR_GREY, WID_C_HAS_PASSWORD), SetFill(0, 0),
EndContainer(),
NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0),
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_C_SELECT_HOSTILE_TAKEOVER),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_C_HOSTILE_TAKEOVER), SetDataTip(STR_COMPANY_VIEW_HOSTILE_TAKEOVER_BUTTON, STR_COMPANY_VIEW_HOSTILE_TAKEOVER_TOOLTIP),
@ -2219,7 +2155,6 @@ static constexpr NWidgetPart _nested_company_widgets[] = {
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_C_GIVE_MONEY), SetDataTip(STR_COMPANY_VIEW_GIVE_MONEY_BUTTON, STR_COMPANY_VIEW_GIVE_MONEY_TOOLTIP),
EndContainer(),
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_C_SELECT_MULTIPLAYER),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_C_COMPANY_PASSWORD), SetDataTip(STR_COMPANY_VIEW_PASSWORD, STR_COMPANY_VIEW_PASSWORD_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_C_COMPANY_JOIN), SetDataTip(STR_COMPANY_VIEW_JOIN, STR_COMPANY_VIEW_JOIN_TOOLTIP),
EndContainer(),
EndContainer(),
@ -2252,10 +2187,6 @@ struct CompanyWindow : Window
/** Display planes in the company window. */
enum CompanyWindowPlanes {
/* Display planes of the #WID_C_SELECT_MULTIPLAYER selection widget. */
CWP_MP_C_PWD = 0, ///< Display the company password button.
CWP_MP_C_JOIN, ///< Display the join company button.
/* Display planes of the #WID_C_SELECT_VIEW_BUILD_HQ selection widget. */
CWP_VB_VIEW = 0, ///< Display the view button
CWP_VB_BUILD, ///< Display the build button
@ -2296,7 +2227,7 @@ struct CompanyWindow : Window
reinit |= this->GetWidget<NWidgetStacked>(WID_C_SELECT_HOSTILE_TAKEOVER)->SetDisplayedPlane((local || _local_company == COMPANY_SPECTATOR || !c->is_ai || _networking) ? SZSP_NONE : 0);
/* Multiplayer buttons. */
reinit |= this->GetWidget<NWidgetStacked>(WID_C_SELECT_MULTIPLAYER)->SetDisplayedPlane((!_networking) ? (int)SZSP_NONE : (int)(local ? CWP_MP_C_PWD : CWP_MP_C_JOIN));
reinit |= this->GetWidget<NWidgetStacked>(WID_C_SELECT_MULTIPLAYER)->SetDisplayedPlane((!_networking || !NetworkCanJoinCompany(c->index) || _local_company == c->index) ? (int)SZSP_NONE : 0);
this->SetWidgetDisabledState(WID_C_COMPANY_JOIN, c->is_ai);
@ -2309,11 +2240,11 @@ struct CompanyWindow : Window
this->DrawWidgets();
}
void UpdateWidgetSize(WidgetID widget, Dimension *size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension *fill, [[maybe_unused]] Dimension *resize) override
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
{
switch (widget) {
case WID_C_FACE:
*size = maxdim(*size, GetScaledSpriteSize(SPR_GRADIENT));
size = maxdim(size, GetScaledSpriteSize(SPR_GRADIENT));
break;
case WID_C_DESC_COLOUR_SCHEME_EXAMPLE: {
@ -2321,31 +2252,31 @@ struct CompanyWindow : Window
Dimension d = GetSpriteSize(SPR_VEH_BUS_SW_VIEW, &offset);
d.width -= offset.x;
d.height -= offset.y;
*size = maxdim(*size, d);
size = maxdim(size, d);
break;
}
case WID_C_DESC_COMPANY_VALUE:
SetDParam(0, INT64_MAX); // Arguably the maximum company value
size->width = GetStringBoundingBox(STR_COMPANY_VIEW_COMPANY_VALUE).width;
size.width = GetStringBoundingBox(STR_COMPANY_VIEW_COMPANY_VALUE).width;
break;
case WID_C_DESC_VEHICLE_COUNTS:
SetDParamMaxValue(0, 5000); // Maximum number of vehicles
for (uint i = 0; i < lengthof(_company_view_vehicle_count_strings); i++) {
size->width = std::max(size->width, GetStringBoundingBox(_company_view_vehicle_count_strings[i]).width + padding.width);
for (const auto &count_string : _company_view_vehicle_count_strings) {
size.width = std::max(size.width, GetStringBoundingBox(count_string).width + padding.width);
}
break;
case WID_C_DESC_INFRASTRUCTURE_COUNTS:
SetDParamMaxValue(0, UINT_MAX);
size->width = GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_RAIL).width;
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_ROAD).width);
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_WATER).width);
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_STATION).width);
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_AIRPORT).width);
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_NONE).width);
size->width += padding.width;
size.width = GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_RAIL).width;
size.width = std::max(size.width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_ROAD).width);
size.width = std::max(size.width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_WATER).width);
size.width = std::max(size.width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_STATION).width);
size.width = std::max(size.width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_AIRPORT).width);
size.width = std::max(size.width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_NONE).width);
size.width += padding.width;
break;
case WID_C_VIEW_HQ:
@ -2354,21 +2285,15 @@ struct CompanyWindow : Window
case WID_C_VIEW_INFRASTRUCTURE:
case WID_C_GIVE_MONEY:
case WID_C_HOSTILE_TAKEOVER:
case WID_C_COMPANY_PASSWORD:
case WID_C_COMPANY_JOIN:
size->width = GetStringBoundingBox(STR_COMPANY_VIEW_VIEW_HQ_BUTTON).width;
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_BUILD_HQ_BUTTON).width);
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_RELOCATE_HQ).width);
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_BUTTON).width);
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_GIVE_MONEY_BUTTON).width);
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_HOSTILE_TAKEOVER_BUTTON).width);
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_PASSWORD).width);
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_JOIN).width);
size->width += padding.width;
break;
case WID_C_HAS_PASSWORD:
if (_networking) *size = maxdim(*size, GetSpriteSize(SPR_LOCK));
size.width = GetStringBoundingBox(STR_COMPANY_VIEW_VIEW_HQ_BUTTON).width;
size.width = std::max(size.width, GetStringBoundingBox(STR_COMPANY_VIEW_BUILD_HQ_BUTTON).width);
size.width = std::max(size.width, GetStringBoundingBox(STR_COMPANY_VIEW_RELOCATE_HQ).width);
size.width = std::max(size.width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_BUTTON).width);
size.width = std::max(size.width, GetStringBoundingBox(STR_COMPANY_VIEW_GIVE_MONEY_BUTTON).width);
size.width = std::max(size.width, GetStringBoundingBox(STR_COMPANY_VIEW_HOSTILE_TAKEOVER_BUTTON).width);
size.width = std::max(size.width, GetStringBoundingBox(STR_COMPANY_VIEW_JOIN).width);
size.width += padding.width;
break;
}
}
@ -2397,16 +2322,15 @@ struct CompanyWindow : Window
{
int y = r.top;
uint rail_pieces = c->infrastructure.signal;
for (uint i = 0; i < lengthof(c->infrastructure.rail); i++) rail_pieces += c->infrastructure.rail[i];
uint rail_pieces = c->infrastructure.signal + c->infrastructure.GetRailTotal();
if (rail_pieces != 0) {
SetDParam(0, rail_pieces);
DrawString(r.left, r.right, y, STR_COMPANY_VIEW_INFRASTRUCTURE_RAIL);
y += GetCharacterHeight(FS_NORMAL);
}
uint road_pieces = 0;
for (uint i = 0; i < lengthof(c->infrastructure.road); i++) road_pieces += c->infrastructure.road[i];
/* GetRoadTotal() skips tram pieces, but we actually want road and tram here. */
uint road_pieces = std::accumulate(std::begin(c->infrastructure.road), std::end(c->infrastructure.road), 0U);
if (road_pieces != 0) {
SetDParam(0, road_pieces);
DrawString(r.left, r.right, y, STR_COMPANY_VIEW_INFRASTRUCTURE_ROAD);
@ -2465,12 +2389,6 @@ struct CompanyWindow : Window
case WID_C_DESC_INFRASTRUCTURE_COUNTS:
DrawInfrastructureCountsWidget(r, c);
break;
case WID_C_HAS_PASSWORD:
if (_networking && NetworkCompanyIsPassworded(c->index)) {
DrawSprite(SPR_LOCK, PAL_NONE, r.left, r.top);
}
break;
}
}
@ -2492,6 +2410,14 @@ struct CompanyWindow : Window
}
}
void OnResize() override
{
NWidgetResizeBase *wid = this->GetWidget<NWidgetResizeBase>(WID_C_FACE_TITLE);
SetDParam(0, this->owner);
int y = GetStringHeight(STR_COMPANY_VIEW_PRESIDENT_MANAGER_TITLE, wid->current_x);
if (wid->UpdateVerticalSize(y)) this->ReInit(0, 0);
}
void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
{
switch (widget) {
@ -2561,19 +2487,12 @@ struct CompanyWindow : Window
ShowBuyCompanyDialog((CompanyID)this->window_number, true);
break;
case WID_C_COMPANY_PASSWORD:
if (this->window_number == _local_company) ShowNetworkCompanyPasswordWindow(this);
break;
case WID_C_COMPANY_JOIN: {
this->query_widget = WID_C_COMPANY_JOIN;
CompanyID company = (CompanyID)this->window_number;
if (_network_server) {
NetworkServerDoMove(CLIENT_ID_SERVER, company);
MarkWholeScreenDirty();
} else if (NetworkCompanyIsPassworded(company)) {
/* ask for the password */
ShowQueryString(STR_EMPTY, STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION, NETWORK_PASSWORD_LENGTH, this, CS_ALPHANUMERAL, QSF_PASSWORD);
} else {
/* just send the join command */
NetworkClientRequestMove(company);
@ -2609,7 +2528,7 @@ struct CompanyWindow : Window
default: NOT_REACHED();
case WID_C_GIVE_MONEY: {
Money money = std::strtoull(str, nullptr, 10) / _currency->rate;
Money money = std::strtoull(str, nullptr, 10) / GetCurrency().rate;
Command<CMD_GIVE_MONEY>::Post(STR_ERROR_CAN_T_GIVE_MONEY, money, (CompanyID)this->window_number);
break;
}
@ -2621,10 +2540,14 @@ struct CompanyWindow : Window
case WID_C_COMPANY_NAME:
Command<CMD_RENAME_COMPANY>::Post(STR_ERROR_CAN_T_CHANGE_COMPANY_NAME, str);
break;
}
}
case WID_C_COMPANY_JOIN:
NetworkClientRequestMove((CompanyID)this->window_number, str);
break;
void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
{
if (gui_scope && data == 1) {
/* Manually call OnResize to adjust minimum height of president name widget. */
OnResize();
}
}
};
@ -2666,18 +2589,18 @@ struct BuyCompanyWindow : Window {
this->company_value = hostile_takeover ? CalculateHostileTakeoverValue(c) : c->bankrupt_value;
}
void UpdateWidgetSize(WidgetID widget, Dimension *size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension *fill, [[maybe_unused]] Dimension *resize) override
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
{
switch (widget) {
case WID_BC_FACE:
*size = GetScaledSpriteSize(SPR_GRADIENT);
size = GetScaledSpriteSize(SPR_GRADIENT);
break;
case WID_BC_QUESTION:
const Company *c = Company::Get((CompanyID)this->window_number);
SetDParam(0, c->index);
SetDParam(1, this->company_value);
size->height = GetStringHeight(this->hostile_takeover ? STR_BUY_COMPANY_HOSTILE_TAKEOVER : STR_BUY_COMPANY_MESSAGE, size->width);
size.height = GetStringHeight(this->hostile_takeover ? STR_BUY_COMPANY_HOSTILE_TAKEOVER : STR_BUY_COMPANY_MESSAGE, size.width);
break;
}
}

@ -523,7 +523,7 @@ DEF_CONSOLE_CMD(ConRemove)
_console_file_list_savegame.ValidateFileList();
const FiosItem *item = _console_file_list_savegame.FindItem(file);
if (item != nullptr) {
if (unlink(item->name.c_str()) != 0) {
if (!FioRemove(item->name)) {
IConsolePrint(CC_ERROR, "Failed to delete '{}'.", item->name);
}
} else {
@ -836,6 +836,7 @@ DEF_CONSOLE_CMD(ConRcon)
if (argc == 0) {
IConsolePrint(CC_HELP, "Remote control the server from another client. Usage: 'rcon <password> <command>'.");
IConsolePrint(CC_HELP, "Remember to enclose the command in quotes, otherwise only the first parameter is sent.");
IConsolePrint(CC_HELP, "When your client's public key is in the 'authorized keys' for 'rcon', the password is not checked and may be '*'.");
return true;
}
@ -913,20 +914,26 @@ DEF_CONSOLE_CMD(ConClientNickChange)
DEF_CONSOLE_CMD(ConJoinCompany)
{
if (argc < 2) {
IConsolePrint(CC_HELP, "Request joining another company. Usage: 'join <company-id> [<password>]'.");
IConsolePrint(CC_HELP, "Request joining another company. Usage: 'join <company-id>'.");
IConsolePrint(CC_HELP, "For valid company-id see company list, use 255 for spectator.");
return true;
}
CompanyID company_id = (CompanyID)(atoi(argv[1]) <= MAX_COMPANIES ? atoi(argv[1]) - 1 : atoi(argv[1]));
const NetworkClientInfo *info = NetworkClientInfo::GetByClientID(_network_own_client_id);
if (info == nullptr) {
IConsolePrint(CC_ERROR, "You have not joined the game yet!");
return true;
}
/* Check we have a valid company id! */
if (!Company::IsValidID(company_id) && company_id != COMPANY_SPECTATOR) {
IConsolePrint(CC_ERROR, "Company does not exist. Company-id must be between 1 and {}.", MAX_COMPANIES);
return true;
}
if (NetworkClientInfo::GetByClientID(_network_own_client_id)->client_playas == company_id) {
if (info->client_playas == company_id) {
IConsolePrint(CC_ERROR, "You are already there!");
return true;
}
@ -936,9 +943,8 @@ DEF_CONSOLE_CMD(ConJoinCompany)
return true;
}
/* Check if the company requires a password */
if (NetworkCompanyIsPassworded(company_id) && argc < 3) {
IConsolePrint(CC_ERROR, "Company {} requires a password to join.", company_id + 1);
if (!info->CanJoinCompany(company_id)) {
IConsolePrint(CC_ERROR, "You are not allowed to join this company.");
return true;
}
@ -946,7 +952,7 @@ DEF_CONSOLE_CMD(ConJoinCompany)
if (_network_server) {
NetworkServerDoMove(CLIENT_ID_SERVER, company_id);
} else {
NetworkClientRequestMove(company_id, NetworkCompanyIsPassworded(company_id) ? argv[2] : "");
NetworkClientRequestMove(company_id);
}
return true;
@ -1822,13 +1828,6 @@ DEF_CONSOLE_CMD(ConCompanies)
SetDParam(0, c->index);
std::string company_name = GetString(STR_COMPANY_NAME);
const char *password_state = "";
if (c->is_ai) {
password_state = "AI";
} else if (_network_server) {
password_state = _network_company_states[c->index].password.empty() ? "unprotected" : "protected";
}
std::string colour = GetString(STR_COLOUR_DARK_BLUE + _company_colours[c->index]);
IConsolePrint(CC_INFO, "#:{}({}) Company Name: '{}' Year Founded: {} Money: {} Loan: {} Value: {} (T:{}, R:{}, P:{}, S:{}) {}",
c->index + 1, colour, company_name,
@ -1837,7 +1836,7 @@ DEF_CONSOLE_CMD(ConCompanies)
c->group_all[VEH_ROAD].num_vehicle,
c->group_all[VEH_AIRCRAFT].num_vehicle,
c->group_all[VEH_SHIP].num_vehicle,
password_state);
c->is_ai ? "AI" : "");
}
return true;
@ -1908,53 +1907,86 @@ DEF_CONSOLE_CMD(ConSayClient)
return true;
}
DEF_CONSOLE_CMD(ConCompanyPassword)
/** All the known authorized keys with their name. */
static std::vector<std::pair<std::string_view, NetworkAuthorizedKeys *>> _console_cmd_authorized_keys{
{ "rcon", &_settings_client.network.rcon_authorized_keys },
{ "server", &_settings_client.network.server_authorized_keys },
};
DEF_CONSOLE_CMD(ConNetworkAuthorizedKey)
{
if (argc == 0) {
if (_network_dedicated) {
IConsolePrint(CC_HELP, "Change the password of a company. Usage: 'company_pw <company-no> \"<password>\".");
} else if (_network_server) {
IConsolePrint(CC_HELP, "Change the password of your or any other company. Usage: 'company_pw [<company-no>] \"<password>\"'.");
} else {
IConsolePrint(CC_HELP, "Change the password of your company. Usage: 'company_pw \"<password>\"'.");
}
if (argc <= 2) {
IConsolePrint(CC_HELP, "List and update authorized keys. Usage: 'authorized_key list [type]|add [type] [key]|remove [type] [key]'.");
IConsolePrint(CC_HELP, " list: list all the authorized keys of the given type.");
IConsolePrint(CC_HELP, " add: add the given key to the authorized keys of the given type.");
IConsolePrint(CC_HELP, " remove: remove the given key from the authorized keys of the given type; use 'all' to remove all authorized keys.");
IConsolePrint(CC_HELP, "Instead of a key, use 'client:<id>' to add/remove the key of that given client.");
IConsolePrint(CC_HELP, "Use \"*\" to disable the password.");
std::string buffer;
for (auto [name, _] : _console_cmd_authorized_keys) fmt::format_to(std::back_inserter(buffer), ", {}", name);
IConsolePrint(CC_HELP, "The supported types are: all{}.", buffer);
return true;
}
CompanyID company_id;
std::string password;
const char *errormsg;
bool valid_type = false; ///< Whether a valid type was given.
if (argc == 2) {
company_id = _local_company;
password = argv[1];
errormsg = "You have to own a company to make use of this command.";
} else if (argc == 3 && _network_server) {
company_id = (CompanyID)(atoi(argv[1]) - 1);
password = argv[2];
errormsg = "You have to specify the ID of a valid human controlled company.";
} else {
return false;
}
for (auto [name, authorized_keys] : _console_cmd_authorized_keys) {
if (!StrEqualsIgnoreCase(argv[2], name) && !StrEqualsIgnoreCase(argv[2], "all")) continue;
valid_type = true;
if (StrEqualsIgnoreCase(argv[1], "list")) {
IConsolePrint(CC_WHITE, "The authorized keys for {} are:", name);
for (auto &authorized_key : *authorized_keys) IConsolePrint(CC_INFO, " {}", authorized_key);
continue;
}
if (!Company::IsValidHumanID(company_id)) {
IConsolePrint(CC_ERROR, errormsg);
if (argc <= 3) {
IConsolePrint(CC_ERROR, "You must enter the key.");
return false;
}
std::string authorized_key = argv[3];
if (StrStartsWithIgnoreCase(authorized_key, "client:")) {
std::string id_string(authorized_key.substr(7));
authorized_key = NetworkGetPublicKeyOfClient(static_cast<ClientID>(std::stoi(id_string)));
if (authorized_key.empty()) {
IConsolePrint(CC_ERROR, "You must enter a valid client id; see 'clients'.");
return false;
}
}
if (StrEqualsIgnoreCase(argv[1], "add")) {
if (authorized_keys->Add(authorized_key)) {
IConsolePrint(CC_INFO, "Added {} to {}.", authorized_key, name);
} else {
IConsolePrint(CC_WARNING, "Not added {} to {} as it already exists.", authorized_key, name);
}
continue;
}
if (StrEqualsIgnoreCase(argv[1], "remove")) {
if (authorized_keys->Remove(authorized_key)) {
IConsolePrint(CC_INFO, "Removed {} from {}.", authorized_key, name);
} else {
IConsolePrint(CC_WARNING, "Not removed {} from {} as it does not exist.", authorized_key, name);
}
continue;
}
IConsolePrint(CC_WARNING, "No valid action was given.");
return false;
}
password = NetworkChangeCompanyPassword(company_id, password);
if (password.empty()) {
IConsolePrint(CC_INFO, "Company password cleared.");
} else {
IConsolePrint(CC_INFO, "Company password changed to '{}'.", password);
if (!valid_type) {
IConsolePrint(CC_WARNING, "No valid type was given.");
return false;
}
return true;
}
/* Content downloading only is available with ZLIB */
#if defined(WITH_ZLIB)
#include "network/network_content.h"
@ -1962,9 +1994,16 @@ DEF_CONSOLE_CMD(ConCompanyPassword)
/** Resolve a string to a content type. */
static ContentType StringToContentType(const char *str)
{
static const char * const inv_lookup[] = { "", "base", "newgrf", "ai", "ailib", "scenario", "heightmap" };
for (uint i = 1 /* there is no type 0 */; i < lengthof(inv_lookup); i++) {
if (StrEqualsIgnoreCase(str, inv_lookup[i])) return (ContentType)i;
static const std::initializer_list<std::pair<std::string_view, ContentType>> content_types = {
{"base", CONTENT_TYPE_BASE_GRAPHICS},
{"newgrf", CONTENT_TYPE_NEWGRF},
{"ai", CONTENT_TYPE_AI},
{"ailib", CONTENT_TYPE_AI_LIBRARY},
{"scenario", CONTENT_TYPE_SCENARIO},
{"heightmap", CONTENT_TYPE_HEIGHTMAP},
};
for (const auto &ct : content_types) {
if (StrEqualsIgnoreCase(str, ct.first)) return ct.second;
}
return CONTENT_TYPE_END;
}
@ -2092,10 +2131,14 @@ DEF_CONSOLE_CMD(ConFont)
IConsolePrint(CC_HELP, "Manage the fonts configuration.");
IConsolePrint(CC_HELP, "Usage 'font'.");
IConsolePrint(CC_HELP, " Print out the fonts configuration.");
IConsolePrint(CC_HELP, "Usage 'font [medium|small|large|mono] [<name>] [<size>] [aa|noaa]'.");
IConsolePrint(CC_HELP, " The \"Currently active\" configuration is the one actually in effect (after interface scaling and replacing unavailable fonts).");
IConsolePrint(CC_HELP, " The \"Requested\" configuration is the one requested via console command or config file.");
IConsolePrint(CC_HELP, "Usage 'font [medium|small|large|mono] [<font name>] [<size>]'.");
IConsolePrint(CC_HELP, " Change the configuration for a font.");
IConsolePrint(CC_HELP, " Omitting an argument will keep the current value.");
IConsolePrint(CC_HELP, " Set <name> to \"\" for the sprite font (size and aa have no effect on sprite font).");
IConsolePrint(CC_HELP, " Set <font name> to \"\" for the default font. Note that <size> has no effect if the default font is in use, and fixed defaults are used instead.");
IConsolePrint(CC_HELP, " If the sprite font is enabled in Game Options, it is used instead of the default font.");
IConsolePrint(CC_HELP, " The <size> is automatically multiplied by the current interface scaling.");
return true;
}
@ -2111,38 +2154,23 @@ DEF_CONSOLE_CMD(ConFont)
FontCacheSubSetting *setting = GetFontCacheSubSetting(argfs);
std::string font = setting->font;
uint size = setting->size;
bool aa = setting->aa;
uint v;
uint8_t arg_index = 2;
/* We may encounter "aa" or "noaa" but it must be the last argument. */
if (StrEqualsIgnoreCase(argv[arg_index], "aa") || StrEqualsIgnoreCase(argv[arg_index], "noaa")) {
aa = !StrStartsWithIgnoreCase(argv[arg_index++], "no");
if (argc > arg_index) return false;
} else {
/* For <name> we want a string. */
uint v;
if (!GetArgumentInteger(&v, argv[arg_index])) {
font = argv[arg_index++];
}
/* For <name> we want a string. */
if (!GetArgumentInteger(&v, argv[arg_index])) {
font = argv[arg_index++];
}
if (argc > arg_index) {
/* For <size> we want a number. */
uint v;
if (GetArgumentInteger(&v, argv[arg_index])) {
size = v;
arg_index++;
}
}
if (argc > arg_index) {
/* Last argument must be "aa" or "noaa". */
if (!StrEqualsIgnoreCase(argv[arg_index], "aa") && !StrEqualsIgnoreCase(argv[arg_index], "noaa")) return false;
aa = !StrStartsWithIgnoreCase(argv[arg_index++], "no");
if (argc > arg_index) return false;
}
SetFont(argfs, font, size, aa);
SetFont(argfs, font, size);
}
for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
@ -2153,7 +2181,9 @@ DEF_CONSOLE_CMD(ConFont)
InitFontCache(fs == FS_MONO);
fc = FontCache::Get(fs);
}
IConsolePrint(CC_DEFAULT, "{}: \"{}\" {} {} [\"{}\" {} {}]", FontSizeToName(fs), fc->GetFontName(), fc->GetFontSize(), GetFontAAState(fs) ? "aa" : "noaa", setting->font, setting->size, setting->aa ? "aa" : "noaa");
IConsolePrint(CC_DEFAULT, "{} font:", FontSizeToName(fs));
IConsolePrint(CC_DEFAULT, "Currently active: \"{}\", size {}", fc->GetFontName(), fc->GetFontSize());
IConsolePrint(CC_DEFAULT, "Requested: \"{}\", size {}", setting->font, setting->size);
}
return true;
@ -2722,8 +2752,8 @@ void IConsoleStdLibRegister()
IConsole::CmdRegister("pause", ConPauseGame, ConHookServerOrNoNetwork);
IConsole::CmdRegister("unpause", ConUnpauseGame, ConHookServerOrNoNetwork);
IConsole::CmdRegister("company_pw", ConCompanyPassword, ConHookNeedNetwork);
IConsole::AliasRegister("company_password", "company_pw %+");
IConsole::CmdRegister("authorized_key", ConNetworkAuthorizedKey, ConHookServerOnly);
IConsole::AliasRegister("ak", "authorized_key %+");
IConsole::AliasRegister("net_frame_freq", "setting frame_freq %+");
IConsole::AliasRegister("net_sync_freq", "setting sync_freq %+");
@ -2740,7 +2770,6 @@ void IConsoleStdLibRegister()
IConsole::AliasRegister("pause_on_join", "setting pause_on_join %+");
IConsole::AliasRegister("autoclean_companies", "setting autoclean_companies %+");
IConsole::AliasRegister("autoclean_protected", "setting autoclean_protected %+");
IConsole::AliasRegister("autoclean_unprotected", "setting autoclean_unprotected %+");
IConsole::AliasRegister("restart_game_year", "setting restart_game_year %+");
IConsole::AliasRegister("min_players", "setting min_active_clients %+");
IConsole::AliasRegister("reload_cfg", "setting reload_cfg %+");

@ -180,11 +180,11 @@ struct IConsoleWindow : Window
void Scroll(int amount)
{
if (amount < 0) {
size_t namount = (size_t) -amount;
size_t namount = static_cast<size_t>(-amount);
IConsoleWindow::scroll = (namount > IConsoleWindow::scroll) ? 0 : IConsoleWindow::scroll - namount;
} else {
assert(this->height >= 0 && this->line_height > 0);
size_t visible_lines = (size_t)(this->height / this->line_height);
size_t visible_lines = static_cast<size_t>(this->height / this->line_height);
size_t max_scroll = (visible_lines > _iconsole_buffer.size()) ? 0 : _iconsole_buffer.size() + 1 - visible_lines;
IConsoleWindow::scroll = std::min<size_t>(IConsoleWindow::scroll + amount, max_scroll);
}
@ -196,7 +196,7 @@ struct IConsoleWindow : Window
const int right = this->width - WidgetDimensions::scaled.frametext.right;
GfxFillRect(0, 0, this->width - 1, this->height - 1, PC_BLACK);
int ypos = this->height - this->line_height;
int ypos = this->height - this->line_height - WidgetDimensions::scaled.hsep_normal;
for (size_t line_index = IConsoleWindow::scroll; line_index < _iconsole_buffer.size(); line_index++) {
const IConsoleLine &print = _iconsole_buffer[line_index];
SetDParamStr(0, print.buffer);
@ -223,7 +223,7 @@ struct IConsoleWindow : Window
/** Check on a regular interval if the console buffer needs truncating. */
IntervalTimer<TimerWindow> truncate_interval = {std::chrono::seconds(3), [this](auto) {
assert(this->height >= 0 && this->line_height > 0);
size_t visible_lines = (size_t)(this->height / this->line_height);
size_t visible_lines = static_cast<size_t>(this->height / this->line_height);
if (TruncateBuffer() && IConsoleWindow::scroll + visible_lines > _iconsole_buffer.size()) {
size_t max_scroll = (visible_lines > _iconsole_buffer.size()) ? 0 : _iconsole_buffer.size() + 1 - visible_lines;

@ -12,7 +12,7 @@
#include "geometry_type.hpp"
Dimension maxdim(const Dimension &d1, const Dimension &d2);
[[nodiscard]] Dimension maxdim(const Dimension &d1, const Dimension &d2);
/**
* Check if a rectangle is empty.

@ -22,7 +22,7 @@
template <typename T>
constexpr T abs(const T a)
{
return (a < (T)0) ? -a : a;
return (a < static_cast<T>(0)) ? -a : a;
}
/**
@ -38,7 +38,7 @@ constexpr T Align(const T x, uint n)
{
assert((n & (n - 1)) == 0 && n != 0);
n--;
return (T)((x + n) & ~((T)n));
return static_cast<T>((x + n) & ~static_cast<T>(n));
}
/**
@ -54,8 +54,8 @@ constexpr T Align(const T x, uint n)
template <typename T>
constexpr T *AlignPtr(T *x, uint n)
{
static_assert(sizeof(size_t) == sizeof(void *));
return reinterpret_cast<T *>(Align((size_t)x, n));
static_assert(sizeof(uintptr_t) == sizeof(void *));
return reinterpret_cast<T *>(Align(reinterpret_cast<uintptr_t>(x), n));
}
/**
@ -251,7 +251,7 @@ constexpr T Delta(const T a, const T b)
template <typename T>
constexpr bool IsInsideBS(const T x, const size_t base, const size_t size)
{
return (size_t)(x - base) < size;
return static_cast<size_t>(x - base) < size;
}
/**
@ -268,9 +268,9 @@ template <typename T, std::enable_if_t<std::disjunction_v<std::is_convertible<T,
constexpr bool IsInsideMM(const T x, const size_t min, const size_t max) noexcept
{
if constexpr (std::is_base_of_v<StrongTypedefBase, T>) {
return (size_t)(x.base() - min) < (max - min);
return static_cast<size_t>(x.base() - min) < (max - min);
} else {
return (size_t)(x - min) < (max - min);
return static_cast<size_t>(x - min) < (max - min);
}
}

@ -115,17 +115,17 @@ DEFINE_POOL_METHOD(inline void *)::AllocateItem(size_t size, size_t index)
Titem *item;
if (Tcache && this->alloc_cache != nullptr) {
assert(sizeof(Titem) == size);
item = (Titem *)this->alloc_cache;
item = reinterpret_cast<Titem *>(this->alloc_cache);
this->alloc_cache = this->alloc_cache->next;
if (Tzero) {
/* Explicitly casting to (void *) prevents a clang warning -
* we are actually memsetting a (not-yet-constructed) object */
memset((void *)item, 0, sizeof(Titem));
memset(static_cast<void *>(item), 0, sizeof(Titem));
}
} else if (Tzero) {
item = (Titem *)CallocT<uint8_t>(size);
item = reinterpret_cast<Titem *>(CallocT<uint8_t>(size));
} else {
item = (Titem *)MallocT<uint8_t>(size);
item = reinterpret_cast<Titem *>(MallocT<uint8_t>(size));
}
this->data[index] = item;
SetBit(this->used_bitmap[index / BITMAP_SIZE], index % BITMAP_SIZE);
@ -188,7 +188,7 @@ DEFINE_POOL_METHOD(void)::FreeItem(size_t index)
assert(index < this->size);
assert(this->data[index] != nullptr);
if (Tcache) {
AllocCache *ac = (AllocCache *)this->data[index];
AllocCache *ac = reinterpret_cast<AllocCache *>(this->data[index]);
ac->next = this->alloc_cache;
this->alloc_cache = ac;
} else {

@ -78,8 +78,8 @@ private:
*/
template <class Titem, typename Tindex, size_t Tgrowth_step, size_t Tmax_size, PoolType Tpool_type = PT_NORMAL, bool Tcache = false, bool Tzero = true>
struct Pool : PoolBase {
/* Ensure Tmax_size is within the bounds of Tindex. */
static_assert((uint64_t)(Tmax_size - 1) >> 8 * sizeof(Tindex) == 0);
/* Ensure the highest possible index, i.e. Tmax_size -1, is within the bounds of Tindex. */
static_assert(Tmax_size - 1 <= MAX_UVALUE(Tindex));
static constexpr size_t MAX_SIZE = Tmax_size; ///< Make template parameter accessible from outside
@ -259,7 +259,7 @@ struct Pool : PoolBase {
inline void operator delete(void *p)
{
if (p == nullptr) return;
Titem *pn = (Titem *)p;
Titem *pn = static_cast<Titem *>(p);
assert(pn == Tpool->Get(pn->index));
Tpool->FreeItem(pn->index);
}

@ -97,7 +97,7 @@ void RandomBytesWithFallback(std::span<uint8_t> buf)
#if defined(_WIN32)
auto res = BCryptGenRandom(nullptr, static_cast<PUCHAR>(buf.data()), static_cast<ULONG>(buf.size()), BCRYPT_USE_SYSTEM_PREFERRED_RNG);
if (res >= 0) return;
#elif defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__)
#elif defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
arc4random_buf(buf.data(), buf.size());
return;
#elif defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 25)))

@ -56,11 +56,12 @@ static void SurveyRecentNews(nlohmann::json &json)
json = nlohmann::json::array();
int i = 0;
for (NewsItem *news = _latest_news; i < 32 && news != nullptr; news = news->prev, i++) {
TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(news->date);
for (const auto &news : GetNews()) {
TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(news.date);
json.push_back(fmt::format("({}-{:02}-{:02}) StringID: {}, Type: {}, Ref1: {}, {}, Ref2: {}, {}",
ymd.year, ymd.month + 1, ymd.day, news->string_id, news->type,
news->reftype1, news->ref1, news->reftype2, news->ref2));
ymd.year, ymd.month + 1, ymd.day, news.string_id, news.type,
news.reftype1, news.ref1, news.reftype2, news.ref2));
if (++i > 32) break;
}
}

@ -26,7 +26,7 @@
* | | Euro year | | | | name
* | | | | | | | | */
/** The original currency specifications. */
static const CurrencySpec origin_currency_specs[CURRENCY_END] = {
static const std::array<CurrencySpec, CURRENCY_END> origin_currency_specs = {{
{ 1, "", CF_NOEURO, "\u00a3", "", "GBP", 0, STR_GAME_OPTIONS_CURRENCY_GBP }, ///< british pound
{ 2, "", CF_NOEURO, "$", "", "USD", 0, STR_GAME_OPTIONS_CURRENCY_USD }, ///< american dollar
{ 2, "", CF_ISEURO, "\u20ac", "", "EUR", 0, STR_GAME_OPTIONS_CURRENCY_EUR }, ///< euro
@ -71,10 +71,10 @@ static const CurrencySpec origin_currency_specs[CURRENCY_END] = {
{ 5, "", CF_NOEURO, "RM", "", "MYR", 0, STR_GAME_OPTIONS_CURRENCY_MYR }, ///< Malaysian Ringgit
{ 1, "", 2014, "", NBSP "Ls", "LVL", 1, STR_GAME_OPTIONS_CURRENCY_LVL }, ///< latvian lats
{ 400, "", 2002, "", "$00", "PTE", 1, STR_GAME_OPTIONS_CURRENCY_PTE }, ///< portuguese escudo
};
}};
/** Array of currencies used by the system */
CurrencySpec _currency_specs[CURRENCY_END];
std::array<CurrencySpec, CURRENCY_END> _currency_specs;
/**
* This array represent the position of OpenTTD's currencies,

@ -11,7 +11,7 @@
#define CURRENCY_H
#include "timer/timer_game_calendar.h"
#include "string_func.h"
#include "settings_type.h"
#include "strings_type.h"
static constexpr TimerGameCalendar::Year CF_NOEURO = 0; ///< Currency never switches to the Euro (as far as known).
@ -99,11 +99,25 @@ struct CurrencySpec {
}
};
extern CurrencySpec _currency_specs[CURRENCY_END];
extern std::array<CurrencySpec, CURRENCY_END> _currency_specs;
/* XXX small hack, but makes the rest of the code a bit nicer to read */
#define _custom_currency (_currency_specs[CURRENCY_CUSTOM])
#define _currency ((const CurrencySpec*)&_currency_specs[GetGameSettings().locale.currency])
/**
* Get the custom currency.
* @return Reference to custom currency.
*/
inline CurrencySpec &GetCustomCurrency()
{
return _currency_specs[CURRENCY_CUSTOM];
}
/**
* Get the currently selected currency.
* @return Read-only reference to the current currency.
*/
inline const CurrencySpec &GetCurrency()
{
return _currency_specs[GetGameSettings().locale.currency];
}
uint64_t GetMaskOfAllowedCurrencies();
void ResetCurrencies(bool preserve_custom = true);

@ -14,8 +14,9 @@
#include "window_gui.h"
#include "date_gui.h"
#include "core/geometry_func.hpp"
#include "dropdown_type.h"
#include "dropdown_func.h"
#include "widgets/dropdown_type.h"
#include "widgets/date_widget.h"
#include "safeguards.h"
@ -75,14 +76,14 @@ struct SetDateWindow : Window {
case WID_SD_DAY:
for (uint i = 0; i < 31; i++) {
list.push_back(std::make_unique<DropDownListStringItem>(STR_DAY_NUMBER_1ST + i, i + 1, false));
list.push_back(MakeDropDownListStringItem(STR_DAY_NUMBER_1ST + i, i + 1));
}
selected = this->date.day;
break;
case WID_SD_MONTH:
for (uint i = 0; i < 12; i++) {
list.push_back(std::make_unique<DropDownListStringItem>(STR_MONTH_JAN + i, i, false));
list.push_back(MakeDropDownListStringItem(STR_MONTH_JAN + i, i));
}
selected = this->date.month;
break;
@ -90,7 +91,7 @@ struct SetDateWindow : Window {
case WID_SD_YEAR:
for (TimerGameEconomy::Year i = this->min_year; i <= this->max_year; i++) {
SetDParam(0, i);
list.push_back(std::make_unique<DropDownListStringItem>(STR_JUST_INT, i.base(), false));
list.push_back(MakeDropDownListStringItem(STR_JUST_INT, i.base()));
}
selected = this->date.year.base();
break;
@ -99,7 +100,7 @@ struct SetDateWindow : Window {
ShowDropDownList(this, std::move(list), selected, widget);
}
void UpdateWidgetSize(WidgetID widget, Dimension *size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension *fill, [[maybe_unused]] Dimension *resize) override
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
{
Dimension d = {0, 0};
switch (widget) {
@ -125,7 +126,7 @@ struct SetDateWindow : Window {
d.width += padding.width;
d.height += padding.height;
*size = d;
size = d;
}
void SetStringParameters(WidgetID widget) const override

@ -60,7 +60,7 @@ struct DebugLevel {
};
#define DEBUG_LEVEL(x) { #x, &_debug_##x##_level }
static const DebugLevel debug_level[] = {
static const DebugLevel _debug_levels[] = {
DEBUG_LEVEL(driver),
DEBUG_LEVEL(grf),
DEBUG_LEVEL(map),
@ -79,7 +79,7 @@ struct DebugLevel {
#ifdef RANDOM_DEBUG
DEBUG_LEVEL(random),
#endif
};
};
#undef DEBUG_LEVEL
/**
@ -89,13 +89,13 @@ struct DebugLevel {
void DumpDebugFacilityNames(std::back_insert_iterator<std::string> &output_iterator)
{
bool written = false;
for (const DebugLevel *i = debug_level; i != endof(debug_level); ++i) {
for (const auto &debug_level : _debug_levels) {
if (!written) {
fmt::format_to(output_iterator, "List of debug facility names:\n");
} else {
fmt::format_to(output_iterator, ", ");
}
fmt::format_to(output_iterator, "{}", i->name);
fmt::format_to(output_iterator, "{}", debug_level.name);
written = true;
}
if (written) {
@ -110,7 +110,7 @@ void DumpDebugFacilityNames(std::back_insert_iterator<std::string> &output_itera
*/
void DebugPrint(const char *category, int level, const std::string &message)
{
if (strcmp(category, "desync") == 0) {
if (strcmp(category, "desync") == 0 && level != 0) {
static FILE *f = FioFOpenFile("commands-out.log", "wb", AUTOSAVE_DIR);
if (f == nullptr) return;
@ -153,13 +153,11 @@ void SetDebugString(const char *s, void (*error_func)(const std::string &))
/* Global debugging level? */
if (*s >= '0' && *s <= '9') {
const DebugLevel *i;
v = std::strtoul(s, &end, 0);
s = end;
for (i = debug_level; i != endof(debug_level); ++i) {
new_levels[i->name] = v;
for (const auto &debug_level : _debug_levels) {
new_levels[debug_level.name] = v;
}
}
@ -174,9 +172,9 @@ void SetDebugString(const char *s, void (*error_func)(const std::string &))
/* check debugging levels */
const DebugLevel *found = nullptr;
for (const DebugLevel *i = debug_level; i != endof(debug_level); ++i) {
if (s == t + strlen(i->name) && strncmp(t, i->name, s - t) == 0) {
found = i;
for (const auto &debug_level : _debug_levels) {
if (s == t + strlen(debug_level.name) && strncmp(t, debug_level.name, s - t) == 0) {
found = &debug_level;
break;
}
}
@ -194,10 +192,10 @@ void SetDebugString(const char *s, void (*error_func)(const std::string &))
}
/* Apply the changes after parse is successful */
for (const DebugLevel *i = debug_level; i != endof(debug_level); ++i) {
const auto &nl = new_levels.find(i->name);
for (const auto &debug_level : _debug_levels) {
const auto &nl = new_levels.find(debug_level.name);
if (nl != new_levels.end()) {
*i->level = nl->second;
*debug_level.level = nl->second;
}
}
}
@ -210,9 +208,9 @@ void SetDebugString(const char *s, void (*error_func)(const std::string &))
std::string GetDebugString()
{
std::string result;
for (const DebugLevel *i = debug_level; i != endof(debug_level); ++i) {
for (const auto &debug_level : _debug_levels) {
if (!result.empty()) result += ", ";
fmt::format_to(std::back_inserter(result), "{}={}", i->name, *i->level);
fmt::format_to(std::back_inserter(result), "{}={}", debug_level.name, *debug_level.level);
}
return result;
}

@ -49,11 +49,11 @@
static constexpr NWidgetPart _nested_train_depot_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, COLOUR_GREY),
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_D_SHOW_RENAME), // rename button
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_D_RENAME), SetMinimalSize(12, 14), SetDataTip(SPR_RENAME, STR_DEPOT_RENAME_TOOLTIP),
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_D_SHOW_RENAME), SetAspect(WidgetDimensions::ASPECT_RENAME), // rename button
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_D_RENAME), SetAspect(WidgetDimensions::ASPECT_RENAME), SetDataTip(SPR_RENAME, STR_DEPOT_RENAME_TOOLTIP),
EndContainer(),
NWidget(WWT_CAPTION, COLOUR_GREY, WID_D_CAPTION), SetDataTip(STR_DEPOT_CAPTION, STR_NULL),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_D_LOCATION), SetMinimalSize(12, 14), SetDataTip(SPR_GOTO_LOCATION, STR_NULL),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_D_LOCATION), SetAspect(WidgetDimensions::ASPECT_LOCATION), SetDataTip(SPR_GOTO_LOCATION, STR_NULL),
NWidget(WWT_SHADEBOX, COLOUR_GREY),
NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
NWidget(WWT_STICKYBOX, COLOUR_GREY),
@ -78,9 +78,9 @@ static constexpr NWidgetPart _nested_train_depot_widgets[] = {
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_D_BUILD), SetDataTip(0x0, STR_NULL), SetFill(1, 1), SetResize(1, 0),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_D_CLONE), SetDataTip(0x0, STR_NULL), SetFill(1, 1), SetResize(1, 0),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_D_VEHICLE_LIST), SetDataTip(0x0, STR_NULL), SetFill(0, 1),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_D_STOP_ALL), SetDataTip(SPR_FLAG_VEH_STOPPED, STR_NULL), SetFill(0, 1),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_D_START_ALL), SetDataTip(SPR_FLAG_VEH_RUNNING, STR_NULL), SetFill(0, 1),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_D_VEHICLE_LIST), SetDataTip(0x0, STR_NULL), SetAspect(WidgetDimensions::ASPECT_VEHICLE_ICON), SetFill(0, 1),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_D_STOP_ALL), SetDataTip(SPR_FLAG_VEH_STOPPED, STR_NULL), SetAspect(WidgetDimensions::ASPECT_VEHICLE_FLAG), SetFill(0, 1),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_D_START_ALL), SetDataTip(SPR_FLAG_VEH_RUNNING, STR_NULL), SetAspect(WidgetDimensions::ASPECT_VEHICLE_FLAG), SetFill(0, 1),
NWidget(WWT_RESIZEBOX, COLOUR_GREY),
EndContainer(),
};
@ -459,10 +459,10 @@ struct DepotWindow : Window {
}
ym = (y - matrix_widget->pos_y) % this->resize.step_height;
int row = this->vscroll->GetScrolledRowFromWidget(y, this, WID_D_MATRIX);
int32_t row = this->vscroll->GetScrolledRowFromWidget(y, this, WID_D_MATRIX);
uint pos = (row * this->num_columns) + xt;
if (row == INT_MAX || this->vehicle_list.size() + this->wagon_list.size() <= pos) {
if (row == INT32_MAX || this->vehicle_list.size() + this->wagon_list.size() <= pos) {
/* Clicking on 'line' / 'block' without a vehicle */
if (this->type == VEH_TRAIN) {
/* End the dragging */
@ -653,7 +653,7 @@ struct DepotWindow : Window {
this->flag_size = maxdim(GetScaledSpriteSize(SPR_FLAG_VEH_STOPPED), GetScaledSpriteSize(SPR_FLAG_VEH_RUNNING));
}
void UpdateWidgetSize(WidgetID widget, Dimension *size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension *fill, [[maybe_unused]] Dimension *resize) override
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
{
switch (widget) {
case WID_D_MATRIX: {
@ -679,18 +679,18 @@ struct DepotWindow : Window {
}
int base_width = this->count_width + this->header_width + padding.width;
resize->height = std::max<uint>(this->cell_size.height, min_height + padding.height);
resize.height = std::max<uint>(this->cell_size.height, min_height + padding.height);
if (this->type == VEH_TRAIN) {
resize->width = 1;
size->width = base_width + 2 * ScaleSpriteTrad(29); // about 2 parts
size->height = resize->height * 6;
resize.width = 1;
size.width = base_width + 2 * ScaleSpriteTrad(29); // about 2 parts
size.height = resize.height * 6;
} else {
resize->width = base_width + this->cell_size.extend_left + this->cell_size.extend_right;
size->width = resize->width * (this->type == VEH_ROAD ? 5 : 3);
size->height = resize->height * (this->type == VEH_ROAD ? 5 : 3);
resize.width = base_width + this->cell_size.extend_left + this->cell_size.extend_right;
size.width = resize.width * (this->type == VEH_ROAD ? 5 : 3);
size.height = resize.height * (this->type == VEH_ROAD ? 5 : 3);
}
fill->width = resize->width;
fill->height = resize->height;
fill.width = resize.width;
fill.height = resize.height;
break;
}
}

@ -925,16 +925,17 @@ static const Disaster _disasters[] = {
static void DoDisaster()
{
uint8_t buf[lengthof(_disasters)];
std::vector<DisasterInitProc *> available_disasters;
uint8_t j = 0;
for (size_t i = 0; i != lengthof(_disasters); i++) {
if (TimerGameCalendar::year >= _disasters[i].min_year && TimerGameCalendar::year < _disasters[i].max_year) buf[j++] = (uint8_t)i;
for (auto &disaster : _disasters) {
if (TimerGameCalendar::year >= disaster.min_year && TimerGameCalendar::year < disaster.max_year) {
available_disasters.push_back(disaster.init_proc);
}
}
if (j == 0) return;
if (available_disasters.empty()) return;
_disasters[buf[RandomRange(j)]].init_proc();
available_disasters[RandomRange(static_cast<uint32_t>(available_disasters.size()))]();
}

@ -529,13 +529,13 @@ public:
UpdateDocksDirection();
}
void UpdateWidgetSize(WidgetID widget, Dimension *size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension *fill, [[maybe_unused]] Dimension *resize) override
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
{
switch (widget) {
case WID_BDD_X:
case WID_BDD_Y:
size->width = ScaleGUITrad(96) + WidgetDimensions::scaled.fullbevel.Horizontal();
size->height = ScaleGUITrad(64) + WidgetDimensions::scaled.fullbevel.Vertical();
size.width = ScaleGUITrad(96) + WidgetDimensions::scaled.fullbevel.Horizontal();
size.height = ScaleGUITrad(64) + WidgetDimensions::scaled.fullbevel.Vertical();
break;
}
}
@ -587,8 +587,8 @@ static constexpr NWidgetPart _nested_build_docks_depot_widgets[] = {
EndContainer(),
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BDD_BACKGROUND),
NWidget(NWID_HORIZONTAL_LTR), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1), SetPadding(WidgetDimensions::unscaled.picker),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BDD_X), SetMinimalSize(98, 66), SetDataTip(0x0, STR_DEPOT_BUILD_SHIP_ORIENTATION_TOOLTIP),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BDD_Y), SetMinimalSize(98, 66), SetDataTip(0x0, STR_DEPOT_BUILD_SHIP_ORIENTATION_TOOLTIP),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BDD_X), SetDataTip(0x0, STR_DEPOT_BUILD_SHIP_ORIENTATION_TOOLTIP),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BDD_Y), SetDataTip(0x0, STR_DEPOT_BUILD_SHIP_ORIENTATION_TOOLTIP),
EndContainer(),
EndContainer(),
};

@ -19,12 +19,6 @@
#include "fileio_func.h"
#include <sstream>
#ifdef _WIN32
# include <windows.h>
#else
# include <unistd.h>
#endif /* _WIN32 */
#include "safeguards.h"
std::string _ini_videodriver; ///< The video driver a stored in the configuration file.
@ -129,7 +123,7 @@ bool DriverFactoryBase::SelectDriverImpl(const std::string &name, Driver::Type t
* hardware acceleration. */
auto filename = FioFindFullPath(BASE_DIR, HWACCELERATION_TEST_FILE);
if (!filename.empty()) {
unlink(filename.c_str());
FioRemove(filename);
Debug(driver, 1, "Probing {} driver '{}' skipped due to earlier crash", GetDriverTypeName(type), d->name);
@ -148,15 +142,15 @@ bool DriverFactoryBase::SelectDriverImpl(const std::string &name, Driver::Type t
Driver *newd = d->CreateInstance();
*GetActiveDriver(type) = newd;
const char *err = newd->Start({});
if (err == nullptr) {
auto err = newd->Start({});
if (!err) {
Debug(driver, 1, "Successfully probed {} driver '{}'", GetDriverTypeName(type), d->name);
delete oldd;
return true;
}
*GetActiveDriver(type) = oldd;
Debug(driver, 1, "Probing {} driver '{}' failed with error: {}", GetDriverTypeName(type), d->name, err);
Debug(driver, 1, "Probing {} driver '{}' failed with error: {}", GetDriverTypeName(type), d->name, *err);
delete newd;
if (type == Driver::DT_VIDEO && _video_hw_accel && d->UsesHardwareAcceleration()) {
@ -192,10 +186,10 @@ bool DriverFactoryBase::SelectDriverImpl(const std::string &name, Driver::Type t
/* Found our driver, let's try it */
Driver *newd = d->CreateInstance();
const char *err = newd->Start(parms);
if (err != nullptr) {
auto err = newd->Start(parms);
if (err) {
delete newd;
UserError("Unable to load driver '{}'. The error was: {}", d->name, err);
UserError("Unable to load driver '{}'. The error was: {}", d->name, *err);
}
Debug(driver, 1, "Successfully loaded {} driver '{}'", GetDriverTypeName(type), d->name);
@ -216,7 +210,7 @@ void DriverFactoryBase::MarkVideoDriverOperational()
* and as we are operational now, remove the hardware acceleration
* test-file. */
auto filename = FioFindFullPath(BASE_DIR, HWACCELERATION_TEST_FILE);
if (!filename.empty()) unlink(filename.c_str());
if (!filename.empty()) FioRemove(filename);
}
/**

@ -23,9 +23,9 @@ public:
/**
* Start this driver.
* @param parm Parameters passed to the driver.
* @return nullptr if everything went okay, otherwise an error message.
* @return std::nullopt if everything went okay, otherwise an error message.
*/
virtual const char *Start(const StringList &parm) = 0;
virtual std::optional<std::string_view> Start(const StringList &parm) = 0;
/**
* Stop this driver.
@ -47,7 +47,7 @@ public:
* Get the name of this driver.
* @return The name of the driver.
*/
virtual const char *GetName() const = 0;
virtual std::string_view GetName() const = 0;
};
DECLARE_POSTFIX_INCREMENT(Driver::Type)
@ -62,8 +62,8 @@ private:
Driver::Type type; ///< The type of driver.
int priority; ///< The priority of this factory.
const char *name; ///< The name of the drivers of this factory.
const char *description; ///< The description of this driver.
std::string_view name; ///< The name of the drivers of this factory.
std::string_view description; ///< The description of this driver.
typedef std::map<std::string, DriverFactoryBase *> Drivers; ///< Type for a map of drivers.
@ -92,9 +92,9 @@ private:
* @param type The type of driver to get the name of.
* @return The name of the type.
*/
static const char *GetDriverTypeName(Driver::Type type)
static std::string_view GetDriverTypeName(Driver::Type type)
{
static const char * const driver_type_name[] = { "music", "sound", "video" };
static const std::string_view driver_type_name[] = { "music", "sound", "video" };
return driver_type_name[type];
}
@ -135,7 +135,7 @@ public:
* Get a nice description of the driver-class.
* @return The description.
*/
const char *GetDescription() const
std::string_view GetDescription() const
{
return this->description;
}

@ -7,20 +7,50 @@
/** @file dropdown.cpp Implementation of the dropdown widget. */
#include "../stdafx.h"
#include "../window_gui.h"
#include "../string_func.h"
#include "../strings_func.h"
#include "../window_func.h"
#include "../zoom_func.h"
#include "../timer/timer.h"
#include "../timer/timer_window.h"
#include "stdafx.h"
#include "dropdown_type.h"
#include "dropdown_func.h"
#include "dropdown_common_type.h"
#include "strings_func.h"
#include "timer/timer.h"
#include "timer/timer_window.h"
#include "window_gui.h"
#include "window_func.h"
#include "zoom_func.h"
#include "dropdown_widget.h"
#include "widgets/dropdown_widget.h"
#include "../safeguards.h"
#include "safeguards.h"
std::unique_ptr<DropDownListItem> MakeDropDownListDividerItem()
{
return std::make_unique<DropDownListDividerItem>(-1);
}
std::unique_ptr<DropDownListItem> MakeDropDownListStringItem(StringID str, int value, bool masked, bool shaded)
{
return std::make_unique<DropDownListStringItem>(str, value, masked, shaded);
}
std::unique_ptr<DropDownListItem> MakeDropDownListStringItem(const std::string &str, int value, bool masked, bool shaded)
{
return std::make_unique<DropDownListStringItem>(str, value, masked, shaded);
}
std::unique_ptr<DropDownListItem> MakeDropDownListIconItem(SpriteID sprite, PaletteID palette, StringID str, int value, bool masked, bool shaded)
{
return std::make_unique<DropDownListIconItem>(sprite, palette, str, value, masked, shaded);
}
std::unique_ptr<DropDownListItem> MakeDropDownListIconItem(const Dimension &dim, SpriteID sprite, PaletteID palette, StringID str, int value, bool masked, bool shaded)
{
return std::make_unique<DropDownListIconItem>(dim, sprite, palette, str, value, masked, shaded);
}
std::unique_ptr<DropDownListItem> MakeDropDownListCheckedItem(bool checked, StringID str, int value, bool masked, bool shaded)
{
return std::make_unique<DropDownListCheckedItem>(checked, str, value, masked, shaded);
}
static constexpr NWidgetPart _nested_dropdown_menu_widgets[] = {
NWidget(NWID_HORIZONTAL),
@ -162,7 +192,12 @@ struct DropdownWindow : Window {
this->position.y = button_rect.bottom + 1;
}
this->position.x = (_current_text_dir == TD_RTL) ? button_rect.right + 1 - (int)widget_dim.width : button_rect.left;
if (_current_text_dir == TD_RTL) {
/* In case the list is wider than the parent button, the list should be right aligned to the button and overflow to the left. */
this->position.x = button_rect.right + 1 - (int)(widget_dim.width + (list_dim.height > widget_dim.height ? NWidgetScrollbar::GetVerticalDimension().width : 0));
} else {
this->position.x = button_rect.left;
}
this->items_dim = widget_dim;
this->GetWidget<NWidgetStacked>(WID_DM_SHOW_SCROLL)->SetDisplayedPlane(list_dim.height > widget_dim.height ? 0 : SZSP_NONE);
@ -175,9 +210,9 @@ struct DropdownWindow : Window {
if (this->position.y < button_rect.top && list_dim.height > widget_dim.height) this->vscroll->UpdatePosition(INT_MAX);
}
void UpdateWidgetSize(WidgetID widget, Dimension *size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension *fill, [[maybe_unused]] Dimension *resize) override
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
{
if (widget == WID_DM_ITEMS) *size = this->items_dim;
if (widget == WID_DM_ITEMS) size = this->items_dim;
}
Point OnInitialPosition([[maybe_unused]] int16_t sm_width, [[maybe_unused]] int16_t sm_height, [[maybe_unused]] int window_number) override
@ -396,21 +431,23 @@ void ShowDropDownList(Window *w, DropDownList &&list, int selected, WidgetID but
* Show a dropdown menu window near a widget of the parent window.
* The result code of the items is their index in the \a strings list.
* @param w Parent window that wants the dropdown menu.
* @param strings Menu list, end with #INVALID_STRING_ID
* @param strings Menu list.
* @param selected Index of initial selected item.
* @param button Button widget number of the parent window \a w that wants the dropdown menu.
* @param disabled_mask Bitmask for disabled items (items with their bit set are displayed, but not selectable in the dropdown list).
* @param hidden_mask Bitmask for hidden items (items with their bit set are not copied to the dropdown list).
* @param width Minimum width of the dropdown menu.
*/
void ShowDropDownMenu(Window *w, const StringID *strings, int selected, WidgetID button, uint32_t disabled_mask, uint32_t hidden_mask, uint width)
void ShowDropDownMenu(Window *w, std::span<const StringID> strings, int selected, WidgetID button, uint32_t disabled_mask, uint32_t hidden_mask, uint width)
{
DropDownList list;
for (uint i = 0; strings[i] != INVALID_STRING_ID; i++) {
uint i = 0;
for (auto string : strings) {
if (!HasBit(hidden_mask, i)) {
list.push_back(std::make_unique<DropDownListStringItem>(strings[i], i, HasBit(disabled_mask, i)));
list.push_back(MakeDropDownListStringItem(string, i, HasBit(disabled_mask, i)));
}
++i;
}
if (!list.empty()) ShowDropDownList(w, std::move(list), selected, button, width);

@ -5,54 +5,25 @@
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file dropdown_type.h Types related to the drop down widget. */
/** @file dropdown_common_type.h Common drop down list components. */
#ifndef WIDGETS_DROPDOWN_TYPE_H
#define WIDGETS_DROPDOWN_TYPE_H
#ifndef DROPDOWN_COMMON_TYPE_H
#define DROPDOWN_COMMON_TYPE_H
#include "../window_type.h"
#include "../gfx_func.h"
#include "../gfx_type.h"
#include "../palette_func.h"
#include "../string_func.h"
#include "../strings_func.h"
#include "../table/strings.h"
#include "../window_gui.h"
/**
* Base list item class from which others are derived.
*/
class DropDownListItem {
public:
int result; ///< Result value to return to window on selection.
bool masked; ///< Masked and unselectable item.
bool shaded; ///< Shaded item, affects text colour.
explicit DropDownListItem(int result, bool masked = false, bool shaded = false) : result(result), masked(masked), shaded(shaded) {}
virtual ~DropDownListItem() = default;
virtual bool Selectable() const { return true; }
virtual uint Height() const { return 0; }
virtual uint Width() const { return 0; }
virtual void Draw(const Rect &full, const Rect &, bool, Colours bg_colour) const
{
if (this->masked) GfxFillRect(full, GetColourGradient(bg_colour, SHADE_LIGHT), FILLRECT_CHECKER);
}
TextColour GetColour(bool sel) const
{
if (this->shaded) return (sel ? TC_SILVER : TC_GREY) | TC_NO_SHADE;
return sel ? TC_WHITE : TC_BLACK;
}
};
#include "gfx_func.h"
#include "gfx_type.h"
#include "palette_func.h"
#include "string_func.h"
#include "strings_func.h"
#include "table/strings.h"
#include "window_gui.h"
/**
* Drop down divider component.
* @tparam TBase Base component.
* @tparam TFs Font size -- used to determine height.
*/
template<class TBase, FontSize TFs = FS_NORMAL>
template <class TBase, FontSize TFs = FS_NORMAL>
class DropDownDivider : public TBase {
public:
template <typename... Args>
@ -78,7 +49,7 @@ public:
* @tparam TFs Font size.
* @tparam TEnd Position string at end if true, or start if false.
*/
template<class TBase, FontSize TFs = FS_NORMAL, bool TEnd = false>
template <class TBase, FontSize TFs = FS_NORMAL, bool TEnd = false>
class DropDownString : public TBase {
std::string string; ///< String to be drawn.
Dimension dim; ///< Dimensions of string.
@ -136,7 +107,7 @@ public:
* @tparam TBase Base component.
* @tparam TEnd Position icon at end if true, or start if false.
*/
template<class TBase, bool TEnd = false>
template <class TBase, bool TEnd = false>
class DropDownIcon : public TBase {
SpriteID sprite; ///< Sprite ID to be drawn.
PaletteID palette; ///< Palette ID to use.
@ -174,7 +145,7 @@ public:
* @tparam TFs Font size.
* @tparam TEnd Position checkmark at end if true, or start if false.
*/
template<class TBase, bool TEnd = false, FontSize TFs = FS_NORMAL>
template <class TBase, bool TEnd = false, FontSize TFs = FS_NORMAL>
class DropDownCheck : public TBase {
bool checked; ///< Is item checked.
Dimension dim; ///< Dimension of checkmark.
@ -204,17 +175,4 @@ using DropDownListStringItem = DropDownString<DropDownListItem>;
using DropDownListIconItem = DropDownIcon<DropDownString<DropDownListItem>>;
using DropDownListCheckedItem = DropDownCheck<DropDownString<DropDownListItem>>;
/**
* A drop down list is a collection of drop down list items.
*/
typedef std::vector<std::unique_ptr<const DropDownListItem>> DropDownList;
void ShowDropDownListAt(Window *w, DropDownList &&list, int selected, WidgetID button, Rect wi_rect, Colours wi_colour, bool instant_close = false, bool persist = false);
void ShowDropDownList(Window *w, DropDownList &&list, int selected, WidgetID button, uint width = 0, bool instant_close = false, bool persist = false);
Dimension GetDropDownListDimension(const DropDownList &list);
void ReplaceDropDownList(Window *parent, DropDownList &&list);
#endif /* WIDGETS_DROPDOWN_TYPE_H */
#endif /* DROPDOWN_COMMON_TYPE_H */

@ -0,0 +1,26 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file dropdown_func.h Functions related to the drop down widget. */
#ifndef DROPDOWN_FUNC_H
#define DROPDOWN_FUNC_H
#include "window_gui.h"
/* Show drop down menu containing a fixed list of strings */
void ShowDropDownMenu(Window *w, std::span<const StringID> strings, int selected, WidgetID button, uint32_t disabled_mask, uint32_t hidden_mask, uint width = 0);
/* Helper functions for commonly used drop down list items. */
std::unique_ptr<DropDownListItem> MakeDropDownListDividerItem();
std::unique_ptr<DropDownListItem> MakeDropDownListStringItem(StringID str, int value, bool masked = false, bool shaded = false);
std::unique_ptr<DropDownListItem> MakeDropDownListStringItem(const std::string &str, int value, bool masked = false, bool shaded = false);
std::unique_ptr<DropDownListItem> MakeDropDownListIconItem(SpriteID sprite, PaletteID palette, StringID str, int value, bool masked = false, bool shaded = false);
std::unique_ptr<DropDownListItem> MakeDropDownListIconItem(const Dimension &dim, SpriteID sprite, PaletteID palette, StringID str, int value, bool masked = false, bool shaded = false);
std::unique_ptr<DropDownListItem> MakeDropDownListCheckedItem(bool checked, StringID str, int value, bool masked = false, bool shaded = false);
#endif /* DROPDOWN_FUNC_H */

@ -0,0 +1,60 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file dropdown_type.h Types related to the drop down widget. */
#ifndef DROPDOWN_TYPE_H
#define DROPDOWN_TYPE_H
#include "window_type.h"
#include "gfx_func.h"
#include "gfx_type.h"
#include "palette_func.h"
#include "window_gui.h"
/**
* Base list item class from which others are derived.
*/
class DropDownListItem {
public:
int result; ///< Result value to return to window on selection.
bool masked; ///< Masked and unselectable item.
bool shaded; ///< Shaded item, affects text colour.
explicit DropDownListItem(int result, bool masked = false, bool shaded = false) : result(result), masked(masked), shaded(shaded) {}
virtual ~DropDownListItem() = default;
virtual bool Selectable() const { return true; }
virtual uint Height() const { return 0; }
virtual uint Width() const { return 0; }
virtual void Draw(const Rect &full, const Rect &, bool, Colours bg_colour) const
{
if (this->masked) GfxFillRect(full, GetColourGradient(bg_colour, SHADE_LIGHT), FILLRECT_CHECKER);
}
TextColour GetColour(bool sel) const
{
if (this->shaded) return (sel ? TC_SILVER : TC_GREY) | TC_NO_SHADE;
return sel ? TC_WHITE : TC_BLACK;
}
};
/**
* A drop down list is a collection of drop down list items.
*/
typedef std::vector<std::unique_ptr<const DropDownListItem>> DropDownList;
void ShowDropDownListAt(Window *w, DropDownList &&list, int selected, WidgetID button, Rect wi_rect, Colours wi_colour, bool instant_close = false, bool persist = false);
void ShowDropDownList(Window *w, DropDownList &&list, int selected, WidgetID button, uint width = 0, bool instant_close = false, bool persist = false);
Dimension GetDropDownListDimension(const DropDownList &list);
void ReplaceDropDownList(Window *parent, DropDownList &&list);
#endif /* DROPDOWN_TYPE_H */

@ -1301,7 +1301,7 @@ void PrepareUnload(Vehicle *front_v)
front_v->last_station_visited, next_station,
front_v->current_order.GetUnloadType(), ge,
front_v->cargo_payment,
v->tile);
v->GetCargoTile());
if (v->cargo.UnloadCount() > 0) SetBit(v->vehicle_flags, VF_CARGO_UNLOADING);
}
}
@ -1441,7 +1441,7 @@ struct ReturnCargoAction
*/
bool operator()(Vehicle *v)
{
v->cargo.Return(UINT_MAX, &this->st->goods[v->cargo_type].cargo, this->next_hop, v->tile);
v->cargo.Return(UINT_MAX, &this->st->goods[v->cargo_type].cargo, this->next_hop, v->GetCargoTile());
return true;
}
};
@ -1476,7 +1476,7 @@ struct FinalizeRefitAction
{
if (this->do_reserve) {
this->st->goods[v->cargo_type].cargo.Reserve(v->cargo_cap - v->cargo.RemainingCount(),
&v->cargo, this->next_station, v->tile);
&v->cargo, this->next_station, v->GetCargoTile());
}
this->consist_capleft[v->cargo_type] += v->cargo_cap - v->cargo.RemainingCount();
return true;
@ -1567,7 +1567,7 @@ struct ReserveCargoAction {
{
if (v->cargo_cap > v->cargo.RemainingCount() && MayLoadUnderExclusiveRights(st, v)) {
st->goods[v->cargo_type].cargo.Reserve(v->cargo_cap - v->cargo.RemainingCount(),
&v->cargo, *next_station, v->tile);
&v->cargo, *next_station, v->GetCargoTile());
}
return true;
@ -1701,7 +1701,7 @@ static void LoadUnloadVehicle(Vehicle *front)
uint new_remaining = v->cargo.RemainingCount() + v->cargo.ActionCount(VehicleCargoList::MTA_DELIVER);
if (v->cargo_cap < new_remaining) {
/* Return some of the reserved cargo to not overload the vehicle. */
v->cargo.Return(new_remaining - v->cargo_cap, &ge->cargo, INVALID_STATION, v->tile);
v->cargo.Return(new_remaining - v->cargo_cap, &ge->cargo, INVALID_STATION, v->GetCargoTile());
}
/* Keep instead of delivering. This may lead to no cargo being unloaded, so ...*/
@ -1728,7 +1728,7 @@ static void LoadUnloadVehicle(Vehicle *front)
}
}
amount_unloaded = v->cargo.Unload(amount_unloaded, &ge->cargo, payment, v->tile);
amount_unloaded = v->cargo.Unload(amount_unloaded, &ge->cargo, payment, v->GetCargoTile());
remaining = v->cargo.UnloadCount() > 0;
if (amount_unloaded > 0) {
dirty_vehicle = true;
@ -1798,7 +1798,7 @@ static void LoadUnloadVehicle(Vehicle *front)
if (v->cargo.StoredCount() == 0) TriggerVehicle(v, VEHICLE_TRIGGER_NEW_CARGO);
if (_settings_game.order.gradual_loading) cap_left = std::min(cap_left, GetLoadAmount(v));
uint loaded = ge->cargo.Load(cap_left, &v->cargo, next_station, v->tile);
uint loaded = ge->cargo.Load(cap_left, &v->cargo, next_station, v->GetCargoTile());
if (v->cargo.ActionCount(VehicleCargoList::MTA_LOAD) > 0) {
/* Remember if there are reservations left so that we don't stop
* loading before they're loaded. */

@ -527,61 +527,33 @@ static bool BubbleTick(EffectVehicle *v)
return true;
}
struct EffectProcs {
using InitProc = void(EffectVehicle *);
using TickProc = bool(EffectVehicle *);
typedef void EffectInitProc(EffectVehicle *v);
typedef bool EffectTickProc(EffectVehicle *v);
/** Functions to initialise an effect vehicle after construction. */
static EffectInitProc * const _effect_init_procs[] = {
ChimneySmokeInit, // EV_CHIMNEY_SMOKE
SteamSmokeInit, // EV_STEAM_SMOKE
DieselSmokeInit, // EV_DIESEL_SMOKE
ElectricSparkInit, // EV_ELECTRIC_SPARK
SmokeInit, // EV_CRASH_SMOKE
ExplosionLargeInit, // EV_EXPLOSION_LARGE
BreakdownSmokeInit, // EV_BREAKDOWN_SMOKE
ExplosionSmallInit, // EV_EXPLOSION_SMALL
BulldozerInit, // EV_BULLDOZER
BubbleInit, // EV_BUBBLE
SmokeInit, // EV_BREAKDOWN_SMOKE_AIRCRAFT
SmokeInit, // EV_COPPER_MINE_SMOKE
};
static_assert(lengthof(_effect_init_procs) == EV_END);
/** Functions for controlling effect vehicles at each tick. */
static EffectTickProc * const _effect_tick_procs[] = {
ChimneySmokeTick, // EV_CHIMNEY_SMOKE
SteamSmokeTick, // EV_STEAM_SMOKE
DieselSmokeTick, // EV_DIESEL_SMOKE
ElectricSparkTick, // EV_ELECTRIC_SPARK
SmokeTick, // EV_CRASH_SMOKE
ExplosionLargeTick, // EV_EXPLOSION_LARGE
BreakdownSmokeTick, // EV_BREAKDOWN_SMOKE
ExplosionSmallTick, // EV_EXPLOSION_SMALL
BulldozerTick, // EV_BULLDOZER
BubbleTick, // EV_BUBBLE
SmokeTick, // EV_BREAKDOWN_SMOKE_AIRCRAFT
SmokeTick, // EV_COPPER_MINE_SMOKE
};
static_assert(lengthof(_effect_tick_procs) == EV_END);
/** Transparency options affecting the effects. */
static const TransparencyOption _effect_transparency_options[] = {
TO_INDUSTRIES, // EV_CHIMNEY_SMOKE
TO_INVALID, // EV_STEAM_SMOKE
TO_INVALID, // EV_DIESEL_SMOKE
TO_INVALID, // EV_ELECTRIC_SPARK
TO_INVALID, // EV_CRASH_SMOKE
TO_INVALID, // EV_EXPLOSION_LARGE
TO_INVALID, // EV_BREAKDOWN_SMOKE
TO_INVALID, // EV_EXPLOSION_SMALL
TO_INVALID, // EV_BULLDOZER
TO_INDUSTRIES, // EV_BUBBLE
TO_INVALID, // EV_BREAKDOWN_SMOKE_AIRCRAFT
TO_INDUSTRIES, // EV_COPPER_MINE_SMOKE
InitProc *init_proc; ///< Function to initialise an effect vehicle after construction.
TickProc *tick_proc; ///< Functions for controlling effect vehicles at each tick.
TransparencyOption transparency; ///< Transparency option affecting the effect.
constexpr EffectProcs(InitProc *init_proc, TickProc *tick_proc, TransparencyOption transparency)
: init_proc(init_proc), tick_proc(tick_proc), transparency(transparency) {}
};
static_assert(lengthof(_effect_transparency_options) == EV_END);
/** Per-EffectVehicleType handling. */
static std::array<EffectProcs, EV_END> _effect_procs = {{
{ ChimneySmokeInit, ChimneySmokeTick, TO_INDUSTRIES }, // EV_CHIMNEY_SMOKE
{ SteamSmokeInit, SteamSmokeTick, TO_INVALID }, // EV_STEAM_SMOKE
{ DieselSmokeInit, DieselSmokeTick, TO_INVALID }, // EV_DIESEL_SMOKE
{ ElectricSparkInit, ElectricSparkTick, TO_INVALID }, // EV_ELECTRIC_SPARK
{ SmokeInit, SmokeTick, TO_INVALID }, // EV_CRASH_SMOKE
{ ExplosionLargeInit, ExplosionLargeTick, TO_INVALID }, // EV_EXPLOSION_LARGE
{ BreakdownSmokeInit, BreakdownSmokeTick, TO_INVALID }, // EV_BREAKDOWN_SMOKE
{ ExplosionSmallInit, ExplosionSmallTick, TO_INVALID }, // EV_EXPLOSION_SMALL
{ BulldozerInit, BulldozerTick, TO_INVALID }, // EV_BULLDOZER
{ BubbleInit, BubbleTick, TO_INDUSTRIES }, // EV_BUBBLE
{ SmokeInit, SmokeTick, TO_INVALID }, // EV_BREAKDOWN_SMOKE_AIRCRAFT
{ SmokeInit, SmokeTick, TO_INDUSTRIES }, // EV_COPPER_MINE_SMOKE
}};
/**
* Create an effect vehicle at a particular location.
@ -604,7 +576,7 @@ EffectVehicle *CreateEffectVehicle(int x, int y, int z, EffectVehicleType type)
v->UpdateDeltaXY();
v->vehstatus = VS_UNCLICKABLE;
_effect_init_procs[type](v);
_effect_procs[type].init_proc(v);
v->UpdatePositionAndViewport();
@ -642,7 +614,7 @@ EffectVehicle *CreateEffectVehicleRel(const Vehicle *v, int x, int y, int z, Eff
bool EffectVehicle::Tick()
{
return _effect_tick_procs[this->subtype](this);
return _effect_procs[this->subtype].tick_proc(this);
}
void EffectVehicle::UpdateDeltaXY()
@ -660,5 +632,5 @@ void EffectVehicle::UpdateDeltaXY()
*/
TransparencyOption EffectVehicle::GetTransparencyOption() const
{
return _effect_transparency_options[this->subtype];
return _effect_procs[this->subtype].transparency;
}

@ -78,7 +78,7 @@ struct EnginePreviewWindow : Window {
this->flags |= WF_STICKY;
}
void UpdateWidgetSize(WidgetID widget, Dimension *size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension *fill, [[maybe_unused]] Dimension *resize) override
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
{
if (widget != WID_EP_QUESTION) return;
@ -98,11 +98,11 @@ struct EnginePreviewWindow : Window {
}
this->vehicle_space = std::max<int>(ScaleSpriteTrad(40), y - y_offs);
size->width = std::max(size->width, x - x_offs);
size.width = std::max(size.width, x + std::abs(x_offs));
SetDParam(0, GetEngineCategoryName(engine));
size->height = GetStringHeight(STR_ENGINE_PREVIEW_MESSAGE, size->width) + WidgetDimensions::scaled.vsep_wide + GetCharacterHeight(FS_NORMAL) + this->vehicle_space;
size.height = GetStringHeight(STR_ENGINE_PREVIEW_MESSAGE, size.width) + WidgetDimensions::scaled.vsep_wide + GetCharacterHeight(FS_NORMAL) + this->vehicle_space;
SetDParam(0, engine);
size->height += GetStringHeight(GetEngineInfoString(engine), size->width);
size.height += GetStringHeight(GetEngineInfoString(engine), size.width);
}
void DrawWidget(const Rect &r, WidgetID widget) const override

@ -11,6 +11,7 @@
#define ENGINE_GUI_H
#include "engine_type.h"
#include "group_type.h"
#include "sortlist_type.h"
#include "gfx_type.h"
#include "vehicle_type.h"
@ -20,9 +21,10 @@ struct GUIEngineListItem {
EngineID engine_id; ///< Engine to display in build purchase list
EngineID variant_id; ///< Variant group of the engine.
EngineDisplayFlags flags; ///< Flags for toggling/drawing (un)folded status and controlling indentation.
int8_t indent; ///< Display indentation level.
uint8_t indent; ///< Display indentation level.
uint16_t level_mask; ///< Mask of level continuations.
GUIEngineListItem(EngineID engine_id, EngineID variant_id, EngineDisplayFlags flags, int indent) : engine_id(engine_id), variant_id(variant_id), flags(flags), indent(indent) {}
GUIEngineListItem(EngineID engine_id, EngineID variant_id, EngineDisplayFlags flags, uint8_t indent) : engine_id(engine_id), variant_id(variant_id), flags(flags), indent(indent), level_mask(0) {}
/* Used when searching list only by engine_id. */
bool operator == (const EngineID &other) const { return this->engine_id == other; }
@ -47,10 +49,13 @@ extern bool _engine_sort_direction;
extern uint8_t _engine_sort_last_criteria[];
extern bool _engine_sort_last_order[];
extern bool _engine_sort_show_hidden_engines[];
extern const StringID _engine_sort_listing[][12];
extern const std::initializer_list<const StringID> _engine_sort_listing[];
extern EngList_SortTypeFunction * const _engine_sort_functions[][11];
/* Functions in build_vehicle_gui.cpp */
uint GetEngineListHeight(VehicleType type);
void DisplayVehicleSortDropDown(Window *w, VehicleType vehicle_type, int selected, WidgetID button);
void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_list, const Scrollbar &sb, EngineID selected_id, bool show_count, GroupID selected_group);
void GUIEngineListAddChildren(GUIEngineList &dst, const GUIEngineList &src, EngineID parent = INVALID_ENGINE, uint8_t indent = 0);
#endif /* ENGINE_GUI_H */

@ -189,16 +189,16 @@ public:
}
}
void UpdateWidgetSize(WidgetID widget, Dimension *size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension *fill, [[maybe_unused]] Dimension *resize) override
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
{
switch (widget) {
case WID_EM_MESSAGE: {
CopyInDParam(this->params);
if (this->textref_stack_size > 0) StartTextRefStackUsage(this->textref_stack_grffile, this->textref_stack_size, this->textref_stack);
this->height_summary = GetStringHeight(this->summary_msg, size->width);
this->height_detailed = (this->detailed_msg == INVALID_STRING_ID) ? 0 : GetStringHeight(this->detailed_msg, size->width);
this->height_extra = (this->extra_msg == INVALID_STRING_ID) ? 0 : GetStringHeight(this->extra_msg, size->width);
this->height_summary = GetStringHeight(this->summary_msg, size.width);
this->height_detailed = (this->detailed_msg == INVALID_STRING_ID) ? 0 : GetStringHeight(this->detailed_msg, size.width);
this->height_extra = (this->extra_msg == INVALID_STRING_ID) ? 0 : GetStringHeight(this->extra_msg, size.width);
if (this->textref_stack_size > 0) StopTextRefStackUsage();
@ -206,11 +206,11 @@ public:
if (this->detailed_msg != INVALID_STRING_ID) panel_height += this->height_detailed + WidgetDimensions::scaled.vsep_wide;
if (this->extra_msg != INVALID_STRING_ID) panel_height += this->height_extra + WidgetDimensions::scaled.vsep_wide;
size->height = std::max(size->height, panel_height);
size.height = std::max(size.height, panel_height);
break;
}
case WID_EM_FACE:
*size = maxdim(*size, GetScaledSpriteSize(SPR_GRADIENT));
size = maxdim(size, GetScaledSpriteSize(SPR_GRADIENT));
break;
}
}
@ -348,7 +348,7 @@ void ShowFirstError()
*/
void UnshowCriticalError()
{
ErrmsgWindow *w = (ErrmsgWindow*)FindWindowById(WC_ERRMSG, 0);
ErrmsgWindow *w = dynamic_cast<ErrmsgWindow *>(FindWindowById(WC_ERRMSG, 0));
if (_window_system_initialized && w != nullptr) {
if (w->IsCritical()) _error_list.push_front(*w);
_window_system_initialized = false;
@ -414,7 +414,7 @@ void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel
ErrorMessageData data(summary_msg, detailed_msg, is_critical, x, y, textref_stack_grffile, textref_stack_size, textref_stack, extra_msg);
data.CopyOutDParams();
ErrmsgWindow *w = (ErrmsgWindow*)FindWindowById(WC_ERRMSG, 0);
ErrmsgWindow *w = dynamic_cast<ErrmsgWindow *>(FindWindowById(WC_ERRMSG, 0));
if (w != nullptr) {
if (w->IsCritical()) {
/* A critical error is currently shown. */
@ -438,7 +438,7 @@ void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel
*/
bool HideActiveErrorMessage()
{
ErrmsgWindow *w = (ErrmsgWindow*)FindWindowById(WC_ERRMSG, 0);
ErrmsgWindow *w = dynamic_cast<ErrmsgWindow *>(FindWindowById(WC_ERRMSG, 0));
if (w == nullptr) return false;
w->Close();
return true;

@ -24,6 +24,7 @@
#include <unistd.h>
#include <pwd.h>
#endif
#include <charconv>
#include <sys/stat.h>
#include <sstream>
#include <filesystem>
@ -67,11 +68,6 @@ std::vector<Searchpath> _valid_searchpaths;
std::array<TarList, NUM_SUBDIRS> _tar_list;
TarFileList _tar_filelist[NUM_SUBDIRS];
typedef std::map<std::string, std::string> TarLinkList;
static TarLinkList _tar_linklist[NUM_SUBDIRS]; ///< List of directory links
extern bool FiosIsValidFile(const std::string &path, const struct dirent *ent, struct stat *sb);
/**
* Checks whether the given search path is a valid search path
* @param sp the search path to check
@ -203,7 +199,7 @@ static FILE *FioFOpenFileSp(const std::string &filename, const char *mode, Searc
* a string, but a variable, it 'renames' the variable,
* so make that variable to makes it compile happily */
wchar_t Lmode[5];
MultiByteToWideChar(CP_ACP, 0, mode, -1, Lmode, lengthof(Lmode));
MultiByteToWideChar(CP_ACP, 0, mode, -1, Lmode, static_cast<int>(std::size(Lmode)));
#endif
FILE *f = nullptr;
std::string buf;
@ -214,10 +210,6 @@ static FILE *FioFOpenFileSp(const std::string &filename, const char *mode, Searc
buf = _searchpaths[sp] + _subdirs[subdir] + filename;
}
#if defined(_WIN32)
if (mode[0] == 'r' && GetFileAttributes(OTTD2FS(buf).c_str()) == INVALID_FILE_ATTRIBUTES) return nullptr;
#endif
f = fopen(buf.c_str(), mode);
#if !defined(_WIN32)
if (f == nullptr && strtolower(buf, subdir == NO_DIRECTORY ? 0 : _searchpaths[sp].size() - 1) ) {
@ -303,17 +295,6 @@ FILE *FioFOpenFile(const std::string &filename, const char *mode, Subdirectory s
first = false;
}
/* Resolve ONE directory link */
for (const auto &link : _tar_linklist[subdir]) {
const std::string &src = link.first;
size_t len = src.length();
if (resolved_name.length() >= len && resolved_name[len - 1] == PATHSEPCHAR && src.compare(0, len, resolved_name, 0, len) == 0) {
/* Apply link */
resolved_name.replace(0, len, link.second);
break; // Only resolve one level
}
}
TarFileList::iterator it = _tar_filelist[subdir].find(resolved_name);
if (it != _tar_filelist[subdir].end()) {
f = FioFOpenFileTar(it->second, filesize);
@ -348,24 +329,26 @@ FILE *FioFOpenFile(const std::string &filename, const char *mode, Subdirectory s
*/
void FioCreateDirectory(const std::string &name)
{
auto p = name.find_last_of(PATHSEPCHAR);
if (p != std::string::npos) {
std::string dirname = name.substr(0, p);
DIR *dir = ttd_opendir(dirname.c_str());
if (dir == nullptr) {
FioCreateDirectory(dirname); // Try creating the parent directory, if we couldn't open it
} else {
closedir(dir);
}
}
/* Ignore directory creation errors; they'll surface later on. */
std::error_code error_code;
std::filesystem::create_directories(OTTD2FS(name), error_code);
}
/* Ignore directory creation errors; they'll surface later on, and most
* of the time they are 'directory already exists' errors anyhow. */
#if defined(_WIN32)
CreateDirectory(OTTD2FS(name).c_str(), nullptr);
#else
mkdir(OTTD2FS(name).c_str(), 0755);
#endif
/**
* Remove a file.
* @param filename Filename to remove.
* @return true iff the file was removed.
*/
bool FioRemove(const std::string &filename)
{
std::filesystem::path path = OTTD2FS(filename);
std::error_code error_code;
std::filesystem::remove(path, error_code);
if (error_code) {
Debug(misc, 0, "Removing {} failed: {}", filename, error_code.message());
return false;
}
return true;
}
/**
@ -381,27 +364,6 @@ void AppendPathSeparator(std::string &buf)
if (buf.back() != PATHSEPCHAR) buf.push_back(PATHSEPCHAR);
}
static void TarAddLink(const std::string &srcParam, const std::string &destParam, Subdirectory subdir)
{
std::string src = srcParam;
std::string dest = destParam;
/* Tar internals assume lowercase */
std::transform(src.begin(), src.end(), src.begin(), tolower);
std::transform(dest.begin(), dest.end(), dest.begin(), tolower);
TarFileList::iterator dest_file = _tar_filelist[subdir].find(dest);
if (dest_file != _tar_filelist[subdir].end()) {
/* Link to file. Process the link like the destination file. */
_tar_filelist[subdir].insert(TarFileList::value_type(src, dest_file->second));
} else {
/* Destination file not found. Assume 'link to directory'
* Append PATHSEPCHAR to 'src' and 'dest' if needed */
const std::string src_path = ((*src.rbegin() == PATHSEPCHAR) ? src : src + PATHSEPCHAR);
const std::string dst_path = (dest.length() == 0 ? "" : ((*dest.rbegin() == PATHSEPCHAR) ? dest : dest + PATHSEPCHAR));
_tar_linklist[subdir].insert(TarLinkList::value_type(src_path, dst_path));
}
}
/**
* Simplify filenames from tars.
* Replace '/' by #PATHSEPCHAR, and force 'name' to lowercase.
@ -477,17 +439,14 @@ bool TarScanner::AddFile(Subdirectory sd, const std::string &filename)
* header contains garbage and is malicious. So, we cannot rely on the string
* being properly terminated.
* As such, do not use strlen to determine the actual length (explicitly or
* implictly via the std::string constructor), but also do not create a string
* of the buffer length as that makes the string contain essentially garbage.
* implictly via the std::string constructor), but pass the buffer bounds
* explicitly.
* @param buffer The buffer to read from.
* @param buffer_length The length of the buffer to read from.
* @return The string data.
*/
static std::string ExtractString(char *buffer, size_t buffer_length)
static std::string ExtractString(std::span<char> buffer)
{
size_t length = 0;
for (; length < buffer_length && buffer[length] != '\0'; length++) {}
return StrMakeValid(std::string_view(buffer, length));
return StrMakeValid(std::string_view(buffer.begin(), buffer.end()));
}
bool TarScanner::AddFile(const std::string &filename, size_t, [[maybe_unused]] const std::string &tar_filename)
@ -533,8 +492,6 @@ bool TarScanner::AddFile(const std::string &filename, size_t, [[maybe_unused]] c
std::string filename_base = std::filesystem::path(filename).filename().string();
SimplifyFileName(filename_base);
TarLinkList links; ///< Temporary list to collect links
TarHeader th;
size_t num = 0, pos = 0;
@ -561,16 +518,25 @@ bool TarScanner::AddFile(const std::string &filename, size_t, [[maybe_unused]] c
/* The prefix contains the directory-name */
if (th.prefix[0] != '\0') {
name = ExtractString(th.prefix, lengthof(th.prefix));
name = ExtractString(th.prefix);
name += PATHSEP;
}
/* Copy the name of the file in a safe way at the end of 'name' */
name += ExtractString(th.name, lengthof(th.name));
name += ExtractString(th.name);
/* The size of the file, for some strange reason, this is stored as a string in octals. */
std::string size = ExtractString(th.size, lengthof(th.size));
size_t skip = size.empty() ? 0 : std::stoul(size, nullptr, 8);
std::string size = ExtractString(th.size);
size_t skip = 0;
if (!size.empty()) {
StrTrimInPlace(size);
auto [_, err] = std::from_chars(size.data(), size.data() + size.size(), skip, 8);
if (err != std::errc()) {
Debug(misc, 0, "The file '{}' has an invalid size for '{}'", filename, name);
fclose(f);
return false;
}
}
switch (th.typeflag) {
case '\0':
@ -594,33 +560,9 @@ bool TarScanner::AddFile(const std::string &filename, size_t, [[maybe_unused]] c
case '1': // hard links
case '2': { // symbolic links
/* Copy the destination of the link in a safe way at the end of 'linkname' */
std::string link = ExtractString(th.linkname, lengthof(th.linkname));
if (name.empty() || link.empty()) break;
/* Convert to lowercase and our PATHSEPCHAR */
SimplifyFileName(name);
SimplifyFileName(link);
/* Only allow relative links */
if (link[0] == PATHSEPCHAR) {
Debug(misc, 5, "Ignoring absolute link in tar: {} -> {}", name, link);
break;
}
/* Process relative path.
* Note: The destination of links must not contain any directory-links. */
std::string dest = (std::filesystem::path(name).remove_filename() /= link).lexically_normal().string();
if (dest[0] == PATHSEPCHAR || dest.starts_with("..")) {
Debug(misc, 5, "Ignoring link pointing outside of data directory: {} -> {}", name, link);
break;
}
/* Store links in temporary list */
Debug(misc, 6, "Found link in tar: {} -> {}", name, dest);
links.insert(TarLinkList::value_type(filename_base + PATHSEPCHAR + name, filename_base + PATHSEPCHAR + dest));
std::string link = ExtractString(th.linkname);
Debug(misc, 5, "Ignoring link in tar: {} -> {}", name, link);
break;
}
@ -651,19 +593,6 @@ bool TarScanner::AddFile(const std::string &filename, size_t, [[maybe_unused]] c
Debug(misc, 4, "Found tar '{}' with {} new files", filename, num);
fclose(f);
/* Resolve file links and store directory links.
* We restrict usage of links to two cases:
* 1) Links to directories:
* Both the source path and the destination path must NOT contain any further links.
* When resolving files at most one directory link is resolved.
* 2) Links to files:
* The destination path must NOT contain any links.
* The source path may contain one directory link.
*/
for (auto &it : links) {
TarAddLink(it.first, it.second, this->subdir);
}
return true;
}
@ -1008,9 +937,9 @@ void DeterminePaths(const char *exe, bool only_local_path)
};
config_dir.clear();
for (uint i = 0; i < lengthof(new_openttd_cfg_order); i++) {
if (IsValidSearchPath(new_openttd_cfg_order[i])) {
config_dir = _searchpaths[new_openttd_cfg_order[i]];
for (const auto &searchpath : new_openttd_cfg_order) {
if (IsValidSearchPath(searchpath)) {
config_dir = _searchpaths[searchpath];
break;
}
}
@ -1061,8 +990,8 @@ void DeterminePaths(const char *exe, bool only_local_path)
SAVE_DIR, AUTOSAVE_DIR, SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR, SCREENSHOT_DIR, SOCIAL_INTEGRATION_DIR
};
for (uint i = 0; i < lengthof(default_subdirs); i++) {
FioCreateDirectory(_personal_dir + _subdirs[default_subdirs[i]]);
for (const auto &default_subdir : default_subdirs) {
FioCreateDirectory(_personal_dir + _subdirs[default_subdir]);
}
/* If we have network we make a directory for the autodownloading of content */
@ -1072,9 +1001,9 @@ void DeterminePaths(const char *exe, bool only_local_path)
FillValidSearchPaths(only_local_path);
/* Create the directory for each of the types of content */
const Subdirectory dirs[] = { SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR, SOCIAL_INTEGRATION_DIR };
for (uint i = 0; i < lengthof(dirs); i++) {
FioCreateDirectory(FioGetDirectory(SP_AUTODOWNLOAD_DIR, dirs[i]));
const Subdirectory subdirs[] = { SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR, SOCIAL_INTEGRATION_DIR };
for (const auto &subdir : subdirs) {
FioCreateDirectory(FioGetDirectory(SP_AUTODOWNLOAD_DIR, subdir));
}
extern std::string _log_file;
@ -1134,12 +1063,13 @@ std::unique_ptr<char[]> ReadFileToMem(const std::string &filename, size_t &lenp,
* @param filename The filename to look in for the extension.
* @return True iff the extension is nullptr, or the filename ends with it.
*/
static bool MatchesExtension(const char *extension, const char *filename)
static bool MatchesExtension(std::string_view extension, const std::string &filename)
{
if (extension == nullptr) return true;
if (extension.empty()) return true;
if (filename.length() < extension.length()) return false;
const char *ext = strrchr(filename, extension[0]);
return ext != nullptr && StrEqualsIgnoreCase(ext, extension);
std::string_view filename_sv = filename; // String view to avoid making another copy of the substring.
return StrCompareIgnoreCase(extension, filename_sv.substr(filename_sv.length() - extension.length())) == 0;
}
/**
@ -1151,36 +1081,24 @@ static bool MatchesExtension(const char *extension, const char *filename)
* @param basepath_length from where in the path are we 'based' on the search path
* @param recursive whether to recursively search the sub directories
*/
static uint ScanPath(FileScanner *fs, const char *extension, const char *path, size_t basepath_length, bool recursive)
static uint ScanPath(FileScanner *fs, std::string_view extension, const std::filesystem::path &path, size_t basepath_length, bool recursive)
{
uint num = 0;
struct stat sb;
struct dirent *dirent;
DIR *dir;
if (path == nullptr || (dir = ttd_opendir(path)) == nullptr) return 0;
while ((dirent = readdir(dir)) != nullptr) {
std::string d_name = FS2OTTD(dirent->d_name);
if (!FiosIsValidFile(path, dirent, &sb)) continue;
std::string filename(path);
filename += d_name;
if (S_ISDIR(sb.st_mode)) {
/* Directory */
std::error_code error_code;
for (const auto &dir_entry : std::filesystem::directory_iterator(path, error_code)) {
if (dir_entry.is_directory()) {
if (!recursive) continue;
if (d_name == "." || d_name == "..") continue;
AppendPathSeparator(filename);
num += ScanPath(fs, extension, filename.c_str(), basepath_length, recursive);
} else if (S_ISREG(sb.st_mode)) {
/* File */
if (MatchesExtension(extension, filename.c_str()) && fs->AddFile(filename, basepath_length, {})) num++;
num += ScanPath(fs, extension, dir_entry.path(), basepath_length, recursive);
} else if (dir_entry.is_regular_file()) {
std::string file = FS2OTTD(dir_entry.path());
if (!MatchesExtension(extension, file)) continue;
if (fs->AddFile(file, basepath_length, {})) num++;
}
}
closedir(dir);
if (error_code) {
Debug(misc, 9, "Unable to read directory {}: {}", path.string(), error_code.message());
}
return num;
}
@ -1191,12 +1109,11 @@ static uint ScanPath(FileScanner *fs, const char *extension, const char *path, s
* @param extension the extension of files to search for.
* @param tar the tar to search in.
*/
static uint ScanTar(FileScanner *fs, const char *extension, const TarFileList::value_type &tar)
static uint ScanTar(FileScanner *fs, std::string_view extension, const TarFileList::value_type &tar)
{
uint num = 0;
const auto &filename = tar.first;
if (MatchesExtension(extension, filename.c_str()) && fs->AddFile(filename, 0, tar.second.tar_filename)) num++;
if (MatchesExtension(extension, tar.first) && fs->AddFile(tar.first, 0, tar.second.tar_filename)) num++;
return num;
}
@ -1210,7 +1127,7 @@ static uint ScanTar(FileScanner *fs, const char *extension, const TarFileList::v
* @return the number of found files, i.e. the number of times that
* AddFile returned true.
*/
uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars, bool recursive)
uint FileScanner::Scan(std::string_view extension, Subdirectory sd, bool tars, bool recursive)
{
this->subdir = sd;
@ -1221,7 +1138,7 @@ uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars, bool r
if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
std::string path = FioGetDirectory(sp, sd);
num += ScanPath(this, extension, path.c_str(), path.size(), recursive);
num += ScanPath(this, extension, OTTD2FS(path), path.size(), recursive);
}
if (tars && sd != NO_DIRECTORY) {
@ -1252,9 +1169,9 @@ uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars, bool r
* @return the number of found files, i.e. the number of times that
* AddFile returned true.
*/
uint FileScanner::Scan(const char *extension, const std::string &directory, bool recursive)
uint FileScanner::Scan(const std::string_view extension, const std::string &directory, bool recursive)
{
std::string path(directory);
AppendPathSeparator(path);
return ScanPath(this, extension, path.c_str(), path.size(), recursive);
return ScanPath(this, extension, OTTD2FS(path), path.size(), recursive);
}

@ -20,6 +20,7 @@ std::string FioFindFullPath(Subdirectory subdir, const std::string &filename);
std::string FioGetDirectory(Searchpath sp, Subdirectory subdir);
std::string FioFindDirectory(Subdirectory subdir);
void FioCreateDirectory(const std::string &name);
bool FioRemove(const std::string &filename);
const char *FiosGetScreenshotDir();
@ -41,8 +42,8 @@ public:
/** Destruct the proper one... */
virtual ~FileScanner() = default;
uint Scan(const char *extension, Subdirectory sd, bool tars = true, bool recursive = true);
uint Scan(const char *extension, const std::string &directory, bool recursive = true);
uint Scan(std::string_view extension, Subdirectory sd, bool tars = true, bool recursive = true);
uint Scan(std::string_view extension, const std::string &directory, bool recursive = true);
/**
* Add a file with the given filename.
@ -80,40 +81,6 @@ public:
DECLARE_ENUM_AS_BIT_SET(TarScanner::Mode)
/* Implementation of opendir/readdir/closedir for Windows */
#if defined(_WIN32)
struct DIR;
struct dirent { // XXX - only d_name implemented
wchar_t *d_name; // name of found file
/* little hack which will point to parent DIR struct which will
* save us a call to GetFileAttributes if we want information
* about the file (for example in function fio_bla) */
DIR *dir;
};
DIR *opendir(const wchar_t *path);
struct dirent *readdir(DIR *d);
int closedir(DIR *d);
#else
/* Use system-supplied opendir/readdir/closedir functions */
# include <sys/types.h>
# include <dirent.h>
#endif /* defined(_WIN32) */
/**
* A wrapper around opendir() which will convert the string from
* OPENTTD encoding to that of the filesystem. For all purposes this
* function behaves the same as the original opendir function
* @param path string to open directory of
* @return DIR pointer
*/
inline DIR *ttd_opendir(const char *path)
{
return opendir(OTTD2FS(path).c_str());
}
/** Auto-close a file upon scope exit. */
class FileCloser {
FILE *f;

@ -21,10 +21,7 @@
#include "tar_type.h"
#include <sys/stat.h>
#include <charconv>
#ifndef _WIN32
# include <unistd.h>
#endif /* _WIN32 */
#include <filesystem>
#include "table/strings.h"
@ -36,8 +33,7 @@ SortingBits _savegame_sort_order = SORT_BY_DATE | SORT_DESCENDING;
/* OS-specific functions are taken from their respective files (win32/unix .c) */
extern bool FiosIsRoot(const std::string &path);
extern bool FiosIsValidFile(const std::string &path, const struct dirent *ent, struct stat *sb);
extern bool FiosIsHiddenFile(const struct dirent *ent);
extern bool FiosIsHiddenFile(const std::filesystem::path &path);
extern void FiosGetDrives(FileList &file_list);
/* get the name of an oldstyle savegame */
@ -53,7 +49,7 @@ bool FiosItem::operator< (const FiosItem &other) const
int r = false;
if ((_savegame_sort_order & SORT_BY_NAME) == 0 && (*this).mtime != other.mtime) {
r = this->mtime - other.mtime;
r = ClampTo<int32_t>(this->mtime - other.mtime);
} else {
r = StrNaturalCompare((*this).title, other.title);
}
@ -246,8 +242,7 @@ std::string FiosMakeHeightmapName(const char *name)
*/
bool FiosDelete(const char *name)
{
std::string filename = FiosMakeSavegameName(name);
return unlink(filename.c_str()) == 0;
return FioRemove(FiosMakeSavegameName(name));
}
typedef std::tuple<FiosType, std::string> FiosGetTypeAndNameProc(SaveLoadOperation fop, const std::string &filename, const std::string_view ext);
@ -292,32 +287,13 @@ bool FiosFileScanner::AddFile(const std::string &filename, size_t, const std::st
}
FiosItem *fios = &file_list.emplace_back();
#ifdef _WIN32
// Retrieve the file modified date using GetFileTime rather than stat to work around an obscure MSVC bug that affects Windows XP
HANDLE fh = CreateFile(OTTD2FS(filename).c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
if (fh != INVALID_HANDLE_VALUE) {
FILETIME ft;
ULARGE_INTEGER ft_int64;
if (GetFileTime(fh, nullptr, nullptr, &ft) != 0) {
ft_int64.HighPart = ft.dwHighDateTime;
ft_int64.LowPart = ft.dwLowDateTime;
// Convert from hectonanoseconds since 01/01/1601 to seconds since 01/01/1970
fios->mtime = ft_int64.QuadPart / 10000000ULL - 11644473600ULL;
} else {
fios->mtime = 0;
}
CloseHandle(fh);
#else
struct stat sb;
if (stat(filename.c_str(), &sb) == 0) {
fios->mtime = sb.st_mtime;
#endif
} else {
std::error_code error_code;
auto write_time = std::filesystem::last_write_time(OTTD2FS(filename), error_code);
if (error_code) {
fios->mtime = 0;
} else {
fios->mtime = std::chrono::duration_cast<std::chrono::milliseconds>(write_time.time_since_epoch()).count();
}
fios->type = type;
@ -345,48 +321,38 @@ bool FiosFileScanner::AddFile(const std::string &filename, size_t, const std::st
*/
static void FiosGetFileList(SaveLoadOperation fop, bool show_dirs, FiosGetTypeAndNameProc *callback_proc, Subdirectory subdir, FileList &file_list)
{
struct stat sb;
struct dirent *dirent;
DIR *dir;
FiosItem *fios;
size_t sort_start;
file_list.clear();
assert(_fios_path != nullptr);
/* A parent directory link exists if we are not in the root directory */
if (show_dirs && !FiosIsRoot(*_fios_path)) {
fios = &file_list.emplace_back();
fios->type = FIOS_TYPE_PARENT;
fios->mtime = 0;
fios->name = "..";
SetDParamStr(0, "..");
fios->title = GetString(STR_SAVELOAD_PARENT_DIRECTORY);
}
if (show_dirs) {
/* A parent directory link exists if we are not in the root directory */
if (!FiosIsRoot(*_fios_path)) {
FiosItem &fios = file_list.emplace_back();
fios.type = FIOS_TYPE_PARENT;
fios.mtime = 0;
fios.name = "..";
SetDParamStr(0, "..");
fios.title = GetString(STR_SAVELOAD_PARENT_DIRECTORY);
}
/* Show subdirectories */
if (show_dirs && (dir = ttd_opendir(_fios_path->c_str())) != nullptr) {
while ((dirent = readdir(dir)) != nullptr) {
std::string d_name = FS2OTTD(dirent->d_name);
/* found file must be directory, but not '.' or '..' */
if (FiosIsValidFile(*_fios_path, dirent, &sb) && S_ISDIR(sb.st_mode) &&
(!FiosIsHiddenFile(dirent) || StrStartsWithIgnoreCase(PERSONAL_DIR, d_name)) &&
d_name != "." && d_name != "..") {
fios = &file_list.emplace_back();
fios->type = FIOS_TYPE_DIR;
fios->mtime = 0;
fios->name = d_name;
SetDParamStr(0, fios->name + PATHSEP);
fios->title = GetString(STR_SAVELOAD_DIRECTORY);
}
/* Show subdirectories */
std::error_code error_code;
for (const auto &dir_entry : std::filesystem::directory_iterator(OTTD2FS(*_fios_path), error_code)) {
if (!dir_entry.is_directory()) continue;
if (FiosIsHiddenFile(dir_entry) && dir_entry.path().filename() != PERSONAL_DIR) continue;
FiosItem &fios = file_list.emplace_back();
fios.type = FIOS_TYPE_DIR;
fios.mtime = 0;
fios.name = FS2OTTD(dir_entry.path().filename());
SetDParamStr(0, fios.name + PATHSEP);
fios.title = GetString(STR_SAVELOAD_DIRECTORY);
}
closedir(dir);
}
/* Sort the subdirs always by name, ascending, remember user-sorting order */
if (show_dirs) {
/* Sort the subdirs always by name, ascending, remember user-sorting order */
SortingBits order = _savegame_sort_order;
_savegame_sort_order = SORT_BY_NAME | SORT_ASCENDING;
std::sort(file_list.begin(), file_list.end());
@ -399,17 +365,15 @@ static void FiosGetFileList(SaveLoadOperation fop, bool show_dirs, FiosGetTypeAn
/* Show files */
FiosFileScanner scanner(fop, callback_proc, file_list);
if (subdir == NO_DIRECTORY) {
scanner.Scan(nullptr, *_fios_path, false);
scanner.Scan({}, *_fios_path, false);
} else {
scanner.Scan(nullptr, subdir, true, true);
scanner.Scan({}, subdir, true, true);
}
std::sort(file_list.begin() + sort_start, file_list.end());
/* Show drives */
FiosGetDrives(file_list);
file_list.shrink_to_fit();
}
/**
@ -741,7 +705,7 @@ FiosNumberedSaveName::FiosNumberedSaveName(const std::string &prefix) : prefix(p
/* Get the save list. */
FileList list;
FiosFileScanner scanner(SLO_SAVE, proc, list);
scanner.Scan(".sav", _autosave_path->c_str(), false);
scanner.Scan(".sav", *_autosave_path, false);
/* Find the number for the most recent save, if any. */
if (list.begin() != list.end()) {

@ -78,7 +78,7 @@ extern LoadCheckData _load_check_data;
/** Deals with finding savegames */
struct FiosItem {
FiosType type;
uint64_t mtime;
int64_t mtime;
std::string title;
std::string name;
bool operator< (const FiosItem &other) const;

@ -90,7 +90,7 @@ static constexpr NWidgetPart _nested_load_dialog_widgets[] = {
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SL_SORT_BYNAME), SetDataTip(STR_SORT_BY_CAPTION_NAME, STR_TOOLTIP_SORT_ORDER), SetFill(1, 0), SetResize(1, 0),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SL_SORT_BYDATE), SetDataTip(STR_SORT_BY_CAPTION_DATE, STR_TOOLTIP_SORT_ORDER), SetFill(1, 0), SetResize(1, 0),
EndContainer(),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_SL_HOME_BUTTON), SetMinimalSize(12, 12), SetDataTip(SPR_HOUSE_ICON, STR_SAVELOAD_HOME_BUTTON),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_SL_HOME_BUTTON), SetAspect(1), SetDataTip(SPR_HOUSE_ICON, STR_SAVELOAD_HOME_BUTTON),
EndContainer(),
/* Files */
NWidget(NWID_HORIZONTAL),
@ -148,7 +148,7 @@ static constexpr NWidgetPart _nested_load_heightmap_dialog_widgets[] = {
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SL_SORT_BYNAME), SetDataTip(STR_SORT_BY_CAPTION_NAME, STR_TOOLTIP_SORT_ORDER), SetFill(1, 0), SetResize(1, 0),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SL_SORT_BYDATE), SetDataTip(STR_SORT_BY_CAPTION_DATE, STR_TOOLTIP_SORT_ORDER), SetFill(1, 0), SetResize(1, 0),
EndContainer(),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_SL_HOME_BUTTON), SetMinimalSize(12, 12), SetDataTip(SPR_HOUSE_ICON, STR_SAVELOAD_HOME_BUTTON),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_SL_HOME_BUTTON), SetAspect(1), SetDataTip(SPR_HOUSE_ICON, STR_SAVELOAD_HOME_BUTTON),
EndContainer(),
/* Files */
NWidget(NWID_HORIZONTAL),
@ -195,7 +195,7 @@ static constexpr NWidgetPart _nested_save_dialog_widgets[] = {
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SL_SORT_BYNAME), SetDataTip(STR_SORT_BY_CAPTION_NAME, STR_TOOLTIP_SORT_ORDER), SetFill(1, 0), SetResize(1, 0),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SL_SORT_BYDATE), SetDataTip(STR_SORT_BY_CAPTION_DATE, STR_TOOLTIP_SORT_ORDER), SetFill(1, 0), SetResize(1, 0),
EndContainer(),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_SL_HOME_BUTTON), SetMinimalSize(12, 12), SetDataTip(SPR_HOUSE_ICON, STR_SAVELOAD_HOME_BUTTON),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_SL_HOME_BUTTON), SetAspect(1), SetDataTip(SPR_HOUSE_ICON, STR_SAVELOAD_HOME_BUTTON),
EndContainer(),
/* Files */
NWidget(NWID_HORIZONTAL),
@ -561,23 +561,23 @@ public:
}
}
void UpdateWidgetSize(WidgetID widget, Dimension *size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension *fill, [[maybe_unused]] Dimension *resize) override
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
{
switch (widget) {
case WID_SL_BACKGROUND:
size->height = 2 * GetCharacterHeight(FS_NORMAL) + padding.height;
size.height = 2 * GetCharacterHeight(FS_NORMAL) + padding.height;
break;
case WID_SL_DRIVES_DIRECTORIES_LIST:
resize->height = GetCharacterHeight(FS_NORMAL);
size->height = resize->height * 10 + padding.height;
resize.height = GetCharacterHeight(FS_NORMAL);
size.height = resize.height * 10 + padding.height;
break;
case WID_SL_SORT_BYNAME:
case WID_SL_SORT_BYDATE: {
Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
d.height += padding.height;
*size = maxdim(*size, d);
size = maxdim(size, d);
break;
}
}

@ -91,15 +91,15 @@ int GetCharacterHeight(FontSize size)
}
/* Check if a glyph should be rendered with anti-aliasing. */
bool GetFontAAState(FontSize size, bool check_blitter)
bool GetFontAAState()
{
/* AA is only supported for 32 bpp */
if (check_blitter && BlitterFactory::GetCurrentBlitter()->GetScreenDepth() != 32) return false;
if (BlitterFactory::GetCurrentBlitter()->GetScreenDepth() != 32) return false;
return _fcsettings.global_aa || GetFontCacheSubSetting(size)->aa;
return _fcsettings.global_aa;
}
void SetFont(FontSize fontsize, const std::string &font, uint size, bool aa)
void SetFont(FontSize fontsize, const std::string &font, uint size)
{
FontCacheSubSetting *setting = GetFontCacheSubSetting(fontsize);
bool changed = false;
@ -114,11 +114,6 @@ void SetFont(FontSize fontsize, const std::string &font, uint size, bool aa)
changed = true;
}
if (setting->aa != aa) {
setting->aa = aa;
changed = true;
}
if (!changed) return;
if (fontsize != FS_MONO) {
@ -233,19 +228,6 @@ void UninitFontCache()
#endif /* WITH_FREETYPE */
}
/**
* Should any of the active fonts be anti-aliased?
* @return True if any of the loaded fonts want anti-aliased drawing.
*/
bool HasAntialiasedFonts()
{
for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
if (!FontCache::Get(fs)->IsBuiltInFont() && GetFontAAState(fs, false)) return true;
}
return false;
}
#if !defined(_WIN32) && !defined(__APPLE__) && !defined(WITH_FONTCONFIG) && !defined(WITH_COCOA)
bool SetFallbackFont(FontCacheSettings *, const std::string &, int, MissingGlyphSearcher *) { return false; }

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save